@@ -18,6 +18,8 @@ final class JSGlueVariableScope {
1818 static let reservedStorageToReturnOptionalFloat = " tmpRetOptionalFloat "
1919 static let reservedStorageToReturnOptionalDouble = " tmpRetOptionalDouble "
2020 static let reservedStorageToReturnOptionalHeapObject = " tmpRetOptionalHeapObject "
21+ static let reservedStorageToReturnJSValuePayload1 = " tmpRetJSValuePayload1 "
22+ static let reservedStorageToReturnJSValuePayload2 = " tmpRetJSValuePayload2 "
2123 static let reservedTextEncoder = " textEncoder "
2224 static let reservedTextDecoder = " textDecoder "
2325 static let reservedTmpRetTag = " tmpRetTag "
@@ -49,6 +51,8 @@ final class JSGlueVariableScope {
4951 reservedStorageToReturnOptionalFloat,
5052 reservedStorageToReturnOptionalDouble,
5153 reservedStorageToReturnOptionalHeapObject,
54+ reservedStorageToReturnJSValuePayload1,
55+ reservedStorageToReturnJSValuePayload2,
5256 reservedTextEncoder,
5357 reservedTextDecoder,
5458 reservedTmpRetTag,
@@ -233,6 +237,143 @@ struct IntrinsicJSFragment: Sendable {
233237 }
234238 )
235239
240+ private static func jsValueKindExpression( for valueExpr: String ) -> String {
241+ // JavaScriptValueKind:
242+ // 0: boolean, 1: string, 2: number, 3: object, 4: null, 5: undefined, 7: symbol, 8: bigint
243+ " ( \( valueExpr) === null) ? 4 : ( \( valueExpr) === undefined) ? 5 : (typeof \( valueExpr) === \" boolean \" ) ? 0 : (typeof \( valueExpr) === \" string \" ) ? 1 : (typeof \( valueExpr) === \" number \" ) ? 2 : (typeof \( valueExpr) === \" symbol \" ) ? 7 : (typeof \( valueExpr) === \" bigint \" ) ? 8 : 3 "
244+ }
245+
246+ /// Lowers a JS value into the (kind, payload1, payload2) triple used by BridgeJS for `JSValue`.
247+ ///
248+ /// - kind: i32 JavaScriptValueKind tag
249+ /// - payload1: i32 (boolean as 0/1, or retained SwiftRuntimeHeap id for string/object/symbol/bigint)
250+ /// - payload2: f64 (number payload)
251+ static let jsValueLowerParameter = IntrinsicJSFragment (
252+ parameters: [ " value " ] ,
253+ printCode: { arguments, scope, printer, cleanupCode in
254+ let value = arguments [ 0 ]
255+ let kindVar = scope. variable ( " kind " )
256+ let payload1Var = scope. variable ( " payload1 " )
257+ let payload2Var = scope. variable ( " payload2 " )
258+
259+ printer. write ( " const \( kindVar) = \( jsValueKindExpression ( for: value) ) ; " )
260+ printer. write ( " let \( payload1Var) = 0; " )
261+ printer. write ( " let \( payload2Var) = 0.0; " )
262+
263+ printer. write ( " switch ( \( kindVar) | 0) { " )
264+ printer. indent {
265+ printer. write ( " case 0: { \( payload1Var) = \( value) ? 1 : 0; break; } " )
266+ printer. write ( " case 2: { \( payload2Var) = + \( value) ; break; } " )
267+ printer. write (
268+ " case 1: case 3: case 7: case 8: { \( payload1Var) = \( JSGlueVariableScope . reservedSwift) .memory.retain( \( value) ); break; } "
269+ )
270+ printer. write ( " case 4: case 5: { break; } " )
271+ printer. write ( " default: { throw new Error( \" Unknown JSValue kind: \" + String( \( kindVar) )); } " )
272+ }
273+ printer. write ( " } " )
274+
275+ return [ kindVar, payload1Var, payload2Var]
276+ }
277+ )
278+
279+ /// Lifts a Swift-returned `JSValue` from the (tag + tmpRet payload stacks) side channel.
280+ static let jsValueLiftReturn = IntrinsicJSFragment (
281+ parameters: [ ] ,
282+ printCode: { _, scope, printer, cleanupCode in
283+ let kindVar = scope. variable ( " kind " )
284+ let resultVar = scope. variable ( " ret " )
285+ printer. write ( " const \( kindVar) = ( \( JSGlueVariableScope . reservedTmpRetTag) | 0); " )
286+ printer. write ( " let \( resultVar) ; " )
287+ printer. write ( " switch ( \( kindVar) ) { " )
288+ printer. indent {
289+ printer. write ( " case 0: { " )
290+ printer. indent {
291+ printer. write ( " const b = \( JSGlueVariableScope . reservedTmpRetInts) .pop(); " )
292+ printer. write ( " \( resultVar) = (b !== 0); " )
293+ }
294+ printer. write ( " break; } " )
295+ printer. write ( " case 2: { " )
296+ printer. indent {
297+ printer. write ( " \( resultVar) = \( JSGlueVariableScope . reservedTmpRetF64s) .pop(); " )
298+ }
299+ printer. write ( " break; } " )
300+ printer. write ( " case 4: { \( resultVar) = null; break; } " )
301+ printer. write ( " case 5: { \( resultVar) = undefined; break; } " )
302+ printer. write ( " case 1: case 3: case 7: case 8: { " )
303+ printer. indent {
304+ printer. write ( " const objectId = \( JSGlueVariableScope . reservedTmpRetInts) .pop(); " )
305+ printer. write ( " \( resultVar) = \( JSGlueVariableScope . reservedSwift) .memory.getObject(objectId); " )
306+ printer. write ( " \( JSGlueVariableScope . reservedSwift) .memory.release(objectId); " )
307+ }
308+ printer. write ( " break; } " )
309+ printer. write (
310+ " default: { throw new Error( \" Unknown JSValue kind returned from Swift: \" + String( \( kindVar) )); } "
311+ )
312+ }
313+ printer. write ( " } " )
314+ return [ resultVar]
315+ }
316+ )
317+
318+ /// Lifts a Swift-sent `JSValue` parameter from the (kind, payload1, payload2) triple.
319+ static let jsValueLiftParameter = IntrinsicJSFragment (
320+ parameters: [ " kind " , " payload1 " , " payload2 " ] ,
321+ printCode: { arguments, scope, printer, cleanupCode in
322+ let kind = arguments [ 0 ]
323+ let payload1 = arguments [ 1 ]
324+ let payload2 = arguments [ 2 ]
325+ let resultVar = scope. variable ( " value " )
326+
327+ printer. write ( " let \( resultVar) ; " )
328+ printer. write ( " switch ( \( kind) | 0) { " )
329+ printer. indent {
330+ printer. write ( " case 0: { \( resultVar) = ( \( payload1) !== 0); break; } " )
331+ printer. write ( " case 2: { \( resultVar) = \( payload2) ; break; } " )
332+ printer. write (
333+ " case 1: case 3: case 7: case 8: { \( resultVar) = \( JSGlueVariableScope . reservedSwift) .memory.getObject( \( payload1) ); break; } "
334+ )
335+ printer. write ( " case 4: { \( resultVar) = null; break; } " )
336+ printer. write ( " case 5: { \( resultVar) = undefined; break; } " )
337+ printer. write (
338+ " default: { throw new Error( \" Unknown JSValue kind passed from Swift: \" + String( \( kind) )); } "
339+ )
340+ }
341+ printer. write ( " } " )
342+ return [ resultVar]
343+ }
344+ )
345+
346+ /// Lowers a JS `JSValue` return into an i32 kind, storing payloads in side-channel vars.
347+ static let jsValueLowerReturn = IntrinsicJSFragment (
348+ parameters: [ " value " ] ,
349+ printCode: { arguments, scope, printer, cleanupCode in
350+ let value = arguments [ 0 ]
351+ let kindVar = scope. variable ( " kind " )
352+ let payload1Var = scope. variable ( " payload1 " )
353+ let payload2Var = scope. variable ( " payload2 " )
354+
355+ printer. write ( " const \( kindVar) = \( jsValueKindExpression ( for: value) ) ; " )
356+ printer. write ( " let \( payload1Var) = 0; " )
357+ printer. write ( " let \( payload2Var) = 0.0; " )
358+
359+ printer. write ( " switch ( \( kindVar) | 0) { " )
360+ printer. indent {
361+ printer. write ( " case 0: { \( payload1Var) = \( value) ? 1 : 0; break; } " )
362+ printer. write ( " case 2: { \( payload2Var) = + \( value) ; break; } " )
363+ printer. write (
364+ " case 1: case 3: case 7: case 8: { \( payload1Var) = \( JSGlueVariableScope . reservedSwift) .memory.retain( \( value) ); break; } "
365+ )
366+ printer. write ( " case 4: case 5: { break; } " )
367+ printer. write ( " default: { throw new Error( \" Unknown JSValue kind: \" + String( \( kindVar) )); } " )
368+ }
369+ printer. write ( " } " )
370+
371+ printer. write ( " \( JSGlueVariableScope . reservedStorageToReturnJSValuePayload1) = \( payload1Var) ; " )
372+ printer. write ( " \( JSGlueVariableScope . reservedStorageToReturnJSValuePayload2) = \( payload2Var) ; " )
373+ return [ kindVar]
374+ }
375+ )
376+
236377 static let swiftHeapObjectLowerParameter = IntrinsicJSFragment (
237378 parameters: [ " value " ] ,
238379 printCode: { arguments, scope, printer, cleanupCode in
@@ -1372,6 +1513,7 @@ struct IntrinsicJSFragment: Sendable {
13721513 switch type {
13731514 case . int, . float, . double, . bool, . unsafePointer: return . identity
13741515 case . string: return . stringLowerParameter
1516+ case . jsValue: return . jsValueLowerParameter
13751517 case . jsObject: return . jsObjectLowerParameter
13761518 case . swiftHeapObject:
13771519 return . swiftHeapObjectLowerParameter
@@ -1416,6 +1558,7 @@ struct IntrinsicJSFragment: Sendable {
14161558 case . int, . float, . double: return . identity
14171559 case . bool: return . boolLiftReturn
14181560 case . string: return . stringLiftReturn
1561+ case . jsValue: return . jsValueLiftReturn
14191562 case . jsObject: return . jsObjectLiftReturn
14201563 case . swiftHeapObject( let name) : return . swiftHeapObjectLiftReturn( name)
14211564 case . unsafePointer: return . identity
@@ -1462,6 +1605,7 @@ struct IntrinsicJSFragment: Sendable {
14621605 case . int, . float, . double: return . identity
14631606 case . bool: return . boolLiftParameter
14641607 case . string: return . stringLiftParameter
1608+ case . jsValue: return . jsValueLiftParameter
14651609 case . jsObject: return . jsObjectLiftParameter
14661610 case . unsafePointer: return . identity
14671611 case . swiftHeapObject( let name) :
@@ -1563,6 +1707,7 @@ struct IntrinsicJSFragment: Sendable {
15631707 case . int, . float, . double: return . identity
15641708 case . bool: return . boolLowerReturn
15651709 case . string: return . stringLowerReturn
1710+ case . jsValue: return . jsValueLowerReturn
15661711 case . jsObject: return . jsObjectLowerReturn
15671712 case . unsafePointer: return . identity
15681713 case . swiftHeapObject( let name) :
@@ -2756,6 +2901,13 @@ struct IntrinsicJSFragment: Sendable {
27562901 allStructs: [ ExportedStruct ]
27572902 ) -> IntrinsicJSFragment {
27582903 switch field. type {
2904+ case . jsValue:
2905+ return IntrinsicJSFragment (
2906+ parameters: [ " value " ] ,
2907+ printCode: { _, _, _, _ in
2908+ fatalError ( " JSValue is not supported as a Swift struct field in BridgeJS " )
2909+ }
2910+ )
27592911 case . string:
27602912 return IntrinsicJSFragment (
27612913 parameters: [ " value " ] ,
@@ -3242,6 +3394,13 @@ struct IntrinsicJSFragment: Sendable {
32423394 allStructs: [ ExportedStruct ]
32433395 ) -> IntrinsicJSFragment {
32443396 switch field. type {
3397+ case . jsValue:
3398+ return IntrinsicJSFragment (
3399+ parameters: [ ] ,
3400+ printCode: { _, _, _, _ in
3401+ fatalError ( " JSValue is not supported as a Swift struct field in BridgeJS " )
3402+ }
3403+ )
32453404 case . string:
32463405 return IntrinsicJSFragment (
32473406 parameters: [ ] ,
0 commit comments