Skip to content

Commit 359b2a9

Browse files
Soft-fail integer conversion from JS values that are not representable
Close #258
1 parent abc05a9 commit 359b2a9

File tree

2 files changed

+72
-4
lines changed

2 files changed

+72
-4
lines changed

IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift

+16
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,22 @@ try test("Value Construction") {
8181
let prop_7 = getJSValue(this: globalObject1Ref, name: "prop_7")
8282
try expectEqual(Double.construct(from: prop_7), 3.14)
8383
try expectEqual(Float.construct(from: prop_7), 3.14)
84+
85+
for source: JSValue in [
86+
.number(.infinity), .number(.nan),
87+
.number(Double(UInt64.max).nextUp), .number(Double(Int64.min).nextDown)
88+
] {
89+
try expectNil(Int.construct(from: source))
90+
try expectNil(Int8.construct(from: source))
91+
try expectNil(Int16.construct(from: source))
92+
try expectNil(Int32.construct(from: source))
93+
try expectNil(Int64.construct(from: source))
94+
try expectNil(UInt.construct(from: source))
95+
try expectNil(UInt8.construct(from: source))
96+
try expectNil(UInt16.construct(from: source))
97+
try expectNil(UInt32.construct(from: source))
98+
try expectNil(UInt64.construct(from: source))
99+
}
84100
}
85101

86102
try test("Array Iterator") {

Sources/JavaScriptKit/ConstructibleFromJSValue.swift

+56-4
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,41 @@ extension Double: ConstructibleFromJSValue {}
3535
extension Float: ConstructibleFromJSValue {}
3636

3737
extension SignedInteger where Self: ConstructibleFromJSValue {
38+
/// Construct an instance of `SignedInteger` from the given `JSBigIntExtended`.
39+
///
40+
/// If the value is too large to fit in the `Self` type, `nil` is returned.
41+
///
42+
/// - Parameter bigInt: The `JSBigIntExtended` to decode
43+
public init?(exactly bigInt: JSBigIntExtended) {
44+
self.init(exactly: bigInt.int64Value)
45+
}
46+
47+
/// Construct an instance of `SignedInteger` from the given `JSBigIntExtended`.
48+
///
49+
/// Crash if the value is too large to fit in the `Self` type.
50+
///
51+
/// - Parameter bigInt: The `JSBigIntExtended` to decode
3852
public init(_ bigInt: JSBigIntExtended) {
3953
self.init(bigInt.int64Value)
4054
}
55+
56+
/// Construct an instance of `SignedInteger` from the given `JSValue`.
57+
///
58+
/// Returns `nil` if one of the following conditions is met:
59+
/// - The value is not a number or a bigint.
60+
/// - The value is a number that does not fit or cannot be represented
61+
/// in the `Self` type (e.g. NaN, Infinity).
62+
/// - The value is a bigint that does not fit in the `Self` type.
63+
///
64+
/// If the value is a number, it is rounded towards zero before conversion.
65+
///
66+
/// - Parameter value: The `JSValue` to decode
4167
public static func construct(from value: JSValue) -> Self? {
4268
if let number = value.number {
43-
return Self(number)
69+
return Self(exactly: number.rounded(.towardZero))
4470
}
4571
if let bigInt = value.bigInt as? JSBigIntExtended {
46-
return Self(bigInt)
72+
return Self(exactly: bigInt)
4773
}
4874
return nil
4975
}
@@ -55,15 +81,41 @@ extension Int32: ConstructibleFromJSValue {}
5581
extension Int64: ConstructibleFromJSValue {}
5682

5783
extension UnsignedInteger where Self: ConstructibleFromJSValue {
84+
85+
/// Construct an instance of `UnsignedInteger` from the given `JSBigIntExtended`.
86+
///
87+
/// Returns `nil` if the value is negative or too large to fit in the `Self` type.
88+
///
89+
/// - Parameter bigInt: The `JSBigIntExtended` to decode
90+
public init?(exactly bigInt: JSBigIntExtended) {
91+
self.init(exactly: bigInt.uInt64Value)
92+
}
93+
94+
/// Construct an instance of `UnsignedInteger` from the given `JSBigIntExtended`.
95+
///
96+
/// Crash if the value is negative or too large to fit in the `Self` type.
97+
///
98+
/// - Parameter bigInt: The `JSBigIntExtended` to decode
5899
public init(_ bigInt: JSBigIntExtended) {
59100
self.init(bigInt.uInt64Value)
60101
}
102+
103+
/// Construct an instance of `UnsignedInteger` from the given `JSValue`.
104+
///
105+
/// Returns `nil` if one of the following conditions is met:
106+
/// - The value is not a number or a bigint.
107+
/// - The value is a number that does not fit or cannot be represented
108+
/// in the `Self` type (e.g. NaN, Infinity).
109+
/// - The value is a bigint that does not fit in the `Self` type.
110+
/// - The value is negative.
111+
///
112+
/// - Parameter value: The `JSValue` to decode
61113
public static func construct(from value: JSValue) -> Self? {
62114
if let number = value.number {
63-
return Self(number)
115+
return Self(exactly: number.rounded(.towardZero))
64116
}
65117
if let bigInt = value.bigInt as? JSBigIntExtended {
66-
return Self(bigInt)
118+
return Self(exactly: bigInt)
67119
}
68120
return nil
69121
}

0 commit comments

Comments
 (0)