From 788ad44af6b3e327122f379a445fda7c4d10cc16 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 9 Feb 2026 11:00:49 +0900 Subject: [PATCH 1/2] [NFC] BridgeJS: Make JSGlueGen and IntrinsicJSFragment methods throwable Instead of crashing on errors in JS code generation for BridgeJSLink, we should prefer propagating errors. --- .../Sources/BridgeJSLink/BridgeJSLink.swift | 77 +++--- .../BridgeJSLink/CodeFragmentPrinter.swift | 4 +- .../Sources/BridgeJSLink/JSGlueGen.swift | 257 +++++++++--------- 3 files changed, 173 insertions(+), 165 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 13ea4e910..d1f8363d2 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -261,11 +261,11 @@ public struct BridgeJSLink { ] } - private func generateAddImports(needsImportsObject: Bool) -> CodeFragmentPrinter { + private func generateAddImports(needsImportsObject: Bool) throws -> CodeFragmentPrinter { let printer = CodeFragmentPrinter() let allStructs = skeletons.compactMap { $0.exported?.structs }.flatMap { $0 } printer.write("return {") - printer.indent { + try printer.indent { printer.write(lines: [ "/**", " * @param {WebAssembly.Imports} importObject", @@ -273,7 +273,7 @@ public struct BridgeJSLink { "addImports: (importObject, importsContext) => {", ]) - printer.indent { + try printer.indent { printer.write(lines: [ "bjs = {};", "importObject[\"bjs\"] = bjs;", @@ -687,20 +687,21 @@ public struct BridgeJSLink { for signature in closureSignatures.sorted(by: { $0.mangleName < $1.mangleName }) { let invokeFuncName = "invoke_js_callback_\(moduleName)_\(signature.mangleName)" - printer.write( - lines: generateInvokeFunction( - signature: signature, - functionName: invokeFuncName - ) + let invokeLines = try generateInvokeFunction( + signature: signature, + functionName: invokeFuncName ) + printer.write(lines: invokeLines) let lowerFuncName = "lower_closure_\(moduleName)_\(signature.mangleName)" let makeFuncName = "make_swift_closure_\(moduleName)_\(signature.mangleName)" printer.write("bjs[\"\(makeFuncName)\"] = function(boxPtr, file, line) {") - printer.indent { - printer.write( - lines: generateLowerClosureFunction(signature: signature, functionName: lowerFuncName) + try printer.indent { + let lowerLines = try generateLowerClosureFunction( + signature: signature, + functionName: lowerFuncName ) + printer.write(lines: lowerLines) printer.write( "return \(JSGlueVariableScope.reservedMakeSwiftClosure)(boxPtr, file, line, \(lowerFuncName));" ) @@ -788,7 +789,7 @@ public struct BridgeJSLink { private func generateInvokeFunction( signature: ClosureSignature, functionName: String - ) -> [String] { + ) throws -> [String] { let printer = CodeFragmentPrinter() let scope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry) let cleanupCode = CodeFragmentPrinter() @@ -806,20 +807,20 @@ public struct BridgeJSLink { printer.nextLine() printer.write("bjs[\"\(functionName)\"] = function(\(invokeParams.joined(separator: ", "))) {") - printer.indent { + try printer.indent { printer.write("try {") - printer.indent { + try printer.indent { printer.write("const callback = \(JSGlueVariableScope.reservedSwift).memory.getObject(callbackId);") for (index, paramType) in signature.parameters.enumerated() { - let fragment = try! IntrinsicJSFragment.closureLiftParameter(type: paramType) + let fragment = try IntrinsicJSFragment.closureLiftParameter(type: paramType) let args: [String] if case .nullable = paramType { args = ["param\(index)IsSome", "param\(index)Value", "param\(index)"] } else { args = ["param\(index)Id", "param\(index)"] } - _ = fragment.printCode(args, scope, printer, cleanupCode) + _ = try fragment.printCode(args, scope, printer, cleanupCode) } let callbackParams = signature.parameters.indices.map { "param\($0)" }.joined(separator: ", ") @@ -837,14 +838,14 @@ public struct BridgeJSLink { break } - let returnFragment = try! IntrinsicJSFragment.closureLowerReturn(type: signature.returnType) - _ = returnFragment.printCode(["result"], scope, printer, cleanupCode) + let returnFragment = try IntrinsicJSFragment.closureLowerReturn(type: signature.returnType) + _ = try returnFragment.printCode(["result"], scope, printer, cleanupCode) } printer.write("} catch (error) {") - printer.indent { + try printer.indent { printer.write("\(JSGlueVariableScope.reservedSetException)?.(error);") let errorFragment = IntrinsicJSFragment.closureErrorReturn(type: signature.returnType) - _ = errorFragment.printCode([], scope, printer, cleanupCode) + _ = try errorFragment.printCode([], scope, printer, cleanupCode) } printer.write("}") } @@ -857,7 +858,7 @@ public struct BridgeJSLink { private func generateLowerClosureFunction( signature: ClosureSignature, functionName: String - ) -> [String] { + ) throws -> [String] { let printer = CodeFragmentPrinter() let scope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry) let cleanupCode = CodeFragmentPrinter() @@ -865,13 +866,13 @@ public struct BridgeJSLink { printer.write( "const \(functionName) = function(\(signature.parameters.indices.map { "param\($0)" }.joined(separator: ", "))) {" ) - printer.indent { + try printer.indent { var invokeArgs: [String] = ["boxPtr"] for (index, paramType) in signature.parameters.enumerated() { let paramName = "param\(index)" - let fragment = try! IntrinsicJSFragment.lowerParameter(type: paramType) - let lowered = fragment.printCode([paramName], scope, printer, cleanupCode) + let fragment = try IntrinsicJSFragment.lowerParameter(type: paramType) + let lowered = try fragment.printCode([paramName], scope, printer, cleanupCode) invokeArgs.append(contentsOf: lowered) } @@ -894,8 +895,8 @@ public struct BridgeJSLink { } printer.write("}") - let returnFragment = try! IntrinsicJSFragment.closureLiftReturn(type: signature.returnType) - _ = returnFragment.printCode([invokeResultName], scope, printer, cleanupCode) + let returnFragment = try IntrinsicJSFragment.closureLiftReturn(type: signature.returnType) + _ = try returnFragment.printCode([invokeResultName], scope, printer, cleanupCode) } printer.write("};") @@ -1073,7 +1074,7 @@ public struct BridgeJSLink { // Main function declaration printer.write("export async function createInstantiator(options, \(JSGlueVariableScope.reservedSwift)) {") - printer.indent { + try printer.indent { printer.write(lines: generateVariableDeclarations()) let bodyPrinter = CodeFragmentPrinter() @@ -1083,7 +1084,7 @@ public struct BridgeJSLink { let structScope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry) let structCleanup = CodeFragmentPrinter() let fragment = IntrinsicJSFragment.structHelper(structDefinition: structDef, allStructs: allStructs) - _ = fragment.printCode([structDef.name], structScope, structPrinter, structCleanup) + _ = try fragment.printCode([structDef.name], structScope, structPrinter, structCleanup) bodyPrinter.write(lines: structPrinter.lines) } @@ -1095,11 +1096,11 @@ public struct BridgeJSLink { let enumScope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry) let enumCleanup = CodeFragmentPrinter() let fragment = IntrinsicJSFragment.associatedValueEnumHelperFactory(enumDefinition: enumDef) - _ = fragment.printCode([enumDef.valuesName], enumScope, enumPrinter, enumCleanup) + _ = try fragment.printCode([enumDef.valuesName], enumScope, enumPrinter, enumCleanup) bodyPrinter.write(lines: enumPrinter.lines) } bodyPrinter.nextLine() - bodyPrinter.write(contentsOf: generateAddImports(needsImportsObject: data.needsImportsObject)) + bodyPrinter.write(contentsOf: try generateAddImports(needsImportsObject: data.needsImportsObject)) if !intrinsicRegistry.isEmpty { printer.write(lines: intrinsicRegistry.emitLines()) @@ -1356,7 +1357,7 @@ public struct BridgeJSLink { loweringFragment.parameters.count == 1, "Lowering fragment should have exactly one parameter to lower" ) - let loweredValues = loweringFragment.printCode([param.name], scope, body, cleanupCode) + let loweredValues = try loweringFragment.printCode([param.name], scope, body, cleanupCode) parameterForwardings.append(contentsOf: loweredValues) } @@ -1388,7 +1389,7 @@ public struct BridgeJSLink { body.write("const \(returnVariable) = \(call);") fragmentArguments = [returnVariable] } - let liftedValues = liftingFragment.printCode(fragmentArguments, scope, body, cleanupCode) + let liftedValues = try liftingFragment.printCode(fragmentArguments, scope, body, cleanupCode) assert(liftedValues.count <= 1, "Lifting fragment should produce at most one value") return liftedValues.first } @@ -1646,7 +1647,7 @@ public struct BridgeJSLink { switch enumDefinition.enumType { case .simple: let fragment = IntrinsicJSFragment.simpleEnumHelper(enumDefinition: enumDefinition) - _ = fragment.printCode([enumValuesName], scope, printer, cleanup) + _ = try fragment.printCode([enumValuesName], scope, printer, cleanup) jsTopLevelLines.append(contentsOf: printer.lines) case .rawValue: guard enumDefinition.rawType != nil else { @@ -1654,11 +1655,11 @@ public struct BridgeJSLink { } let fragment = IntrinsicJSFragment.rawValueEnumHelper(enumDefinition: enumDefinition) - _ = fragment.printCode([enumValuesName], scope, printer, cleanup) + _ = try fragment.printCode([enumValuesName], scope, printer, cleanup) jsTopLevelLines.append(contentsOf: printer.lines) case .associatedValue: let fragment = IntrinsicJSFragment.associatedValueEnumValues(enumDefinition: enumDefinition) - _ = fragment.printCode([enumValuesName], scope, printer, cleanup) + _ = try fragment.printCode([enumValuesName], scope, printer, cleanup) jsTopLevelLines.append(contentsOf: printer.lines) case .namespace: break @@ -2252,7 +2253,7 @@ extension BridgeJSLink { valuesToLift = liftingFragment.parameters.map { scope.variable(param.name + $0.capitalizedFirstLetter) } parameterNames.append(contentsOf: valuesToLift) } - let liftedValues = liftingFragment.printCode(valuesToLift, scope, body, cleanupCode) + let liftedValues = try liftingFragment.printCode(valuesToLift, scope, body, cleanupCode) assert(liftedValues.count == 1, "Lifting fragment should produce exactly one value") parameterForwardings.append(contentsOf: liftedValues) } @@ -2362,7 +2363,7 @@ extension BridgeJSLink { ) let fragment = try IntrinsicJSFragment.protocolPropertyOptionalToSideChannel(wrappedType: wrappedType) - _ = fragment.printCode([resultVar], scope, body, cleanupCode) + _ = try fragment.printCode([resultVar], scope, body, cleanupCode) return nil // Side-channel types return nil (no direct return value) } @@ -2415,7 +2416,7 @@ extension BridgeJSLink { loweringFragment: IntrinsicJSFragment ) throws -> String? { assert(loweringFragment.parameters.count <= 1, "Lowering fragment should have at most one parameter") - let loweredValues = loweringFragment.printCode(returnExpr.map { [$0] } ?? [], scope, body, cleanupCode) + let loweredValues = try loweringFragment.printCode(returnExpr.map { [$0] } ?? [], scope, body, cleanupCode) assert(loweredValues.count <= 1, "Lowering fragment should produce at most one value") return loweredValues.first } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/CodeFragmentPrinter.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/CodeFragmentPrinter.swift index 8c5a47b23..4bad3dd34 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/CodeFragmentPrinter.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/CodeFragmentPrinter.swift @@ -6,10 +6,10 @@ final class JSIntrinsicRegistry { entries.isEmpty } - func register(name: String, build: (CodeFragmentPrinter) -> Void) { + func register(name: String, build: (CodeFragmentPrinter) throws -> Void) rethrows { guard entries[name] == nil else { return } let printer = CodeFragmentPrinter() - build(printer) + try build(printer) entries[name] = printer.lines } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 68230e8d1..9402cdd9d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -83,8 +83,8 @@ final class JSGlueVariableScope { return suffixedName } - func registerIntrinsic(_ name: String, build: (CodeFragmentPrinter) -> Void) { - intrinsicRegistry.register(name: name, build: build) + func registerIntrinsic(_ name: String, build: (CodeFragmentPrinter) throws -> Void) rethrows { + try intrinsicRegistry.register(name: name, build: build) } func makeChildScope() -> JSGlueVariableScope { @@ -162,7 +162,7 @@ struct IntrinsicJSFragment: Sendable { _ scope: JSGlueVariableScope, _ printer: CodeFragmentPrinter, _ cleanupCode: CodeFragmentPrinter - ) -> [String] + ) throws -> [String] /// A fragment that does nothing static let void = IntrinsicJSFragment( @@ -505,7 +505,7 @@ struct IntrinsicJSFragment: Sendable { return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, scope, printer, cleanupCode in - let lowered = jsValueLower.printCode(arguments, scope, printer, cleanupCode) + let lowered = try jsValueLower.printCode(arguments, scope, printer, cleanupCode) let kindVar = lowered[0] let payload1Var = lowered[1] let payload2Var = lowered[2] @@ -521,7 +521,7 @@ struct IntrinsicJSFragment: Sendable { return IntrinsicJSFragment( parameters: ["value"], printCode: { arguments, scope, printer, cleanupCode in - let lowered = jsValueLower.printCode(arguments, scope, printer, cleanupCode) + let lowered = try jsValueLower.printCode(arguments, scope, printer, cleanupCode) let kindVar = lowered[0] let payload1Var = lowered[1] let payload2Var = lowered[2] @@ -627,7 +627,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["isSome", "kind", "payload1", "payload2"], printCode: { arguments, scope, printer, cleanupCode in let isSome = arguments[0] - let lifted = jsValueLiftParameter.printCode( + let lifted = try jsValueLiftParameter.printCode( [arguments[1], arguments[2], arguments[3]], scope, printer, @@ -723,9 +723,9 @@ struct IntrinsicJSFragment: Sendable { let arrayVar = scope.variable("arrayValue") printer.write("let \(arrayVar);") printer.write("if (\(isSome)) {") - printer.indent { - let arrayLiftFragment = try! arrayLift(elementType: elementType) - let liftResults = arrayLiftFragment.printCode([], scope, printer, cleanupCode) + try printer.indent { + let arrayLiftFragment = try arrayLift(elementType: elementType) + let liftResults = try arrayLiftFragment.printCode([], scope, printer, cleanupCode) if let liftResult = liftResults.first { printer.write("\(arrayVar) = \(liftResult);") } @@ -740,9 +740,9 @@ struct IntrinsicJSFragment: Sendable { let dictVar = scope.variable("dictValue") printer.write("let \(dictVar);") printer.write("if (\(isSome)) {") - printer.indent { - let dictLiftFragment = try! dictionaryLift(valueType: valueType) - let liftResults = dictLiftFragment.printCode([], scope, printer, cleanupCode) + try printer.indent { + let dictLiftFragment = try dictionaryLift(valueType: valueType) + let liftResults = try dictLiftFragment.printCode([], scope, printer, cleanupCode) if let liftResult = liftResults.first { printer.write("\(dictVar) = \(liftResult);") } @@ -805,7 +805,7 @@ struct IntrinsicJSFragment: Sendable { return ["+\(isSomeVar)", "\(isSomeVar) ? \(idVar) : 0", "\(isSomeVar) ? \(bytesVar).length : 0"] case .jsValue: - let lowered = jsValueLower.printCode([value], scope, printer, cleanupCode) + let lowered = try jsValueLower.printCode([value], scope, printer, cleanupCode) return ["+\(isSomeVar)"] + lowered case .associatedValueEnum(let fullName): let base = fullName.components(separatedBy: ".").last ?? fullName @@ -833,10 +833,10 @@ struct IntrinsicJSFragment: Sendable { let cleanupArrayVar = scope.variable("\(value)Cleanups") printer.write("const \(cleanupArrayVar) = [];") printer.write("if (\(isSomeVar)) {") - printer.indent { - let arrayLowerFragment = try! arrayLower(elementType: elementType) + try printer.indent { + let arrayLowerFragment = try arrayLower(elementType: elementType) let arrayCleanup = CodeFragmentPrinter() - let _ = arrayLowerFragment.printCode([value], scope, printer, arrayCleanup) + let _ = try arrayLowerFragment.printCode([value], scope, printer, arrayCleanup) if !arrayCleanup.lines.isEmpty { for line in arrayCleanup.lines { printer.write("\(cleanupArrayVar).push(() => { \(line) });") @@ -850,10 +850,10 @@ struct IntrinsicJSFragment: Sendable { let cleanupArrayVar = scope.variable("\(value)Cleanups") printer.write("const \(cleanupArrayVar) = [];") printer.write("if (\(isSomeVar)) {") - printer.indent { - let dictLowerFragment = try! dictionaryLower(valueType: valueType) + try printer.indent { + let dictLowerFragment = try dictionaryLower(valueType: valueType) let dictCleanup = CodeFragmentPrinter() - let _ = dictLowerFragment.printCode([value], scope, printer, dictCleanup) + let _ = try dictLowerFragment.printCode([value], scope, printer, dictCleanup) if !dictCleanup.lines.isEmpty { for line in dictCleanup.lines { printer.write("\(cleanupArrayVar).push(() => { \(line) });") @@ -1002,9 +1002,9 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(isSomeVar) = \(scope.popI32());") printer.write("let \(resultVar);") printer.write("if (\(isSomeVar)) {") - printer.indent { - let arrayLiftFragment = try! arrayLift(elementType: elementType) - let liftResults = arrayLiftFragment.printCode([], scope, printer, cleanupCode) + try printer.indent { + let arrayLiftFragment = try arrayLift(elementType: elementType) + let liftResults = try arrayLiftFragment.printCode([], scope, printer, cleanupCode) if let liftResult = liftResults.first { printer.write("\(resultVar) = \(liftResult);") } @@ -1019,9 +1019,9 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(isSomeVar) = \(scope.popI32());") printer.write("let \(resultVar);") printer.write("if (\(isSomeVar)) {") - printer.indent { - let dictLiftFragment = try! dictionaryLift(valueType: valueType) - let liftResults = dictLiftFragment.printCode([], scope, printer, cleanupCode) + try printer.indent { + let dictLiftFragment = try dictionaryLift(valueType: valueType) + let liftResults = try dictLiftFragment.printCode([], scope, printer, cleanupCode) if let liftResult = liftResults.first { printer.write("\(resultVar) = \(liftResult);") } @@ -1036,8 +1036,8 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(isSomeVar) = \(scope.popI32());") printer.write("let \(resultVar);") printer.write("if (\(isSomeVar)) {") - printer.indent { - let lifted = jsValueLift.printCode([], scope, printer, cleanupCode) + try printer.indent { + let lifted = try jsValueLift.printCode([], scope, printer, cleanupCode) if let liftedValue = lifted.first { printer.write("\(resultVar) = \(liftedValue);") } @@ -1111,7 +1111,7 @@ struct IntrinsicJSFragment: Sendable { printer.write("bjs[\"swift_js_return_optional_object\"](\(isSomeVar) ? 1 : 0, \(idVar));") case .jsValue: if value != "undefined" { - let lowered = jsValueLower.printCode([value], scope, printer, cleanupCode) + let lowered = try jsValueLower.printCode([value], scope, printer, cleanupCode) let kindVar = lowered[0] let payload1Var = lowered[1] let payload2Var = lowered[2] @@ -1122,10 +1122,10 @@ struct IntrinsicJSFragment: Sendable { scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) case .array(let elementType): printer.write("if (\(isSomeVar)) {") - printer.indent { - let arrayLowerFragment = try! arrayLower(elementType: elementType) + try printer.indent { + let arrayLowerFragment = try arrayLower(elementType: elementType) let arrayCleanup = CodeFragmentPrinter() - let _ = arrayLowerFragment.printCode([value], scope, printer, arrayCleanup) + let _ = try arrayLowerFragment.printCode([value], scope, printer, arrayCleanup) if !arrayCleanup.lines.isEmpty { for line in arrayCleanup.lines { printer.write(line) @@ -1185,21 +1185,21 @@ struct IntrinsicJSFragment: Sendable { cleanupCode.write("if (\(cleanupVar)) { \(cleanupVar)(); }") case .dictionary(let valueType): printer.write("if (\(isSomeVar)) {") - printer.indent { + try printer.indent { let cleanupArrayVar = scope.variable("arrayCleanups") let entriesVar = scope.variable("entries") let entryVar = scope.variable("entry") printer.write("const \(cleanupArrayVar) = [];") printer.write("const \(entriesVar) = Object.entries(\(value));") printer.write("for (const \(entryVar) of \(entriesVar)) {") - printer.indent { + try printer.indent { let keyVar = scope.variable("key") let valueVar = scope.variable("value") printer.write("const [\(keyVar), \(valueVar)] = \(entryVar);") - let keyFragment = try! stackLowerFragment(elementType: .string) + let keyFragment = try stackLowerFragment(elementType: .string) let keyCleanup = CodeFragmentPrinter() - let _ = keyFragment.printCode([keyVar], scope, printer, keyCleanup) + let _ = try keyFragment.printCode([keyVar], scope, printer, keyCleanup) if !keyCleanup.lines.isEmpty { printer.write("\(cleanupArrayVar).push(() => {") printer.indent { @@ -1210,9 +1210,9 @@ struct IntrinsicJSFragment: Sendable { printer.write("});") } - let valueFragment = try! stackLowerFragment(elementType: valueType) + let valueFragment = try stackLowerFragment(elementType: valueType) let valueCleanup = CodeFragmentPrinter() - let _ = valueFragment.printCode([valueVar], scope, printer, valueCleanup) + let _ = try valueFragment.printCode([valueVar], scope, printer, valueCleanup) if !valueCleanup.lines.isEmpty { printer.write("\(cleanupArrayVar).push(() => {") printer.indent { @@ -1302,7 +1302,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["value", "targetVar"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = boolLiftParameter - let lifted = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("let \(arguments[1]) = \(lifted[0]);") return [] } @@ -1312,7 +1312,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["objectId", "targetVar"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = stringLiftParameter - let lifted = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("let \(arguments[1]) = String(\(lifted[0]));") return [] } @@ -1324,7 +1324,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["objectId", "targetVar"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = stringLiftParameter - let lifted = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("let \(arguments[1]) = String(\(lifted[0]));") return [] } @@ -1334,7 +1334,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["value", "targetVar"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = boolLiftParameter - let lifted = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("let \(arguments[1]) = \(lifted[0]);") return [] } @@ -1494,7 +1494,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["result"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = boolLowerReturn - let lowered = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lowered = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("return \(lowered[0]);") return [] } @@ -1520,7 +1520,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["result"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = stringLowerReturn - let lowered = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lowered = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("return \(lowered[0]);") return [] } @@ -1530,7 +1530,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["result"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = jsObjectLowerReturn - let lowered = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lowered = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("return \(lowered[0]);") return [] } @@ -1540,7 +1540,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["result"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = swiftHeapObjectLowerReturn - let lowered = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lowered = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("return \(lowered[0]);") return [] } @@ -1552,7 +1552,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["result"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = stringLowerReturn - let lowered = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lowered = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("return \(lowered[0]);") return [] } @@ -1562,7 +1562,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["result"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = boolLowerReturn - let lowered = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lowered = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("return \(lowered[0]);") return [] } @@ -1697,7 +1697,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["invokeCall"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = boolLiftReturn - let lifted = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("return \(lifted[0]);") return [] } @@ -1716,7 +1716,7 @@ struct IntrinsicJSFragment: Sendable { printCode: { arguments, scope, printer, cleanupCode in printer.write("const resultLen = \(arguments[0]);") let baseFragment = stringLiftReturn - let lifted = baseFragment.printCode([], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([], scope, printer, cleanupCode) printer.write("return \(lifted[0]);") return [] } @@ -1727,7 +1727,7 @@ struct IntrinsicJSFragment: Sendable { printCode: { arguments, scope, printer, cleanupCode in printer.write("const resultId = \(arguments[0]);") let baseFragment = jsObjectLiftReturn - let lifted = baseFragment.printCode(["resultId"], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode(["resultId"], scope, printer, cleanupCode) printer.write("return \(lifted[0]);") return [] } @@ -1763,7 +1763,7 @@ struct IntrinsicJSFragment: Sendable { printCode: { arguments, scope, printer, cleanupCode in printer.write("const resultLen = \(arguments[0]);") let baseFragment = stringLiftReturn - let lifted = baseFragment.printCode([], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([], scope, printer, cleanupCode) printer.write("return \(lifted[0]);") return [] } @@ -1773,7 +1773,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["invokeCall"], printCode: { arguments, scope, printer, cleanupCode in let baseFragment = boolLiftReturn - let lifted = baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([arguments[0]], scope, printer, cleanupCode) printer.write("return \(lifted[0]);") return [] } @@ -1823,7 +1823,7 @@ struct IntrinsicJSFragment: Sendable { context: .importTS, kind: kind ) - let lifted = baseFragment.printCode([], scope, printer, cleanupCode) + let lifted = try baseFragment.printCode([], scope, printer, cleanupCode) if !lifted.isEmpty { printer.write("return \(lifted[0]);") } @@ -2186,10 +2186,10 @@ struct IntrinsicJSFragment: Sendable { // Generate lower function printer.write("lower: (value) => {") - printer.indent { + try printer.indent { printer.write("const enumTag = value.tag;") printer.write("switch (enumTag) {") - printer.indent { + try printer.indent { let lowerPrinter = CodeFragmentPrinter() for enumCase in enumDefinition.cases { let caseName = enumCase.name.capitalizedFirstLetter @@ -2197,7 +2197,12 @@ struct IntrinsicJSFragment: Sendable { let caseCleanup = CodeFragmentPrinter() caseCleanup.indent() let fragment = IntrinsicJSFragment.associatedValuePushPayload(enumCase: enumCase) - _ = fragment.printCode(["value", enumName, caseName], caseScope, lowerPrinter, caseCleanup) + _ = try fragment.printCode( + ["value", enumName, caseName], + caseScope, + lowerPrinter, + caseCleanup + ) } for line in lowerPrinter.lines { @@ -2214,10 +2219,10 @@ struct IntrinsicJSFragment: Sendable { printer.write( "lift: (tag) => {" ) - printer.indent { + try printer.indent { printer.write("tag = tag | 0;") printer.write("switch (tag) {") - printer.indent { + try printer.indent { let liftPrinter = CodeFragmentPrinter() for enumCase in enumDefinition.cases { let caseName = enumCase.name.capitalizedFirstLetter @@ -2225,7 +2230,7 @@ struct IntrinsicJSFragment: Sendable { let caseCleanup = CodeFragmentPrinter() let fragment = IntrinsicJSFragment.associatedValuePopPayload(enumCase: enumCase) - _ = fragment.printCode([enumName, caseName], caseScope, liftPrinter, caseCleanup) + _ = try fragment.printCode([enumName, caseName], caseScope, liftPrinter, caseCleanup) } for line in liftPrinter.lines { @@ -2306,7 +2311,7 @@ struct IntrinsicJSFragment: Sendable { printer.write("case \(enumName).Tag.\(caseName): {") - printer.indent { + try printer.indent { if enumCase.associatedValues.isEmpty { printer.write("const cleanup = undefined;") printer.write("return { caseId: \(enumName).Tag.\(caseName), cleanup };") @@ -2316,9 +2321,11 @@ struct IntrinsicJSFragment: Sendable { for (associatedValueIndex, associatedValue) in reversedValues { let prop = associatedValue.label ?? "param\(associatedValueIndex)" - let fragment = IntrinsicJSFragment.associatedValuePushPayload(type: associatedValue.type) + let fragment = try IntrinsicJSFragment.associatedValuePushPayload( + type: associatedValue.type + ) - _ = fragment.printCode(["value.\(prop)"], scope, printer, cleanup) + _ = try fragment.printCode(["value.\(prop)"], scope, printer, cleanup) } if cleanup.lines.isEmpty { @@ -2355,9 +2362,9 @@ struct IntrinsicJSFragment: Sendable { // Process associated values in reverse order (to match the order they'll be popped) for (associatedValueIndex, associatedValue) in enumCase.associatedValues.enumerated().reversed() { let prop = associatedValue.label ?? "param\(associatedValueIndex)" - let fragment = IntrinsicJSFragment.associatedValuePopPayload(type: associatedValue.type) + let fragment = try IntrinsicJSFragment.associatedValuePopPayload(type: associatedValue.type) - let result = fragment.printCode([], scope, casePrinter, cleanup) + let result = try fragment.printCode([], scope, casePrinter, cleanup) let varName = result.first ?? "value_\(associatedValueIndex)" fieldPairs.append("\(prop): \(varName)") @@ -2380,12 +2387,12 @@ struct IntrinsicJSFragment: Sendable { ) } - private static func associatedValuePushPayload(type: BridgeType) -> IntrinsicJSFragment { + private static func associatedValuePushPayload(type: BridgeType) throws -> IntrinsicJSFragment { switch type { case .nullable(let wrappedType, let kind): return associatedValueOptionalPushPayload(wrappedType: wrappedType, kind: kind) default: - return try! stackLowerFragment(elementType: type) + return try stackLowerFragment(elementType: type) } } @@ -2551,10 +2558,10 @@ struct IntrinsicJSFragment: Sendable { let arrCleanupVar = scope.variable("arrCleanup") printer.write("let \(arrCleanupVar);") printer.write("if (\(isSomeVar)) {") - printer.indent { + try printer.indent { let localCleanup = CodeFragmentPrinter() - let arrFragment = try! arrayLower(elementType: elementType) - _ = arrFragment.printCode([value], scope, printer, localCleanup) + let arrFragment = try arrayLower(elementType: elementType) + _ = try arrFragment.printCode([value], scope, printer, localCleanup) let cleanupLines = localCleanup.lines.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty } @@ -2580,12 +2587,12 @@ struct IntrinsicJSFragment: Sendable { ) } - private static func associatedValuePopPayload(type: BridgeType) -> IntrinsicJSFragment { + private static func associatedValuePopPayload(type: BridgeType) throws -> IntrinsicJSFragment { switch type { case .nullable(let wrappedType, let kind): return associatedValueOptionalPopPayload(wrappedType: wrappedType, kind: kind) default: - return try! stackLiftFragment(elementType: type) + return try stackLiftFragment(elementType: type) } } @@ -2602,7 +2609,7 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(isSomeVar) = \(scope.popI32());") printer.write("let \(optVar);") printer.write("if (\(isSomeVar)) {") - printer.indent { + try printer.indent { // For optional associated value enums, Swift uses bridgeJSLowerParameter() // which pushes caseId to i32Stack (not tagStack like bridgeJSLowerReturn()). if case .associatedValueEnum(let fullName) = wrappedType { @@ -2613,8 +2620,8 @@ struct IntrinsicJSFragment: Sendable { "\(optVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(caseIdVar));" ) } else { - let wrappedFragment = associatedValuePopPayload(type: wrappedType) - let wrappedResults = wrappedFragment.printCode([], scope, printer, cleanup) + let wrappedFragment = try associatedValuePopPayload(type: wrappedType) + let wrappedResults = try wrappedFragment.printCode([], scope, printer, cleanup) if let wrappedResult = wrappedResults.first { printer.write("\(optVar) = \(wrappedResult);") } else { @@ -2690,10 +2697,10 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(cleanupArrayVar) = [];") let elemVar = scope.variable("elem") printer.write("for (const \(elemVar) of \(arr)) {") - printer.indent { - let elementFragment = try! stackLowerFragment(elementType: elementType) + try printer.indent { + let elementFragment = try stackLowerFragment(elementType: elementType) let elementCleanup = CodeFragmentPrinter() - let _ = elementFragment.printCode([elemVar], scope, printer, elementCleanup) + let _ = try elementFragment.printCode([elemVar], scope, printer, elementCleanup) if !elementCleanup.lines.isEmpty { printer.write("\(cleanupArrayVar).push(() => {") printer.indent { @@ -2725,14 +2732,14 @@ struct IntrinsicJSFragment: Sendable { let entryVar = scope.variable("entry") printer.write("const \(entriesVar) = Object.entries(\(dict));") printer.write("for (const \(entryVar) of \(entriesVar)) {") - printer.indent { + try printer.indent { let keyVar = scope.variable("key") let valueVar = scope.variable("value") printer.write("const [\(keyVar), \(valueVar)] = \(entryVar);") - let keyFragment = try! stackLowerFragment(elementType: .string) + let keyFragment = try stackLowerFragment(elementType: .string) let keyCleanup = CodeFragmentPrinter() - let _ = keyFragment.printCode([keyVar], scope, printer, keyCleanup) + let _ = try keyFragment.printCode([keyVar], scope, printer, keyCleanup) if !keyCleanup.lines.isEmpty { printer.write("\(cleanupArrayVar).push(() => {") printer.indent { @@ -2743,9 +2750,9 @@ struct IntrinsicJSFragment: Sendable { printer.write("});") } - let valueFragment = try! stackLowerFragment(elementType: valueType) + let valueFragment = try stackLowerFragment(elementType: valueType) let valueCleanup = CodeFragmentPrinter() - let _ = valueFragment.printCode([valueVar], scope, printer, valueCleanup) + let _ = try valueFragment.printCode([valueVar], scope, printer, valueCleanup) if !valueCleanup.lines.isEmpty { printer.write("\(cleanupArrayVar).push(() => {") printer.indent { @@ -2776,9 +2783,9 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(lenVar) = \(scope.popI32());") printer.write("const \(resultVar) = [];") printer.write("for (let \(iVar) = 0; \(iVar) < \(lenVar); \(iVar)++) {") - printer.indent { - let elementFragment = try! stackLiftFragment(elementType: elementType) - let elementResults = elementFragment.printCode([], scope, printer, cleanupCode) + try printer.indent { + let elementFragment = try stackLiftFragment(elementType: elementType) + let elementResults = try elementFragment.printCode([], scope, printer, cleanupCode) if let elementExpr = elementResults.first { printer.write("\(resultVar).push(\(elementExpr));") } @@ -2802,11 +2809,11 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(lenVar) = \(scope.popI32());") printer.write("const \(resultVar) = {};") printer.write("for (let \(iVar) = 0; \(iVar) < \(lenVar); \(iVar)++) {") - printer.indent { - let valueFragment = try! stackLiftFragment(elementType: valueType) - let valueResults = valueFragment.printCode([], scope, printer, cleanupCode) - let keyFragment = try! stackLiftFragment(elementType: .string) - let keyResults = keyFragment.printCode([], scope, printer, cleanupCode) + try printer.indent { + let valueFragment = try stackLiftFragment(elementType: valueType) + let valueResults = try valueFragment.printCode([], scope, printer, cleanupCode) + let keyFragment = try stackLiftFragment(elementType: .string) + let keyResults = try keyFragment.printCode([], scope, printer, cleanupCode) if let keyExpr = keyResults.first, let valueExpr = valueResults.first { printer.write("\(resultVar)[\(keyExpr)] = \(valueExpr);") } @@ -2978,9 +2985,9 @@ struct IntrinsicJSFragment: Sendable { } ) case .array(let innerElementType): - return try! arrayLift(elementType: innerElementType) + return try arrayLift(elementType: innerElementType) case .dictionary(let valueType): - return try! dictionaryLift(valueType: valueType) + return try dictionaryLift(valueType: valueType) case .nullable(let wrappedType, let kind): return try optionalElementRaiseFragment(wrappedType: wrappedType, kind: kind) case .unsafePointer: @@ -3004,7 +3011,7 @@ struct IntrinsicJSFragment: Sendable { parameters: ["value"], printCode: { arguments, scope, printer, cleanup in registerJSValueHelpers(scope: scope) - let lowered = jsValueLower.printCode([arguments[0]], scope, printer, cleanup) + let lowered = try jsValueLower.printCode([arguments[0]], scope, printer, cleanup) let kindVar = lowered[0] let payload1Var = lowered[1] let payload2Var = lowered[2] @@ -3165,9 +3172,9 @@ struct IntrinsicJSFragment: Sendable { } ) case .array(let innerElementType): - return try! arrayLower(elementType: innerElementType) + return try arrayLower(elementType: innerElementType) case .dictionary(let valueType): - return try! dictionaryLower(valueType: valueType) + return try dictionaryLower(valueType: valueType) case .nullable(let wrappedType, let kind): return try optionalElementLowerFragment( wrappedType: wrappedType, @@ -3216,9 +3223,9 @@ struct IntrinsicJSFragment: Sendable { printer.write("\(resultVar) = \(absenceLiteral);") } printer.write("} else {") - printer.indent { - let innerFragment = try! stackLiftFragment(elementType: wrappedType) - let innerResults = innerFragment.printCode([], scope, printer, cleanup) + try printer.indent { + let innerFragment = try stackLiftFragment(elementType: wrappedType) + let innerResults = try innerFragment.printCode([], scope, printer, cleanup) if let innerResult = innerResults.first { printer.write("\(resultVar) = \(innerResult);") } else { @@ -3247,9 +3254,9 @@ struct IntrinsicJSFragment: Sendable { // Cleanup is written inside the if block so retained id is in scope let localCleanupWriter = CodeFragmentPrinter() printer.write("if (\(isSomeVar)) {") - printer.indent { - let innerFragment = try! stackLowerFragment(elementType: wrappedType) - let _ = innerFragment.printCode([value], scope, printer, localCleanupWriter) + try printer.indent { + let innerFragment = try stackLowerFragment(elementType: wrappedType) + let _ = try innerFragment.printCode([value], scope, printer, localCleanupWriter) let localCleanupLines = localCleanupWriter.lines.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty } @@ -3302,8 +3309,8 @@ struct IntrinsicJSFragment: Sendable { printer.indent() printer.write("lower: (value) => {") - printer.indent { - generateStructLowerCode( + try printer.indent { + try generateStructLowerCode( structDef: capturedStructDef, allStructs: capturedAllStructs, scope: scope, @@ -3315,8 +3322,8 @@ struct IntrinsicJSFragment: Sendable { printer.write( "lift: () => {" ) - printer.indent { - generateStructLiftCode( + try printer.indent { + try generateStructLiftCode( structDef: capturedStructDef, allStructs: capturedAllStructs, scope: scope, @@ -3344,7 +3351,7 @@ struct IntrinsicJSFragment: Sendable { allStructs: [ExportedStruct], scope: JSGlueVariableScope, printer: CodeFragmentPrinter - ) { + ) throws { let lowerPrinter = CodeFragmentPrinter() let lowerScope = scope.makeChildScope() let lowerCleanup = CodeFragmentPrinter() @@ -3352,9 +3359,9 @@ struct IntrinsicJSFragment: Sendable { let instanceProps = structDef.properties.filter { !$0.isStatic } for property in instanceProps { - let fragment = structFieldLowerFragment(field: property, allStructs: allStructs) + let fragment = try structFieldLowerFragment(field: property, allStructs: allStructs) let fieldValue = "value.\(property.name)" - _ = fragment.printCode([fieldValue], lowerScope, lowerPrinter, lowerCleanup) + _ = try fragment.printCode([fieldValue], lowerScope, lowerPrinter, lowerCleanup) } for line in lowerPrinter.lines { @@ -3377,7 +3384,7 @@ struct IntrinsicJSFragment: Sendable { scope: JSGlueVariableScope, printer: CodeFragmentPrinter, attachMethods: Bool = false - ) { + ) throws { let liftScope = scope.makeChildScope() let liftCleanup = CodeFragmentPrinter() @@ -3385,8 +3392,8 @@ struct IntrinsicJSFragment: Sendable { let instanceProps = structDef.properties.filter { !$0.isStatic } for property in instanceProps.reversed() { - let fragment = structFieldLiftFragment(field: property, allStructs: allStructs) - let results = fragment.printCode([], liftScope, printer, liftCleanup) + let fragment = try structFieldLiftFragment(field: property, allStructs: allStructs) + let results = try fragment.printCode([], liftScope, printer, liftCleanup) if let resultExpr = results.first { fieldExpressions.append((property.name, resultExpr)) @@ -3411,7 +3418,7 @@ struct IntrinsicJSFragment: Sendable { printer.write( "\(instanceVar).\(method.name) = function(\(paramList)) {" ) - printer.indent { + try printer.indent { let methodScope = scope.makeChildScope() let methodCleanup = CodeFragmentPrinter() @@ -3422,8 +3429,8 @@ struct IntrinsicJSFragment: Sendable { var paramForwardings: [String] = [] for param in method.parameters { - let fragment = try! IntrinsicJSFragment.lowerParameter(type: param.type) - let loweredValues = fragment.printCode([param.name], methodScope, printer, methodCleanup) + let fragment = try IntrinsicJSFragment.lowerParameter(type: param.type) + let loweredValues = try fragment.printCode([param.name], methodScope, printer, methodCleanup) paramForwardings.append(contentsOf: loweredValues) } @@ -3440,9 +3447,9 @@ struct IntrinsicJSFragment: Sendable { // Lift return value if needed if method.returnType != .void { - let liftFragment = try! IntrinsicJSFragment.liftReturn(type: method.returnType) + let liftFragment = try IntrinsicJSFragment.liftReturn(type: method.returnType) let liftArgs = liftFragment.parameters.isEmpty ? [] : ["ret"] - let lifted = liftFragment.printCode(liftArgs, methodScope, printer, methodCleanup) + let lifted = try liftFragment.printCode(liftArgs, methodScope, printer, methodCleanup) if let liftedValue = lifted.first { printer.write("return \(liftedValue);") } @@ -3460,7 +3467,7 @@ struct IntrinsicJSFragment: Sendable { private static func structFieldLowerFragment( field: ExportedProperty, allStructs: [ExportedStruct] - ) -> IntrinsicJSFragment { + ) throws -> IntrinsicJSFragment { switch field.type { case .jsValue: preconditionFailure("Struct field of JSValue is not supported yet") @@ -3724,7 +3731,7 @@ struct IntrinsicJSFragment: Sendable { scope.emitPushI32Parameter("\(isSomeVar) ? 1 : 0", printer: printer) cleanup.write("if (\(enumCleanupVar)) { \(enumCleanupVar)(); }") default: - let wrappedFragment = structFieldLowerFragment( + let wrappedFragment = try structFieldLowerFragment( field: ExportedProperty( name: field.name, type: wrappedType, @@ -3735,7 +3742,7 @@ struct IntrinsicJSFragment: Sendable { ) let guardedPrinter = CodeFragmentPrinter() let guardedCleanup = CodeFragmentPrinter() - _ = wrappedFragment.printCode([value], scope, guardedPrinter, guardedCleanup) + _ = try wrappedFragment.printCode([value], scope, guardedPrinter, guardedCleanup) var loweredLines = guardedPrinter.lines var hoistedCleanupVar: String? if let first = loweredLines.first { @@ -3794,7 +3801,7 @@ struct IntrinsicJSFragment: Sendable { } ) default: - return try! stackLowerFragment(elementType: field.type) + return try stackLowerFragment(elementType: field.type) } } @@ -3846,7 +3853,7 @@ struct IntrinsicJSFragment: Sendable { private static func structFieldLiftFragment( field: ExportedProperty, allStructs: [ExportedStruct] - ) -> IntrinsicJSFragment { + ) throws -> IntrinsicJSFragment { switch field.type { case .jsValue: preconditionFailure("Struct field of JSValue is not supported yet") @@ -3859,7 +3866,7 @@ struct IntrinsicJSFragment: Sendable { printer.write("const \(isSomeVar) = \(scope.popI32());") printer.write("let \(optVar);") printer.write("if (\(isSomeVar)) {") - printer.indent { + try printer.indent { // Special handling for associated value enum - in struct fields, case ID is pushed to i32Stack if case .associatedValueEnum(let enumName) = wrappedType { let base = enumName.components(separatedBy: ".").last ?? enumName @@ -3869,7 +3876,7 @@ struct IntrinsicJSFragment: Sendable { "\(optVar) = \(JSGlueVariableScope.reservedEnumHelpers).\(base).lift(\(caseIdVar), );" ) } else { - let wrappedFragment = structFieldLiftFragment( + let wrappedFragment = try structFieldLiftFragment( field: ExportedProperty( name: field.name, type: wrappedType, @@ -3878,7 +3885,7 @@ struct IntrinsicJSFragment: Sendable { ), allStructs: allStructs ) - let wrappedResults = wrappedFragment.printCode([], scope, printer, cleanup) + let wrappedResults = try wrappedFragment.printCode([], scope, printer, cleanup) if let wrappedResult = wrappedResults.first { printer.write("\(optVar) = \(wrappedResult);") } else { @@ -3938,7 +3945,7 @@ struct IntrinsicJSFragment: Sendable { } ) default: - return try! stackLiftFragment(elementType: field.type) + return try stackLiftFragment(elementType: field.type) } } } From 3d9678a2331967ebb937b66d4a8324d06af9451b Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 9 Feb 2026 11:50:40 +0900 Subject: [PATCH 2/2] BridgeJS: Build fixes for profiling API --- .../BridgeJS/Sources/BridgeJSCore/Misc.swift | 110 +++++++++--------- .../Sources/BridgeJSTool/BridgeJSTool.swift | 24 +++- 2 files changed, 76 insertions(+), 58 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift index 70dae3a82..df6d0c755 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/Misc.swift @@ -1,12 +1,3 @@ -import class Foundation.FileHandle -import class Foundation.ProcessInfo -import func Foundation.open -import func Foundation.strerror -import var Foundation.errno -import var Foundation.O_WRONLY -import var Foundation.O_CREAT -import var Foundation.O_TRUNC - // MARK: - ProgressReporting public struct ProgressReporting { @@ -31,61 +22,66 @@ public struct ProgressReporting { // MARK: - Profiling -/// A simple time-profiler to emit `chrome://tracing` format +/// A simple time-profiler API public final class Profiling { nonisolated(unsafe) static var current: Profiling? - let startTime: ContinuousClock.Instant - let clock = ContinuousClock() - let output: @Sendable (String) -> Void - var firstEntry = true - - init(output: @Sendable @escaping (String) -> Void) { - self.startTime = ContinuousClock.now - self.output = output - } - - public static func with(body: @escaping () throws -> Void) rethrows -> Void { - guard let outputPath = ProcessInfo.processInfo.environment["BRIDGE_JS_PROFILING"] else { - return try body() + let beginEntry: (_ label: String) -> Void + let endEntry: (_ label: String) -> Void + let finalize: () -> Void + + /// Create a profiling instance that outputs Trace Event Format, which + /// can be viewed in chrome://tracing or other compatible viewers. + /// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit?usp=sharing + public static func traceEvent(output: @escaping (String) -> Void) -> Profiling { + let clock = ContinuousClock() + let startTime = clock.now + var firstEntry = true + + func formatTimestamp() -> Int { + let duration = startTime.duration(to: .now) + let (seconds, attoseconds) = duration.components + // Convert to microseconds + return Int(seconds * 1_000_000 + attoseconds / 1_000_000_000_000) } - let fd = open(outputPath, O_WRONLY | O_CREAT | O_TRUNC, 0o644) - guard fd >= 0 else { - let error = String(cString: strerror(errno)) - fatalError("Failed to open profiling output file \(outputPath): \(error)") - } - let output = FileHandle(fileDescriptor: fd, closeOnDealloc: true) - let profiling = Profiling(output: { output.write($0.data(using: .utf8) ?? Data()) }) - defer { - profiling.output("]\n") - } - Profiling.current = profiling - defer { - Profiling.current = nil - } - return try body() - } - private func formatTimestamp(instant: ContinuousClock.Instant) -> Int { - let duration = self.startTime.duration(to: instant) - let (seconds, attoseconds) = duration.components - // Convert to microseconds - return Int(seconds * 1_000_000 + attoseconds / 1_000_000_000_000) + return Profiling( + beginEntry: { label in + let entry = #"{"ph":"B","pid":1,"name":\#(JSON.serialize(label)),"ts":\#(formatTimestamp())}"# + if firstEntry { + firstEntry = false + output("[\n\(entry)") + } else { + output(",\n\(entry)") + } + }, + endEntry: { label in + output(#",\n{"ph":"E","pid":1,"name":\#(JSON.serialize(label)),"ts":\#(formatTimestamp())}"#) + }, + finalize: { + output("]\n") + } + ) } - func begin(_ label: String, _ instant: ContinuousClock.Instant) { - let entry = #"{"ph":"B","pid":1,"name":\#(JSON.serialize(label)),"ts":\#(formatTimestamp(instant: instant))}"# - if firstEntry { - firstEntry = false - output("[\n\(entry)") - } else { - output(",\n\(entry)") - } + public init( + beginEntry: @escaping (_ label: String) -> Void, + endEntry: @escaping (_ label: String) -> Void, + finalize: @escaping () -> Void + ) { + self.beginEntry = beginEntry + self.endEntry = endEntry + self.finalize = finalize } - func end(_ label: String, _ instant: ContinuousClock.Instant) { - let entry = #"{"ph":"E","pid":1,"name":\#(JSON.serialize(label)),"ts":\#(formatTimestamp(instant: instant))}"# - output(",\n\(entry)") + public static func with(_ makeCurrent: () -> Profiling?, body: @escaping () throws -> Void) rethrows -> Void { + guard let current = makeCurrent() else { + return try body() + } + defer { current.finalize() } + Profiling.current = current + defer { Profiling.current = nil } + return try body() } } @@ -94,9 +90,9 @@ public func withSpan(_ label: String, body: @escaping () throws -> T) rethrow guard let profiling = Profiling.current else { return try body() } - profiling.begin(label, profiling.clock.now) + profiling.beginEntry(label) defer { - profiling.end(label, profiling.clock.now) + profiling.endEntry(label) } return try body() } diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift index f9fa56c42..b36b3ec8c 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift @@ -1,10 +1,17 @@ @preconcurrency import func Foundation.exit @preconcurrency import func Foundation.fputs +@preconcurrency import func Foundation.open +@preconcurrency import func Foundation.strerror @preconcurrency import var Foundation.stderr +@preconcurrency import var Foundation.errno +@preconcurrency import var Foundation.O_WRONLY +@preconcurrency import var Foundation.O_CREAT +@preconcurrency import var Foundation.O_TRUNC @preconcurrency import struct Foundation.URL @preconcurrency import struct Foundation.Data @preconcurrency import struct Foundation.ObjCBool @preconcurrency import class Foundation.JSONEncoder +@preconcurrency import class Foundation.FileHandle @preconcurrency import class Foundation.FileManager @preconcurrency import class Foundation.JSONDecoder @preconcurrency import class Foundation.ProcessInfo @@ -50,7 +57,7 @@ import BridgeJSUtilities static func main() throws { do { - try Profiling.with { + try Profiling.with(Profiling.make) { try run() } } catch { @@ -318,6 +325,21 @@ private func inputSwiftFiles(targetDirectory: URL, positionalArguments: [String] return positionalArguments } +extension Profiling { + static func make() -> Profiling? { + guard let outputPath = ProcessInfo.processInfo.environment["BRIDGE_JS_PROFILING"] else { + return nil + } + let fd = open(outputPath, O_WRONLY | O_CREAT | O_TRUNC, 0o644) + guard fd >= 0 else { + let error = String(cString: strerror(errno)) + fatalError("Failed to open profiling output file \(outputPath): \(error)") + } + let output = FileHandle(fileDescriptor: fd, closeOnDealloc: true) + return Profiling.traceEvent(output: { output.write($0.data(using: .utf8) ?? Data()) }) + } +} + // MARK: - Minimal Argument Parsing struct OptionRule {