├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── actions │ └── install-swift │ │ └── action.yml ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .spi.yml ├── .swift-format ├── Benchmarks ├── Package.swift ├── README.md ├── Sources │ ├── Benchmarks.swift │ ├── Generated │ │ ├── ExportSwift.swift │ │ ├── ImportTS.swift │ │ └── JavaScript │ │ │ ├── ExportSwift.json │ │ │ └── ImportTS.json │ └── bridge.d.ts ├── package.json └── run.js ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Examples ├── ActorOnWebWorker │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ └── MyApp.swift │ ├── build.sh │ ├── index.html │ └── serve.json ├── Basic │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ └── main.swift │ ├── build.sh │ └── index.html ├── Embedded │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ └── EmbeddedApp │ │ │ ├── _thingsThatShouldNotBeNeeded.swift │ │ │ └── main.swift │ ├── build.sh │ └── index.html ├── ExportSwift │ ├── Package.swift │ ├── Sources │ │ └── main.swift │ ├── index.html │ └── index.js ├── ImportTS │ ├── Package.swift │ ├── Sources │ │ ├── bridge.d.ts │ │ └── main.swift │ ├── index.html │ └── index.js ├── Multithreading │ ├── Package.resolved │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ └── MyApp │ │ │ ├── Scene.swift │ │ │ └── main.swift │ ├── build.sh │ ├── index.html │ └── serve.json ├── OffscrenCanvas │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ └── MyApp │ │ │ ├── main.swift │ │ │ └── render.swift │ ├── build.sh │ ├── index.html │ └── serve.json └── Testing │ ├── Package.swift │ ├── README.md │ ├── Sources │ └── Counter │ │ └── Counter.swift │ ├── Tests │ └── CounterTests │ │ └── CounterTests.swift │ └── package.json ├── LICENSE ├── Makefile ├── Package.swift ├── Plugins ├── BridgeJS │ ├── Package.swift │ ├── README.md │ ├── Sources │ │ ├── BridgeJSBuildPlugin │ │ │ └── BridgeJSBuildPlugin.swift │ │ ├── BridgeJSCommandPlugin │ │ │ └── BridgeJSCommandPlugin.swift │ │ ├── BridgeJSLink │ │ │ ├── BridgeJSLink.swift │ │ │ └── BridgeJSSkeleton │ │ ├── BridgeJSSkeleton │ │ │ └── BridgeJSSkeleton.swift │ │ ├── BridgeJSTool │ │ │ ├── BridgeJSSkeleton │ │ │ ├── BridgeJSTool.swift │ │ │ ├── DiagnosticError.swift │ │ │ ├── ExportSwift.swift │ │ │ ├── ImportTS.swift │ │ │ └── TypeDeclResolver.swift │ │ └── JavaScript │ │ │ ├── README.md │ │ │ ├── bin │ │ │ └── ts2skeleton.js │ │ │ ├── package.json │ │ │ └── src │ │ │ ├── cli.js │ │ │ ├── index.d.ts │ │ │ └── processor.js │ └── Tests │ │ └── BridgeJSToolTests │ │ ├── BridgeJSLinkTests.swift │ │ ├── ExportSwiftTests.swift │ │ ├── ImportTSTests.swift │ │ ├── Inputs │ │ ├── ArrayParameter.d.ts │ │ ├── Interface.d.ts │ │ ├── PrimitiveParameters.d.ts │ │ ├── PrimitiveParameters.swift │ │ ├── PrimitiveReturn.d.ts │ │ ├── PrimitiveReturn.swift │ │ ├── StringParameter.d.ts │ │ ├── StringParameter.swift │ │ ├── StringReturn.d.ts │ │ ├── StringReturn.swift │ │ ├── SwiftClass.swift │ │ ├── TypeAlias.d.ts │ │ ├── TypeScriptClass.d.ts │ │ ├── VoidParameterVoidReturn.d.ts │ │ └── VoidParameterVoidReturn.swift │ │ ├── SnapshotTesting.swift │ │ ├── TemporaryDirectory.swift │ │ └── __Snapshots__ │ │ ├── BridgeJSLinkTests │ │ ├── ArrayParameter.Import.d.ts │ │ ├── ArrayParameter.Import.js │ │ ├── Interface.Import.d.ts │ │ ├── Interface.Import.js │ │ ├── PrimitiveParameters.Export.d.ts │ │ ├── PrimitiveParameters.Export.js │ │ ├── PrimitiveParameters.Import.d.ts │ │ ├── PrimitiveParameters.Import.js │ │ ├── PrimitiveReturn.Export.d.ts │ │ ├── PrimitiveReturn.Export.js │ │ ├── PrimitiveReturn.Import.d.ts │ │ ├── PrimitiveReturn.Import.js │ │ ├── StringParameter.Export.d.ts │ │ ├── StringParameter.Export.js │ │ ├── StringParameter.Import.d.ts │ │ ├── StringParameter.Import.js │ │ ├── StringReturn.Export.d.ts │ │ ├── StringReturn.Export.js │ │ ├── StringReturn.Import.d.ts │ │ ├── StringReturn.Import.js │ │ ├── SwiftClass.Export.d.ts │ │ ├── SwiftClass.Export.js │ │ ├── TypeAlias.Import.d.ts │ │ ├── TypeAlias.Import.js │ │ ├── TypeScriptClass.Import.d.ts │ │ ├── TypeScriptClass.Import.js │ │ ├── VoidParameterVoidReturn.Export.d.ts │ │ ├── VoidParameterVoidReturn.Export.js │ │ ├── VoidParameterVoidReturn.Import.d.ts │ │ └── VoidParameterVoidReturn.Import.js │ │ ├── ExportSwiftTests │ │ ├── PrimitiveParameters.json │ │ ├── PrimitiveParameters.swift │ │ ├── PrimitiveReturn.json │ │ ├── PrimitiveReturn.swift │ │ ├── StringParameter.json │ │ ├── StringParameter.swift │ │ ├── StringReturn.json │ │ ├── StringReturn.swift │ │ ├── SwiftClass.json │ │ ├── SwiftClass.swift │ │ ├── VoidParameterVoidReturn.json │ │ └── VoidParameterVoidReturn.swift │ │ └── ImportTSTests │ │ ├── ArrayParameter.swift │ │ ├── Interface.swift │ │ ├── PrimitiveParameters.swift │ │ ├── PrimitiveReturn.swift │ │ ├── StringParameter.swift │ │ ├── StringReturn.swift │ │ ├── TypeAlias.swift │ │ ├── TypeScriptClass.swift │ │ └── VoidParameterVoidReturn.swift └── PackageToJS │ ├── Fixtures │ └── ContinuationLeakInTest │ │ ├── SwiftTesting │ │ ├── Package.swift │ │ └── Tests │ │ │ └── CheckTests.swift │ │ └── XCTest │ │ ├── Package.swift │ │ └── Tests │ │ └── CheckTests.swift │ ├── Package.swift │ ├── README.md │ ├── Sources │ ├── BridgeJSLink │ ├── MiniMake.swift │ ├── PackageToJS.swift │ ├── PackageToJSPlugin.swift │ ├── ParseWasm.swift │ ├── Preprocess.swift │ └── TestsParser.swift │ ├── Templates │ ├── bin │ │ └── test.js │ ├── index.d.ts │ ├── index.js │ ├── instantiate.d.ts │ ├── instantiate.js │ ├── package.json │ ├── platforms │ │ ├── browser.d.ts │ │ ├── browser.js │ │ ├── browser.worker.js │ │ ├── node.d.ts │ │ └── node.js │ ├── runtime.d.ts │ ├── runtime.mjs │ ├── test.browser.html │ ├── test.d.ts │ ├── test.js │ └── tsconfig.json │ └── Tests │ ├── ExampleTests.swift │ ├── MiniMakeTests.swift │ ├── PackagingPlannerTests.swift │ ├── PreprocessTests.swift │ ├── SnapshotTesting.swift │ ├── TemplatesTests.swift │ ├── TemporaryDirectory.swift │ ├── TestsParserTests.swift │ └── __Snapshots__ │ ├── PackagingPlannerTests │ ├── planBuild_debug.json │ ├── planBuild_release.json │ ├── planBuild_release_dwarf.json │ ├── planBuild_release_name.json │ ├── planBuild_release_no_optimize.json │ └── planTestBuild.json │ └── TestsParserTests │ ├── testAllPassed.txt │ ├── testAssertFailure.txt │ ├── testCrash.txt │ ├── testSkipped.txt │ └── testThrowFailure.txt ├── README.md ├── Runtime ├── .gitignore ├── .npmignore ├── rollup.config.mjs ├── src │ ├── closure-heap.ts │ ├── find-global.ts │ ├── index.ts │ ├── itc.ts │ ├── js-value.ts │ ├── memory.ts │ ├── object-heap.ts │ └── types.ts └── tsconfig.json ├── Sources ├── JavaScriptBigIntSupport │ ├── Int64+I64.swift │ └── JSBigInt+I64.swift ├── JavaScriptEventLoop │ ├── JSSending.swift │ ├── JavaScriptEventLoop+ExecutorFactory.swift │ ├── JavaScriptEventLoop+LegacyHooks.swift │ ├── JavaScriptEventLoop.swift │ ├── JobQueue.swift │ ├── WebWorkerDedicatedExecutor.swift │ └── WebWorkerTaskExecutor.swift ├── JavaScriptEventLoopTestSupport │ └── JavaScriptEventLoopTestSupport.swift ├── JavaScriptKit │ ├── BasicObjects │ │ ├── JSArray.swift │ │ ├── JSDate.swift │ │ ├── JSError.swift │ │ ├── JSPromise.swift │ │ ├── JSTimer.swift │ │ └── JSTypedArray.swift │ ├── ConstructibleFromJSValue.swift │ ├── ConvertibleToJSValue.swift │ ├── Deprecated.swift │ ├── Documentation.docc │ │ ├── Articles │ │ │ ├── Ahead-of-Time-Code-Generation.md │ │ │ ├── Deploying-Pages.md │ │ │ ├── Exporting-Swift-to-JavaScript.md │ │ │ ├── Importing-TypeScript-into-Swift.md │ │ │ └── JavaScript-Environment-Requirements.md │ │ ├── Documentation.md │ │ └── Tutorials │ │ │ ├── Hello-World │ │ │ ├── Hello-World.tutorial │ │ │ └── Resources │ │ │ │ ├── hello-world-0-1-swift-version.txt │ │ │ │ ├── hello-world-0-2-select-sdk.txt │ │ │ │ ├── hello-world-1-1-init-package.txt │ │ │ │ ├── hello-world-1-2-add-dependency.txt │ │ │ │ ├── hello-world-1-3-add-target-dependency.txt │ │ │ │ ├── hello-world-2-1-main-swift.swift │ │ │ │ ├── hello-world-2-2-index-html.html │ │ │ │ ├── hello-world-3-1-build.txt │ │ │ │ ├── hello-world-3-2-server.txt │ │ │ │ ├── hello-world-3-3-app.png │ │ │ │ └── hello-world-3-3-open.txt │ │ │ ├── Resources │ │ │ └── image.png │ │ │ └── Table-of-Contents.tutorial │ ├── Features.swift │ ├── FundamentalObjects │ │ ├── JSBigInt.swift │ │ ├── JSClosure.swift │ │ ├── JSFunction.swift │ │ ├── JSObject.swift │ │ ├── JSString.swift │ │ ├── JSSymbol.swift │ │ └── JSThrowingFunction.swift │ ├── JSBridgedType.swift │ ├── JSException.swift │ ├── JSValue.swift │ ├── JSValueDecoder.swift │ ├── Macros.swift │ ├── Runtime │ │ ├── index.d.ts │ │ └── index.mjs │ └── ThreadLocal.swift ├── _CJavaScriptBigIntSupport │ ├── _CJavaScriptKit+I64.c │ └── include │ │ ├── _CJavaScriptKit+I64.h │ │ └── module.modulemap ├── _CJavaScriptEventLoop │ ├── _CJavaScriptEventLoop.c │ └── include │ │ ├── _CJavaScriptEventLoop.h │ │ └── module.modulemap ├── _CJavaScriptEventLoopTestSupport │ ├── _CJavaScriptEventLoopTestSupport.c │ └── include │ │ └── dummy.h └── _CJavaScriptKit │ ├── _CJavaScriptKit.c │ └── include │ ├── _CJavaScriptKit.h │ └── module.modulemap ├── Tests ├── BridgeJSRuntimeTests │ ├── ExportAPITests.swift │ └── Generated │ │ ├── ExportSwift.swift │ │ └── JavaScript │ │ └── ExportSwift.json ├── JavaScriptBigIntSupportTests │ └── JavaScriptBigIntSupportTests.swift ├── JavaScriptEventLoopTestSupportTests │ └── JavaScriptEventLoopTestSupportTests.swift ├── JavaScriptEventLoopTests │ ├── JSPromiseTests.swift │ ├── JSTimerTests.swift │ ├── JavaScriptEventLoopTests.swift │ ├── WebWorkerDedicatedExecutorTests.swift │ └── WebWorkerTaskExecutorTests.swift ├── JavaScriptKitTests │ ├── JSObjectTests.swift │ ├── JSStringTests.swift │ ├── JSTypedArrayTests.swift │ ├── JavaScriptKitTests.swift │ └── ThreadLocalTests.swift └── prelude.mjs ├── Utilities └── format.swift ├── package-lock.json └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | Sources/JavaScriptKit/Runtime/** linguist-generated 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [swiftwasm] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: swiftwasm # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/actions/install-swift/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Install Swift toolchain' 2 | description: 'Install Swift toolchain tarball from URL' 3 | inputs: 4 | download-url: 5 | description: 'URL to download Swift toolchain tarball' 6 | required: true 7 | 8 | runs: 9 | using: composite 10 | steps: 11 | # https://www.swift.org/install/linux/#installation-via-tarball 12 | - name: Install dependent packages for Swift 13 | shell: bash 14 | run: > 15 | sudo apt-get -q update && 16 | sudo apt-get install -y 17 | binutils 18 | git 19 | gnupg2 20 | libc6-dev 21 | libcurl4-openssl-dev 22 | libedit2 23 | libgcc-9-dev 24 | libpython3.8 25 | libsqlite3-0 26 | libstdc++-9-dev 27 | libxml2-dev 28 | libz3-dev 29 | pkg-config 30 | tzdata 31 | unzip 32 | zlib1g-dev 33 | curl 34 | 35 | - name: Install Swift 36 | shell: bash 37 | run: curl -fL ${{ inputs.download-url }} | sudo tar xfz - --strip-components=2 -C /usr/local 38 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: 'github-actions' 9 | directory: '/' 10 | schedule: 11 | interval: 'weekly' 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .DS_Store 4 | .build 5 | /Packages 6 | /*.xcodeproj 7 | xcuserdata/ 8 | .swiftpm 9 | .vscode 10 | Examples/*/Bundle 11 | Examples/*/package-lock.json 12 | Package.resolved 13 | Plugins/BridgeJS/Sources/JavaScript/package-lock.json 14 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .build 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": ["tsconfig.json", "*.js", "*.ts"], 5 | "options": { 6 | "tabWidth": 4 7 | } 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | builder: 3 | configs: 4 | - documentation_targets: 5 | - JavaScriptKit 6 | - JavaScriptEventLoop 7 | - JavaScriptBigIntSupport 8 | -------------------------------------------------------------------------------- /.swift-format: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "lineLength": 120, 4 | "indentation": { 5 | "spaces": 4 6 | }, 7 | "lineBreakBeforeEachArgument": true, 8 | "indentConditionalCompilationBlocks": false, 9 | "prioritizeKeepingFunctionOutputTogether": true, 10 | "rules": { 11 | "AlwaysUseLowerCamelCase": false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Benchmarks/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Benchmarks", 7 | dependencies: [ 8 | .package(path: "../") 9 | ], 10 | targets: [ 11 | .executableTarget( 12 | name: "Benchmarks", 13 | dependencies: ["JavaScriptKit"], 14 | exclude: ["Generated/JavaScript", "bridge.d.ts"], 15 | swiftSettings: [ 16 | .enableExperimentalFeature("Extern") 17 | ] 18 | ) 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /Benchmarks/README.md: -------------------------------------------------------------------------------- 1 | # JavaScriptKit Benchmarks 2 | 3 | This directory contains performance benchmarks for JavaScriptKit. 4 | 5 | ## Building Benchmarks 6 | 7 | Before running the benchmarks, you need to build the test suite: 8 | 9 | ```bash 10 | JAVASCRIPTKIT_EXPERIMENTAL_BRIDGEJS=1 swift package --swift-sdk $SWIFT_SDK_ID js -c release 11 | ``` 12 | 13 | ## Running Benchmarks 14 | 15 | ```bash 16 | # Run with default settings 17 | node run.js 18 | 19 | # Save results to a JSON file 20 | node run.js --output=results.json 21 | 22 | # Specify number of iterations 23 | node run.js --runs=20 24 | 25 | # Run in adaptive mode until results stabilize 26 | node run.js --adaptive --output=stable-results.json 27 | 28 | # Run benchmarks and compare with previous results 29 | node run.js --baseline=previous-results.json 30 | ``` 31 | -------------------------------------------------------------------------------- /Benchmarks/Sources/Benchmarks.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | 3 | class Benchmark { 4 | init(_ title: String) { 5 | self.title = title 6 | } 7 | 8 | let title: String 9 | 10 | func testSuite(_ name: String, _ body: @escaping () -> Void) { 11 | let jsBody = JSClosure { arguments -> JSValue in 12 | body() 13 | return .undefined 14 | } 15 | benchmarkRunner("\(title)/\(name)", jsBody) 16 | } 17 | } 18 | 19 | @JS func run() { 20 | 21 | let call = Benchmark("Call") 22 | 23 | call.testSuite("JavaScript function call through Wasm import") { 24 | for _ in 0..<20_000_000 { 25 | benchmarkHelperNoop() 26 | } 27 | } 28 | 29 | call.testSuite("JavaScript function call through Wasm import with int") { 30 | for _ in 0..<10_000_000 { 31 | benchmarkHelperNoopWithNumber(42) 32 | } 33 | } 34 | 35 | let propertyAccess = Benchmark("Property access") 36 | 37 | do { 38 | let swiftInt: Double = 42 39 | let object = JSObject() 40 | object.jsNumber = JSValue.number(swiftInt) 41 | propertyAccess.testSuite("Write Number") { 42 | for _ in 0..<1_000_000 { 43 | object.jsNumber = JSValue.number(swiftInt) 44 | } 45 | } 46 | } 47 | 48 | do { 49 | let object = JSObject() 50 | object.jsNumber = JSValue.number(42) 51 | propertyAccess.testSuite("Read Number") { 52 | for _ in 0..<1_000_000 { 53 | _ = object.jsNumber.number 54 | } 55 | } 56 | } 57 | 58 | do { 59 | let swiftString = "Hello, world" 60 | let object = JSObject() 61 | object.jsString = swiftString.jsValue 62 | propertyAccess.testSuite("Write String") { 63 | for _ in 0..<1_000_000 { 64 | object.jsString = swiftString.jsValue 65 | } 66 | } 67 | } 68 | 69 | do { 70 | let object = JSObject() 71 | object.jsString = JSValue.string("Hello, world") 72 | propertyAccess.testSuite("Read String") { 73 | for _ in 0..<1_000_000 { 74 | _ = object.jsString.string 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Benchmarks/Sources/Generated/ExportSwift.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | @_extern(wasm, module: "bjs", name: "return_string") 7 | private func _return_string(_ ptr: UnsafePointer?, _ len: Int32) 8 | @_extern(wasm, module: "bjs", name: "init_memory") 9 | private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?) 10 | 11 | @_expose(wasm, "bjs_main") 12 | @_cdecl("bjs_main") 13 | public func _bjs_main() -> Void { 14 | main() 15 | } -------------------------------------------------------------------------------- /Benchmarks/Sources/Generated/ImportTS.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func benchmarkHelperNoop() -> Void { 19 | @_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkHelperNoop") 20 | func bjs_benchmarkHelperNoop() -> Void 21 | bjs_benchmarkHelperNoop() 22 | } 23 | 24 | func benchmarkHelperNoopWithNumber(_ n: Double) -> Void { 25 | @_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkHelperNoopWithNumber") 26 | func bjs_benchmarkHelperNoopWithNumber(_ n: Float64) -> Void 27 | bjs_benchmarkHelperNoopWithNumber(n) 28 | } 29 | 30 | func benchmarkRunner(_ name: String, _ body: JSObject) -> Void { 31 | @_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkRunner") 32 | func bjs_benchmarkRunner(_ name: Int32, _ body: Int32) -> Void 33 | var name = name 34 | let nameId = name.withUTF8 { b in 35 | _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) 36 | } 37 | bjs_benchmarkRunner(nameId, Int32(bitPattern: body.id)) 38 | } -------------------------------------------------------------------------------- /Benchmarks/Sources/Generated/JavaScript/ExportSwift.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes" : [ 3 | 4 | ], 5 | "functions" : [ 6 | { 7 | "abiName" : "bjs_main", 8 | "name" : "main", 9 | "parameters" : [ 10 | 11 | ], 12 | "returnType" : { 13 | "void" : { 14 | 15 | } 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /Benchmarks/Sources/Generated/JavaScript/ImportTS.json: -------------------------------------------------------------------------------- 1 | { 2 | "children" : [ 3 | { 4 | "functions" : [ 5 | { 6 | "name" : "benchmarkHelperNoop", 7 | "parameters" : [ 8 | 9 | ], 10 | "returnType" : { 11 | "void" : { 12 | 13 | } 14 | } 15 | }, 16 | { 17 | "name" : "benchmarkHelperNoopWithNumber", 18 | "parameters" : [ 19 | { 20 | "name" : "n", 21 | "type" : { 22 | "double" : { 23 | 24 | } 25 | } 26 | } 27 | ], 28 | "returnType" : { 29 | "void" : { 30 | 31 | } 32 | } 33 | }, 34 | { 35 | "name" : "benchmarkRunner", 36 | "parameters" : [ 37 | { 38 | "name" : "name", 39 | "type" : { 40 | "string" : { 41 | 42 | } 43 | } 44 | }, 45 | { 46 | "name" : "body", 47 | "type" : { 48 | "jsObject" : { 49 | 50 | } 51 | } 52 | } 53 | ], 54 | "returnType" : { 55 | "void" : { 56 | 57 | } 58 | } 59 | } 60 | ], 61 | "types" : [ 62 | 63 | ] 64 | } 65 | ], 66 | "moduleName" : "Benchmarks" 67 | } -------------------------------------------------------------------------------- /Benchmarks/Sources/bridge.d.ts: -------------------------------------------------------------------------------- 1 | declare function benchmarkHelperNoop(): void; 2 | declare function benchmarkHelperNoopWithNumber(n: number): void; 3 | declare function benchmarkRunner(name: string, body: (n: number) => void): void; 4 | -------------------------------------------------------------------------------- /Benchmarks/package.json: -------------------------------------------------------------------------------- 1 | { "type": "module" } 2 | -------------------------------------------------------------------------------- /Examples/ActorOnWebWorker/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Example", 7 | platforms: [.macOS("15"), .iOS("18"), .watchOS("11"), .tvOS("18"), .visionOS("2")], 8 | dependencies: [ 9 | .package(path: "../../") 10 | ], 11 | targets: [ 12 | .executableTarget( 13 | name: "MyApp", 14 | dependencies: [ 15 | .product(name: "JavaScriptKit", package: "JavaScriptKit"), 16 | .product(name: "JavaScriptEventLoop", package: "JavaScriptKit"), 17 | ] 18 | ) 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /Examples/ActorOnWebWorker/README.md: -------------------------------------------------------------------------------- 1 | # WebWorker + Actor example 2 | 3 | Install Development Snapshot toolchain `DEVELOPMENT-SNAPSHOT-2024-07-08-a` or later from [swift.org/install](https://www.swift.org/install/) and run the following commands: 4 | 5 | ```sh 6 | $ ( 7 | set -eo pipefail; \ 8 | V="$(swiftc --version | head -n1)"; \ 9 | TAG="$(curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/tag-by-version.json" | jq -e -r --arg v "$V" '.[$v] | .[-1]')"; \ 10 | curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/builds/$TAG.json" | \ 11 | jq -r '.["swift-sdks"]["wasm32-unknown-wasip1-threads"] | "swift sdk install \"\(.url)\" --checksum \"\(.checksum)\""' | sh -x 12 | ) 13 | $ export SWIFT_SDK_ID=$( 14 | V="$(swiftc --version | head -n1)"; \ 15 | TAG="$(curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/tag-by-version.json" | jq -e -r --arg v "$V" '.[$v] | .[-1]')"; \ 16 | curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/builds/$TAG.json" | \ 17 | jq -r '.["swift-sdks"]["wasm32-unknown-wasip1-threads"]["id"]' 18 | ) 19 | $ ./build.sh 20 | $ npx serve 21 | ``` 22 | -------------------------------------------------------------------------------- /Examples/ActorOnWebWorker/build.sh: -------------------------------------------------------------------------------- 1 | swift package --swift-sdk "${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}" -c release \ 2 | plugin --allow-writing-to-package-directory \ 3 | js --use-cdn --output ./Bundle 4 | -------------------------------------------------------------------------------- /Examples/ActorOnWebWorker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebWorker + Actor example 6 | 7 | 8 | 9 | 13 |

Full-text Search with Actor on Web Worker

14 | 15 |
16 | 20 | 21 |
22 |
23 | 25 | 26 |

Ready

27 |
28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Examples/ActorOnWebWorker/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": [{ 3 | "source": "**/*", 4 | "headers": [ 5 | { 6 | "key": "Cross-Origin-Embedder-Policy", 7 | "value": "require-corp" 8 | }, { 9 | "key": "Cross-Origin-Opener-Policy", 10 | "value": "same-origin" 11 | } 12 | ] 13 | }] 14 | } 15 | -------------------------------------------------------------------------------- /Examples/Basic/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Basic", 7 | platforms: [ 8 | .macOS(.v14) 9 | ], 10 | dependencies: [.package(name: "JavaScriptKit", path: "../../")], 11 | targets: [ 12 | .executableTarget( 13 | name: "Basic", 14 | dependencies: [ 15 | "JavaScriptKit", 16 | .product(name: "JavaScriptEventLoop", package: "JavaScriptKit"), 17 | ] 18 | ) 19 | ], 20 | swiftLanguageModes: [.v5] 21 | ) 22 | -------------------------------------------------------------------------------- /Examples/Basic/README.md: -------------------------------------------------------------------------------- 1 | # Basic example 2 | 3 | Install Development Snapshot toolchain `DEVELOPMENT-SNAPSHOT-2024-07-08-a` from [swift.org/install](https://www.swift.org/install/) and run the following commands: 4 | 5 | ```sh 6 | $ swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle.zip 7 | $ ./build.sh 8 | $ npx serve 9 | ``` 10 | -------------------------------------------------------------------------------- /Examples/Basic/Sources/main.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptEventLoop 2 | import JavaScriptKit 3 | 4 | let alert = JSObject.global.alert.function! 5 | let document = JSObject.global.document 6 | 7 | let divElement = document.createElement("div") 8 | divElement.innerText = "Hello, world" 9 | _ = document.body.appendChild(divElement) 10 | 11 | let buttonElement = document.createElement("button") 12 | buttonElement.innerText = "Alert demo" 13 | buttonElement.onclick = .object( 14 | JSClosure { _ in 15 | alert("Swift is running on browser!") 16 | return .undefined 17 | } 18 | ) 19 | 20 | _ = document.body.appendChild(buttonElement) 21 | 22 | private let jsFetch = JSObject.global.fetch.function! 23 | func fetch(_ url: String) -> JSPromise { 24 | JSPromise(jsFetch(url).object!)! 25 | } 26 | 27 | JavaScriptEventLoop.installGlobalExecutor() 28 | 29 | struct Response: Decodable { 30 | let uuid: String 31 | } 32 | 33 | let asyncButtonElement = document.createElement("button") 34 | asyncButtonElement.innerText = "Fetch UUID demo" 35 | asyncButtonElement.onclick = .object( 36 | JSClosure { _ in 37 | Task { 38 | do { 39 | let response = try await fetch("https://httpbin.org/uuid").value 40 | let json = try await JSPromise(response.json().object!)!.value 41 | let parsedResponse = try JSValueDecoder().decode(Response.self, from: json) 42 | alert(parsedResponse.uuid) 43 | } catch { 44 | print(error) 45 | } 46 | } 47 | 48 | return .undefined 49 | } 50 | ) 51 | 52 | _ = document.body.appendChild(asyncButtonElement) 53 | -------------------------------------------------------------------------------- /Examples/Basic/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | swift package --swift-sdk "${SWIFT_SDK_ID:-wasm32-unknown-wasi}" -c "${1:-debug}" js --use-cdn 4 | -------------------------------------------------------------------------------- /Examples/Basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Getting Started 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Examples/Embedded/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Embedded", 7 | dependencies: [ 8 | .package(name: "JavaScriptKit", path: "../../"), 9 | .package(url: "https://github.com/swiftwasm/swift-dlmalloc", branch: "0.1.0"), 10 | ], 11 | targets: [ 12 | .executableTarget( 13 | name: "EmbeddedApp", 14 | dependencies: [ 15 | "JavaScriptKit", 16 | .product(name: "dlmalloc", package: "swift-dlmalloc"), 17 | ], 18 | cSettings: [.unsafeFlags(["-fdeclspec"])], 19 | swiftSettings: [ 20 | .enableExperimentalFeature("Embedded"), 21 | .enableExperimentalFeature("Extern"), 22 | .unsafeFlags([ 23 | "-Xfrontend", "-gnone", 24 | "-Xfrontend", "-disable-stack-protector", 25 | ]), 26 | ], 27 | linkerSettings: [ 28 | .unsafeFlags([ 29 | "-Xclang-linker", "-nostdlib", 30 | "-Xlinker", "--no-entry", 31 | "-Xlinker", "--export-if-defined=__main_argc_argv", 32 | ]) 33 | ] 34 | ) 35 | ], 36 | swiftLanguageModes: [.v5] 37 | ) 38 | -------------------------------------------------------------------------------- /Examples/Embedded/README.md: -------------------------------------------------------------------------------- 1 | # Embedded example 2 | 3 | Requires a recent DEVELOPMENT-SNAPSHOT toolchain. (tested with swift-6.1-DEVELOPMENT-SNAPSHOT-2025-02-21-a) 4 | 5 | ```sh 6 | $ ./build.sh 7 | $ npx serve 8 | ``` 9 | -------------------------------------------------------------------------------- /Examples/Embedded/Sources/EmbeddedApp/_thingsThatShouldNotBeNeeded.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | 3 | // NOTE: it seems the embedded tree shaker gets rid of these exports if they are not used somewhere 4 | func _i_need_to_be_here_for_wasm_exports_to_work() { 5 | _ = _swjs_library_features 6 | _ = _swjs_call_host_function 7 | _ = _swjs_free_host_function 8 | } 9 | 10 | // TODO: why do I need this? and surely this is not ideal... figure this out, or at least have this come from a C lib 11 | @_cdecl("strlen") 12 | func strlen(_ s: UnsafePointer) -> Int { 13 | var p = s 14 | while p.pointee != 0 { 15 | p += 1 16 | } 17 | return p - s 18 | } 19 | 20 | enum LCG { 21 | static var x: UInt8 = 0 22 | static let a: UInt8 = 0x05 23 | static let c: UInt8 = 0x0b 24 | 25 | static func next() -> UInt8 { 26 | x = a &* x &+ c 27 | return x 28 | } 29 | } 30 | 31 | @_cdecl("arc4random_buf") 32 | public func arc4random_buf(_ buffer: UnsafeMutableRawPointer, _ size: Int) { 33 | for i in 0..( 34 | unsafelyWrapping: encode(this: textEncoder, textInputElement.value).object! 35 | ) 36 | encodeResultElement.innerText = .string( 37 | encodedData.withUnsafeBytes { bytes in 38 | bytes.map { hex($0) }.joined(separator: " ") 39 | } 40 | ) 41 | return .undefined 42 | } 43 | ) 44 | let encoderContainer = document.createElement("div") 45 | _ = encoderContainer.appendChild(textInputElement) 46 | _ = encoderContainer.appendChild(encodeResultElement) 47 | _ = document.body.appendChild(encoderContainer) 48 | 49 | func print(_ message: String) { 50 | _ = JSObject.global.console.log(message) 51 | } 52 | 53 | func hex(_ value: UInt8) -> String { 54 | var result = "0x" 55 | let hexChars: [Character] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"] 56 | result.append(hexChars[Int(value / 16)]) 57 | result.append(hexChars[Int(value % 16)]) 58 | return result 59 | } 60 | -------------------------------------------------------------------------------- /Examples/Embedded/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | package_dir="$(cd "$(dirname "$0")" && pwd)" 3 | JAVASCRIPTKIT_EXPERIMENTAL_EMBEDDED_WASM=true \ 4 | swift package --package-path "$package_dir" \ 5 | -c release --triple wasm32-unknown-none-wasm js 6 | -------------------------------------------------------------------------------- /Examples/Embedded/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Getting Started 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Examples/ExportSwift/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "MyApp", 7 | platforms: [ 8 | .macOS(.v14) 9 | ], 10 | dependencies: [.package(name: "JavaScriptKit", path: "../../")], 11 | targets: [ 12 | .executableTarget( 13 | name: "MyApp", 14 | dependencies: [ 15 | "JavaScriptKit" 16 | ], 17 | swiftSettings: [ 18 | .enableExperimentalFeature("Extern") 19 | ], 20 | plugins: [ 21 | .plugin(name: "BridgeJS", package: "JavaScriptKit") 22 | ] 23 | ) 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /Examples/ExportSwift/Sources/main.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | 3 | // Mark functions you want to export to JavaScript with the @JS attribute 4 | // This function will be available as `renderCircleSVG(size)` in JavaScript 5 | @JS public func renderCircleSVG(size: Int) -> String { 6 | let strokeWidth = 3 7 | let strokeColor = "black" 8 | let fillColor = "red" 9 | let cx = size / 2 10 | let cy = size / 2 11 | let r = (size / 2) - strokeWidth 12 | var svg = "" 13 | svg += 14 | "" 15 | svg += "" 16 | return svg 17 | } 18 | 19 | // Classes can also be exported using the @JS attribute 20 | // This class will be available as a constructor in JavaScript: new Greeter("name") 21 | @JS class Greeter { 22 | var name: String 23 | 24 | // Use @JS for initializers you want to expose 25 | @JS init(name: String) { 26 | self.name = name 27 | } 28 | 29 | // Methods need the @JS attribute to be accessible from JavaScript 30 | // This method will be available as greeter.greet() in JavaScript 31 | @JS public func greet() -> String { 32 | "Hello, \(name)!" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Examples/ExportSwift/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Getting Started 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Examples/ExportSwift/index.js: -------------------------------------------------------------------------------- 1 | import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js"; 2 | const { exports } = await init({}); 3 | 4 | const Greeter = exports.Greeter; 5 | const greeter = new Greeter("World"); 6 | const circle = exports.renderCircleSVG(100); 7 | 8 | // Display the results 9 | const textOutput = document.createElement("div"); 10 | textOutput.innerText = greeter.greet() 11 | document.body.appendChild(textOutput); 12 | const circleOutput = document.createElement("div"); 13 | circleOutput.innerHTML = circle; 14 | document.body.appendChild(circleOutput); 15 | -------------------------------------------------------------------------------- /Examples/ImportTS/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "MyApp", 7 | platforms: [ 8 | .macOS(.v10_15), 9 | .iOS(.v13), 10 | .tvOS(.v13), 11 | .watchOS(.v6), 12 | .macCatalyst(.v13), 13 | ], 14 | dependencies: [.package(name: "JavaScriptKit", path: "../../")], 15 | targets: [ 16 | .executableTarget( 17 | name: "MyApp", 18 | dependencies: [ 19 | "JavaScriptKit" 20 | ], 21 | swiftSettings: [ 22 | .enableExperimentalFeature("Extern") 23 | ], 24 | plugins: [ 25 | .plugin(name: "BridgeJS", package: "JavaScriptKit") 26 | ] 27 | ) 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /Examples/ImportTS/Sources/bridge.d.ts: -------------------------------------------------------------------------------- 1 | // Function definition to expose console.log to Swift 2 | // Will be imported as a Swift function: consoleLog(message: String) 3 | export function consoleLog(message: string): void 4 | 5 | // TypeScript interface types are converted to Swift structs 6 | // This defines a subset of the browser's HTMLElement interface 7 | type HTMLElement = Pick & { 8 | // Methods with object parameters are properly handled 9 | appendChild(child: HTMLElement): void 10 | } 11 | 12 | // TypeScript object type with read-only properties 13 | // Properties will become Swift properties with appropriate access level 14 | type Document = { 15 | // Regular property - will be read/write in Swift 16 | title: string 17 | // Read-only property - will be read-only in Swift 18 | readonly body: HTMLElement 19 | // Method returning an object - will become a Swift method returning an HTMLElement 20 | createElement(tagName: string): HTMLElement 21 | } 22 | // Function returning a complex object 23 | // Will be imported as a Swift function: getDocument() -> Document 24 | export function getDocument(): Document 25 | -------------------------------------------------------------------------------- /Examples/ImportTS/Sources/main.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | 3 | // This function is automatically generated by the @JS plugin 4 | // It demonstrates how to use TypeScript functions and types imported from bridge.d.ts 5 | @JS public func run() { 6 | // Call the imported consoleLog function defined in bridge.d.ts 7 | consoleLog("Hello, World!") 8 | 9 | // Get the document object - this comes from the imported getDocument() function 10 | let document = getDocument() 11 | 12 | // Access and modify properties - the title property is read/write 13 | document.title = "Hello, World!" 14 | 15 | // Access read-only properties - body is defined as readonly in TypeScript 16 | let body = document.body 17 | 18 | // Create a new element using the document.createElement method 19 | let h1 = document.createElement("h1") 20 | 21 | // Set properties on the created element 22 | h1.innerText = "Hello, World!" 23 | 24 | // Call methods on objects - appendChild is defined in the HTMLElement interface 25 | body.appendChild(h1) 26 | } 27 | -------------------------------------------------------------------------------- /Examples/ImportTS/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Getting Started 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

14 | 
15 | 
16 | 
17 | 


--------------------------------------------------------------------------------
/Examples/ImportTS/index.js:
--------------------------------------------------------------------------------
 1 | import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
 2 | const { exports } = await init({
 3 |     imports: {
 4 |         consoleLog: (message) => {
 5 |             console.log(message);
 6 |         },
 7 |         getDocument: () => {
 8 |             return document;
 9 |         },
10 |     }
11 | });
12 | 
13 | exports.run()
14 | 


--------------------------------------------------------------------------------
/Examples/Multithreading/Package.resolved:
--------------------------------------------------------------------------------
 1 | {
 2 |   "originHash" : "072d03a6e24e01bd372682a6090adb80cf29dea39421e065de6ff8853de704c9",
 3 |   "pins" : [
 4 |     {
 5 |       "identity" : "chibi-ray",
 6 |       "kind" : "remoteSourceControl",
 7 |       "location" : "https://github.com/kateinoigakukun/chibi-ray",
 8 |       "state" : {
 9 |         "revision" : "c8cab621a3338dd2f8e817d3785362409d3b8cf1"
10 |       }
11 |     },
12 |     {
13 |       "identity" : "swift-syntax",
14 |       "kind" : "remoteSourceControl",
15 |       "location" : "https://github.com/swiftlang/swift-syntax",
16 |       "state" : {
17 |         "revision" : "0687f71944021d616d34d922343dcef086855920",
18 |         "version" : "600.0.1"
19 |       }
20 |     }
21 |   ],
22 |   "version" : 3
23 | }
24 | 


--------------------------------------------------------------------------------
/Examples/Multithreading/Package.swift:
--------------------------------------------------------------------------------
 1 | // swift-tools-version: 5.10
 2 | 
 3 | import PackageDescription
 4 | 
 5 | let package = Package(
 6 |     name: "Example",
 7 |     platforms: [.macOS("15"), .iOS("18"), .watchOS("11"), .tvOS("18"), .visionOS("2")],
 8 |     dependencies: [
 9 |         .package(path: "../../"),
10 |         .package(
11 |             url: "https://github.com/kateinoigakukun/chibi-ray",
12 |             revision: "c8cab621a3338dd2f8e817d3785362409d3b8cf1"
13 |         ),
14 |     ],
15 |     targets: [
16 |         .executableTarget(
17 |             name: "MyApp",
18 |             dependencies: [
19 |                 .product(name: "JavaScriptKit", package: "JavaScriptKit"),
20 |                 .product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
21 |                 .product(name: "ChibiRay", package: "chibi-ray"),
22 |             ]
23 |         )
24 |     ]
25 | )
26 | 


--------------------------------------------------------------------------------
/Examples/Multithreading/README.md:
--------------------------------------------------------------------------------
 1 | # Multithreading example
 2 | 
 3 | Install Development Snapshot toolchain `DEVELOPMENT-SNAPSHOT-2024-07-08-a` or later from [swift.org/install](https://www.swift.org/install/) and run the following commands:
 4 | 
 5 | ```sh
 6 | $ (
 7 |   set -eo pipefail; \
 8 |   V="$(swiftc --version | head -n1)"; \
 9 |   TAG="$(curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/tag-by-version.json" | jq -e -r --arg v "$V" '.[$v] | .[-1]')"; \
10 |   curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/builds/$TAG.json" | \
11 |   jq -r '.["swift-sdks"]["wasm32-unknown-wasip1-threads"] | "swift sdk install \"\(.url)\" --checksum \"\(.checksum)\""' | sh -x
12 | )
13 | $ export SWIFT_SDK_ID=$(
14 |   V="$(swiftc --version | head -n1)"; \
15 |   TAG="$(curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/tag-by-version.json" | jq -e -r --arg v "$V" '.[$v] | .[-1]')"; \
16 |   curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/builds/$TAG.json" | \
17 |   jq -r '.["swift-sdks"]["wasm32-unknown-wasip1-threads"]["id"]'
18 | )
19 | $ ./build.sh
20 | $ npx serve
21 | ```
22 | 


--------------------------------------------------------------------------------
/Examples/Multithreading/build.sh:
--------------------------------------------------------------------------------
1 | swift package --swift-sdk "${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}" -c release \
2 |     plugin --allow-writing-to-package-directory \
3 |     js --use-cdn --output ./Bundle
4 | 


--------------------------------------------------------------------------------
/Examples/Multithreading/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |   
 5 |   Threading Example
 6 |   
13 |   
27 | 
28 | 
29 | 
30 |   
34 |   

Threading Example

35 |

36 |

37 | 38 | 39 |
40 |
41 | 42 | 43 |
44 |
45 | 46 | 47 | 48 |
49 |

50 |

51 |

🧵
52 |

53 |

54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Examples/Multithreading/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": [{ 3 | "source": "**/*", 4 | "headers": [ 5 | { 6 | "key": "Cross-Origin-Embedder-Policy", 7 | "value": "require-corp" 8 | }, { 9 | "key": "Cross-Origin-Opener-Policy", 10 | "value": "same-origin" 11 | } 12 | ] 13 | }] 14 | } 15 | -------------------------------------------------------------------------------- /Examples/OffscrenCanvas/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.10 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Example", 7 | platforms: [.macOS("15"), .iOS("18"), .watchOS("11"), .tvOS("18"), .visionOS("2")], 8 | dependencies: [ 9 | .package(path: "../../") 10 | ], 11 | targets: [ 12 | .executableTarget( 13 | name: "MyApp", 14 | dependencies: [ 15 | .product(name: "JavaScriptKit", package: "JavaScriptKit"), 16 | .product(name: "JavaScriptEventLoop", package: "JavaScriptKit"), 17 | ] 18 | ) 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /Examples/OffscrenCanvas/README.md: -------------------------------------------------------------------------------- 1 | # OffscreenCanvas example 2 | 3 | Install Development Snapshot toolchain `DEVELOPMENT-SNAPSHOT-2024-07-08-a` or later from [swift.org/install](https://www.swift.org/install/) and run the following commands: 4 | 5 | ```sh 6 | $ ( 7 | set -eo pipefail; \ 8 | V="$(swiftc --version | head -n1)"; \ 9 | TAG="$(curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/tag-by-version.json" | jq -e -r --arg v "$V" '.[$v] | .[-1]')"; \ 10 | curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/builds/$TAG.json" | \ 11 | jq -r '.["swift-sdks"]["wasm32-unknown-wasip1-threads"] | "swift sdk install \"\(.url)\" --checksum \"\(.checksum)\""' | sh -x 12 | ) 13 | $ export SWIFT_SDK_ID=$( 14 | V="$(swiftc --version | head -n1)"; \ 15 | TAG="$(curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/tag-by-version.json" | jq -e -r --arg v "$V" '.[$v] | .[-1]')"; \ 16 | curl -sL "https://raw.githubusercontent.com/swiftwasm/swift-sdk-index/refs/heads/main/v1/builds/$TAG.json" | \ 17 | jq -r '.["swift-sdks"]["wasm32-unknown-wasip1-threads"]["id"]' 18 | ) 19 | $ ./build.sh 20 | $ npx serve 21 | ``` 22 | -------------------------------------------------------------------------------- /Examples/OffscrenCanvas/build.sh: -------------------------------------------------------------------------------- 1 | swift package --swift-sdk "${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}" -c release \ 2 | plugin --allow-writing-to-package-directory \ 3 | js --use-cdn --output ./Bundle 4 | -------------------------------------------------------------------------------- /Examples/OffscrenCanvas/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OffscreenCanvas Example 6 | 68 | 69 | 70 | 71 | 75 |

OffscreenCanvas Example

76 |

77 |

78 | 82 | 83 | 84 |
85 |

86 | 87 |

CSS Animation (Main Thread Performance Indicator)

88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | 96 |
FPS: 0
97 | 98 |
99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Examples/OffscrenCanvas/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": [{ 3 | "source": "**/*", 4 | "headers": [ 5 | { 6 | "key": "Cross-Origin-Embedder-Policy", 7 | "value": "require-corp" 8 | }, { 9 | "key": "Cross-Origin-Opener-Policy", 10 | "value": "same-origin" 11 | } 12 | ] 13 | }] 14 | } 15 | -------------------------------------------------------------------------------- /Examples/Testing/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Counter", 7 | products: [ 8 | .library( 9 | name: "Counter", 10 | targets: ["Counter"] 11 | ) 12 | ], 13 | dependencies: [.package(name: "JavaScriptKit", path: "../../")], 14 | targets: [ 15 | .target( 16 | name: "Counter", 17 | dependencies: [ 18 | .product(name: "JavaScriptKit", package: "JavaScriptKit") 19 | ] 20 | ), 21 | .testTarget( 22 | name: "CounterTests", 23 | dependencies: [ 24 | "Counter", 25 | // This is needed to run the tests in the JavaScript event loop 26 | .product(name: "JavaScriptEventLoopTestSupport", package: "JavaScriptKit"), 27 | ] 28 | ), 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /Examples/Testing/README.md: -------------------------------------------------------------------------------- 1 | # Testing example 2 | 3 | This example demonstrates how to write and run tests for Swift code compiled to WebAssembly using JavaScriptKit. 4 | 5 | ## Running Tests 6 | 7 | To run the tests, use the following command: 8 | 9 | ```console 10 | swift package --disable-sandbox --swift-sdk wasm32-unknown-wasi js test 11 | ``` 12 | 13 | ## Code Coverage 14 | 15 | To generate and view code coverage reports: 16 | 17 | 1. Run tests with code coverage enabled: 18 | 19 | ```console 20 | swift package --disable-sandbox --swift-sdk wasm32-unknown-wasi js test --enable-code-coverage 21 | ``` 22 | 23 | 2. Generate HTML coverage report: 24 | 25 | ```console 26 | llvm-cov show -instr-profile=.build/plugins/PackageToJS/outputs/PackageTests/default.profdata --format=html .build/plugins/PackageToJS/outputs/PackageTests/main.wasm -o .build/coverage/html Sources 27 | ``` 28 | 29 | 3. Serve and view the coverage report: 30 | 31 | ```console 32 | npx serve .build/coverage/html 33 | ``` 34 | -------------------------------------------------------------------------------- /Examples/Testing/Sources/Counter/Counter.swift: -------------------------------------------------------------------------------- 1 | public struct Counter { 2 | public private(set) var count = 0 3 | 4 | public mutating func increment() { 5 | count += 1 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Examples/Testing/Tests/CounterTests/CounterTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import Counter 4 | 5 | #if canImport(Testing) 6 | import Testing 7 | 8 | @Test func increment() async throws { 9 | var counter = Counter() 10 | counter.increment() 11 | #expect(counter.count == 1) 12 | } 13 | 14 | @Test func incrementTwice() async throws { 15 | var counter = Counter() 16 | counter.increment() 17 | counter.increment() 18 | #expect(counter.count == 2) 19 | } 20 | 21 | #endif 22 | 23 | class CounterTests: XCTestCase { 24 | func testIncrement() async { 25 | var counter = Counter() 26 | counter.increment() 27 | XCTAssertEqual(counter.count, 1) 28 | } 29 | 30 | func testIncrementTwice() async { 31 | var counter = Counter() 32 | counter.increment() 33 | counter.increment() 34 | XCTAssertEqual(counter.count, 2) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Examples/Testing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "playwright": "^1.52.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yuta Saito 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SWIFT_SDK_ID ?= wasm32-unknown-wasi 2 | 3 | .PHONY: bootstrap 4 | bootstrap: 5 | npm ci 6 | 7 | .PHONY: unittest 8 | unittest: 9 | @echo Running unit tests 10 | env JAVASCRIPTKIT_EXPERIMENTAL_BRIDGEJS=1 swift package --swift-sdk "$(SWIFT_SDK_ID)" \ 11 | --disable-sandbox \ 12 | -Xlinker --stack-first \ 13 | -Xlinker --global-base=524288 \ 14 | -Xlinker -z \ 15 | -Xlinker stack-size=524288 \ 16 | js test --prelude ./Tests/prelude.mjs 17 | 18 | .PHONY: regenerate_swiftpm_resources 19 | regenerate_swiftpm_resources: 20 | npm run build 21 | cp Runtime/lib/index.mjs Plugins/PackageToJS/Templates/runtime.mjs 22 | cp Runtime/lib/index.d.ts Plugins/PackageToJS/Templates/runtime.d.ts 23 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "BridgeJS", 7 | platforms: [.macOS(.v13)], 8 | dependencies: [ 9 | .package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.1") 10 | ], 11 | targets: [ 12 | .target(name: "BridgeJSBuildPlugin"), 13 | .target(name: "BridgeJSLink"), 14 | .executableTarget( 15 | name: "BridgeJSTool", 16 | dependencies: [ 17 | .product(name: "SwiftParser", package: "swift-syntax"), 18 | .product(name: "SwiftSyntax", package: "swift-syntax"), 19 | .product(name: "SwiftBasicFormat", package: "swift-syntax"), 20 | .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), 21 | ] 22 | ), 23 | .testTarget( 24 | name: "BridgeJSToolTests", 25 | dependencies: ["BridgeJSTool", "BridgeJSLink"], 26 | exclude: ["__Snapshots__", "Inputs"] 27 | ), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSSkeleton: -------------------------------------------------------------------------------- 1 | ../BridgeJSSkeleton -------------------------------------------------------------------------------- /Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift: -------------------------------------------------------------------------------- 1 | // This file is shared between BridgeTool and BridgeJSLink 2 | 3 | // MARK: - Types 4 | 5 | enum BridgeType: Codable, Equatable { 6 | case int, float, double, string, bool, jsObject(String?), swiftHeapObject(String), void 7 | } 8 | 9 | enum WasmCoreType: String, Codable { 10 | case i32, i64, f32, f64, pointer 11 | } 12 | 13 | struct Parameter: Codable { 14 | let label: String? 15 | let name: String 16 | let type: BridgeType 17 | } 18 | 19 | // MARK: - Exported Skeleton 20 | 21 | struct ExportedFunction: Codable { 22 | var name: String 23 | var abiName: String 24 | var parameters: [Parameter] 25 | var returnType: BridgeType 26 | } 27 | 28 | struct ExportedClass: Codable { 29 | var name: String 30 | var constructor: ExportedConstructor? 31 | var methods: [ExportedFunction] 32 | } 33 | 34 | struct ExportedConstructor: Codable { 35 | var abiName: String 36 | var parameters: [Parameter] 37 | } 38 | 39 | struct ExportedSkeleton: Codable { 40 | let functions: [ExportedFunction] 41 | let classes: [ExportedClass] 42 | } 43 | 44 | // MARK: - Imported Skeleton 45 | 46 | struct ImportedFunctionSkeleton: Codable { 47 | let name: String 48 | let parameters: [Parameter] 49 | let returnType: BridgeType 50 | let documentation: String? 51 | 52 | func abiName(context: ImportedTypeSkeleton?) -> String { 53 | return context.map { "bjs_\($0.name)_\(name)" } ?? "bjs_\(name)" 54 | } 55 | } 56 | 57 | struct ImportedConstructorSkeleton: Codable { 58 | let parameters: [Parameter] 59 | 60 | func abiName(context: ImportedTypeSkeleton) -> String { 61 | return "bjs_\(context.name)_init" 62 | } 63 | } 64 | 65 | struct ImportedPropertySkeleton: Codable { 66 | let name: String 67 | let isReadonly: Bool 68 | let type: BridgeType 69 | let documentation: String? 70 | 71 | func getterAbiName(context: ImportedTypeSkeleton) -> String { 72 | return "bjs_\(context.name)_\(name)_get" 73 | } 74 | 75 | func setterAbiName(context: ImportedTypeSkeleton) -> String { 76 | return "bjs_\(context.name)_\(name)_set" 77 | } 78 | } 79 | 80 | struct ImportedTypeSkeleton: Codable { 81 | let name: String 82 | let constructor: ImportedConstructorSkeleton? 83 | let methods: [ImportedFunctionSkeleton] 84 | let properties: [ImportedPropertySkeleton] 85 | let documentation: String? 86 | } 87 | 88 | struct ImportedFileSkeleton: Codable { 89 | let functions: [ImportedFunctionSkeleton] 90 | let types: [ImportedTypeSkeleton] 91 | } 92 | 93 | struct ImportedModuleSkeleton: Codable { 94 | let moduleName: String 95 | var children: [ImportedFileSkeleton] 96 | } 97 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSSkeleton: -------------------------------------------------------------------------------- 1 | ../BridgeJSSkeleton -------------------------------------------------------------------------------- /Plugins/BridgeJS/Sources/BridgeJSTool/DiagnosticError.swift: -------------------------------------------------------------------------------- 1 | import SwiftSyntax 2 | 3 | struct DiagnosticError: Error { 4 | let node: Syntax 5 | let message: String 6 | let hint: String? 7 | 8 | init(node: some SyntaxProtocol, message: String, hint: String? = nil) { 9 | self.node = Syntax(node) 10 | self.message = message 11 | self.hint = hint 12 | } 13 | 14 | func formattedDescription(fileName: String) -> String { 15 | let locationConverter = SourceLocationConverter(fileName: fileName, tree: node.root) 16 | let location = locationConverter.location(for: node.position) 17 | var description = "\(fileName):\(location.line):\(location.column): error: \(message)" 18 | if let hint { 19 | description += "\nHint: \(hint)" 20 | } 21 | return description 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Sources/JavaScript/README.md: -------------------------------------------------------------------------------- 1 | # ts2skeleton 2 | 3 | This script analyzes the TypeScript type definitions and produces a structured JSON output with skeleton information that can be used to generate Swift bindings. 4 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Sources/JavaScript/bin/ts2skeleton.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // @ts-check 3 | 4 | /** 5 | * Main entry point for the ts2skeleton tool 6 | * 7 | * This script analyzes the TypeScript type definitions and produces a structured 8 | * JSON output with skeleton information that can be used to generate Swift 9 | * bindings. 10 | */ 11 | 12 | import { main } from "../src/cli.js" 13 | 14 | main(process.argv.slice(2)); 15 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Sources/JavaScript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "dependencies": { 4 | "typescript": "5.8.2" 5 | }, 6 | "bin": { 7 | "ts2skeleton": "./bin/ts2skeleton.js" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Sources/JavaScript/src/index.d.ts: -------------------------------------------------------------------------------- 1 | export type BridgeType = 2 | | { "int": {} } 3 | | { "float": {} } 4 | | { "double": {} } 5 | | { "string": {} } 6 | | { "bool": {} } 7 | | { "jsObject": { "_0": string } | {} } 8 | | { "void": {} } 9 | 10 | export type Parameter = { 11 | name: string; 12 | type: BridgeType; 13 | } 14 | 15 | export type ImportFunctionSkeleton = { 16 | name: string; 17 | parameters: Parameter[]; 18 | returnType: BridgeType; 19 | documentation: string | undefined; 20 | } 21 | 22 | export type ImportConstructorSkeleton = { 23 | parameters: Parameter[]; 24 | } 25 | 26 | export type ImportPropertySkeleton = { 27 | name: string; 28 | type: BridgeType; 29 | isReadonly: boolean; 30 | documentation: string | undefined; 31 | } 32 | 33 | export type ImportTypeSkeleton = { 34 | name: string; 35 | documentation: string | undefined; 36 | constructor?: ImportConstructorSkeleton; 37 | properties: ImportPropertySkeleton[]; 38 | methods: ImportFunctionSkeleton[]; 39 | } 40 | 41 | export type ImportSkeleton = { 42 | functions: ImportFunctionSkeleton[]; 43 | types: ImportTypeSkeleton[]; 44 | } 45 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/ExportSwiftTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftSyntax 3 | import SwiftParser 4 | import Testing 5 | 6 | @testable import BridgeJSTool 7 | 8 | @Suite struct ExportSwiftTests { 9 | private func snapshot( 10 | swiftAPI: ExportSwift, 11 | name: String? = nil, 12 | filePath: String = #filePath, 13 | function: String = #function, 14 | sourceLocation: Testing.SourceLocation = #_sourceLocation 15 | ) throws { 16 | let (outputSwift, outputSkeleton) = try #require(try swiftAPI.finalize()) 17 | try assertSnapshot( 18 | name: name, 19 | filePath: filePath, 20 | function: function, 21 | sourceLocation: sourceLocation, 22 | input: outputSwift.data(using: .utf8)!, 23 | fileExtension: "swift" 24 | ) 25 | let encoder = JSONEncoder() 26 | encoder.outputFormatting = [.prettyPrinted, .sortedKeys] 27 | let outputSkeletonData = try encoder.encode(outputSkeleton) 28 | try assertSnapshot( 29 | name: name, 30 | filePath: filePath, 31 | function: function, 32 | sourceLocation: sourceLocation, 33 | input: outputSkeletonData, 34 | fileExtension: "json" 35 | ) 36 | } 37 | 38 | static let inputsDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent( 39 | "Inputs" 40 | ) 41 | 42 | static func collectInputs() -> [String] { 43 | let fileManager = FileManager.default 44 | let inputs = try! fileManager.contentsOfDirectory(atPath: Self.inputsDirectory.path) 45 | return inputs.filter { $0.hasSuffix(".swift") } 46 | } 47 | 48 | @Test(arguments: collectInputs()) 49 | func snapshot(input: String) throws { 50 | let swiftAPI = ExportSwift(progress: .silent) 51 | let url = Self.inputsDirectory.appendingPathComponent(input) 52 | let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8)) 53 | try swiftAPI.addSourceFile(sourceFile, input) 54 | let name = url.deletingPathExtension().lastPathComponent 55 | try snapshot(swiftAPI: swiftAPI, name: name) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/ImportTSTests.swift: -------------------------------------------------------------------------------- 1 | import Testing 2 | import Foundation 3 | @testable import BridgeJSTool 4 | 5 | @Suite struct ImportTSTests { 6 | static let inputsDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent( 7 | "Inputs" 8 | ) 9 | 10 | static func collectInputs() -> [String] { 11 | let fileManager = FileManager.default 12 | let inputs = try! fileManager.contentsOfDirectory(atPath: Self.inputsDirectory.path) 13 | return inputs.filter { $0.hasSuffix(".d.ts") } 14 | } 15 | 16 | @Test(arguments: collectInputs()) 17 | func snapshot(input: String) throws { 18 | var api = ImportTS(progress: .silent, moduleName: "Check") 19 | let url = Self.inputsDirectory.appendingPathComponent(input) 20 | let tsconfigPath = url.deletingLastPathComponent().appendingPathComponent("tsconfig.json") 21 | try api.addSourceFile(url.path, tsconfigPath: tsconfigPath.path) 22 | let outputSwift = try #require(try api.finalize()) 23 | let name = url.deletingPathExtension().deletingPathExtension().deletingPathExtension().lastPathComponent 24 | try assertSnapshot( 25 | name: name, 26 | filePath: #filePath, 27 | function: #function, 28 | input: outputSwift.data(using: .utf8)!, 29 | fileExtension: "swift" 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/ArrayParameter.d.ts: -------------------------------------------------------------------------------- 1 | export function checkArray(a: number[]): void; 2 | export function checkArrayWithLength(a: number[], b: number): void; 3 | export function checkArray(a: Array): void; 4 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Interface.d.ts: -------------------------------------------------------------------------------- 1 | interface Animatable { 2 | animate(keyframes: any, options: any): any; 3 | getAnimations(options: any): any; 4 | } 5 | 6 | export function returnAnimatable(): Animatable; 7 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveParameters.d.ts: -------------------------------------------------------------------------------- 1 | export function check(a: number, b: boolean): void; 2 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveParameters.swift: -------------------------------------------------------------------------------- 1 | @JS func check(a: Int, b: Float, c: Double, d: Bool) {} 2 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveReturn.d.ts: -------------------------------------------------------------------------------- 1 | export function checkNumber(): number; 2 | export function checkBoolean(): boolean; 3 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveReturn.swift: -------------------------------------------------------------------------------- 1 | @JS func checkInt() -> Int { fatalError() } 2 | @JS func checkFloat() -> Float { fatalError() } 3 | @JS func checkDouble() -> Double { fatalError() } 4 | @JS func checkBool() -> Bool { fatalError() } 5 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.d.ts: -------------------------------------------------------------------------------- 1 | export function checkString(a: string): void; 2 | export function checkStringWithLength(a: string, b: number): void; 3 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.swift: -------------------------------------------------------------------------------- 1 | @JS func checkString(a: String) {} 2 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringReturn.d.ts: -------------------------------------------------------------------------------- 1 | export function checkString(): string; 2 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringReturn.swift: -------------------------------------------------------------------------------- 1 | @JS func checkString() -> String { fatalError() } 2 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift: -------------------------------------------------------------------------------- 1 | @JS class Greeter { 2 | var name: String 3 | 4 | @JS init(name: String) { 5 | self.name = name 6 | } 7 | @JS func greet() -> String { 8 | return "Hello, " + self.name + "!" 9 | } 10 | @JS func changeName(name: String) { 11 | self.name = name 12 | } 13 | } 14 | 15 | @JS func takeGreeter(greeter: Greeter) { 16 | print(greeter.greet()) 17 | } 18 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/TypeAlias.d.ts: -------------------------------------------------------------------------------- 1 | export type MyType = number; 2 | 3 | export function checkSimple(a: MyType): void; 4 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/TypeScriptClass.d.ts: -------------------------------------------------------------------------------- 1 | export class Greeter { 2 | constructor(name: string); 3 | greet(): string; 4 | changeName(name: string): void; 5 | } 6 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/VoidParameterVoidReturn.d.ts: -------------------------------------------------------------------------------- 1 | export function check(): void; 2 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/VoidParameterVoidReturn.swift: -------------------------------------------------------------------------------- 1 | @JS func check() {} 2 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/SnapshotTesting.swift: -------------------------------------------------------------------------------- 1 | import Testing 2 | import Foundation 3 | 4 | func assertSnapshot( 5 | name: String? = nil, 6 | filePath: String = #filePath, 7 | function: String = #function, 8 | sourceLocation: SourceLocation = #_sourceLocation, 9 | variant: String? = nil, 10 | input: Data, 11 | fileExtension: String = "json" 12 | ) throws { 13 | let testFileName = URL(fileURLWithPath: filePath).deletingPathExtension().lastPathComponent 14 | let snapshotDir = URL(fileURLWithPath: filePath) 15 | .deletingLastPathComponent() 16 | .appendingPathComponent("__Snapshots__") 17 | .appendingPathComponent(testFileName) 18 | try FileManager.default.createDirectory(at: snapshotDir, withIntermediateDirectories: true) 19 | let snapshotName = name ?? String(function[.. Comment { 28 | "Snapshot mismatch: \(actualFilePath) \(snapshotPath.path)" 29 | } 30 | if !ok { 31 | try input.write(to: URL(fileURLWithPath: actualFilePath)) 32 | } 33 | if ProcessInfo.processInfo.environment["UPDATE_SNAPSHOTS"] == nil { 34 | #expect(ok, buildComment(), sourceLocation: sourceLocation) 35 | } else { 36 | try input.write(to: snapshotPath) 37 | } 38 | } else { 39 | try input.write(to: snapshotPath) 40 | #expect(Bool(false), "Snapshot created at \(snapshotPath.path)", sourceLocation: sourceLocation) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/TemporaryDirectory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct MakeTemporaryDirectoryError: Error { 4 | let error: CInt 5 | } 6 | 7 | internal func withTemporaryDirectory(body: (URL, _ retain: inout Bool) throws -> T) throws -> T { 8 | // Create a temporary directory using mkdtemp 9 | var template = FileManager.default.temporaryDirectory.appendingPathComponent("PackageToJSTests.XXXXXX").path 10 | return try template.withUTF8 { template in 11 | let copy = UnsafeMutableBufferPointer.allocate(capacity: template.count + 1) 12 | template.copyBytes(to: copy) 13 | copy[template.count] = 0 14 | 15 | guard let result = mkdtemp(copy.baseAddress!) else { 16 | throw MakeTemporaryDirectoryError(error: errno) 17 | } 18 | let tempDir = URL(fileURLWithPath: String(cString: result)) 19 | var retain = false 20 | defer { 21 | if !retain { 22 | try? FileManager.default.removeItem(at: tempDir) 23 | } 24 | } 25 | return try body(tempDir, &retain) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | checkArray(a: any): void; 11 | checkArrayWithLength(a: any, b: number): void; 12 | checkArray(a: any): void; 13 | } 14 | export function createInstantiator(options: { 15 | imports: Imports; 16 | }, swift: any): Promise<{ 17 | addImports: (importObject: WebAssembly.Imports) => void; 18 | setInstance: (instance: WebAssembly.Instance) => void; 19 | createExports: (instance: WebAssembly.Instance) => Exports; 20 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_checkArray"] = function bjs_checkArray(a) { 40 | options.imports.checkArray(swift.memory.getObject(a)); 41 | } 42 | TestModule["bjs_checkArrayWithLength"] = function bjs_checkArrayWithLength(a, b) { 43 | options.imports.checkArrayWithLength(swift.memory.getObject(a), b); 44 | } 45 | TestModule["bjs_checkArray"] = function bjs_checkArray(a) { 46 | options.imports.checkArray(swift.memory.getObject(a)); 47 | } 48 | }, 49 | setInstance: (i) => { 50 | instance = i; 51 | memory = instance.exports.memory; 52 | }, 53 | /** @param {WebAssembly.Instance} instance */ 54 | createExports: (instance) => { 55 | const js = swift.memory.heap; 56 | 57 | return { 58 | 59 | }; 60 | }, 61 | } 62 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | returnAnimatable(): any; 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_returnAnimatable"] = function bjs_returnAnimatable() { 40 | let ret = options.imports.returnAnimatable(); 41 | return swift.memory.retain(ret); 42 | } 43 | TestModule["bjs_Animatable_animate"] = function bjs_Animatable_animate(self, keyframes, options) { 44 | let ret = swift.memory.getObject(self).animate(swift.memory.getObject(keyframes), swift.memory.getObject(options)); 45 | return swift.memory.retain(ret); 46 | } 47 | TestModule["bjs_Animatable_getAnimations"] = function bjs_Animatable_getAnimations(self, options) { 48 | let ret = swift.memory.getObject(self).getAnimations(swift.memory.getObject(options)); 49 | return swift.memory.retain(ret); 50 | } 51 | }, 52 | setInstance: (i) => { 53 | instance = i; 54 | memory = instance.exports.memory; 55 | }, 56 | /** @param {WebAssembly.Instance} instance */ 57 | createExports: (instance) => { 58 | const js = swift.memory.heap; 59 | 60 | return { 61 | 62 | }; 63 | }, 64 | } 65 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | check(a: number, b: number, c: number, d: boolean): void; 9 | } 10 | export type Imports = { 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | 39 | }, 40 | setInstance: (i) => { 41 | instance = i; 42 | memory = instance.exports.memory; 43 | }, 44 | /** @param {WebAssembly.Instance} instance */ 45 | createExports: (instance) => { 46 | const js = swift.memory.heap; 47 | 48 | return { 49 | check: function bjs_check(a, b, c, d) { 50 | instance.exports.bjs_check(a, b, c, d); 51 | }, 52 | }; 53 | }, 54 | } 55 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | check(a: number, b: boolean): void; 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_check"] = function bjs_check(a, b) { 40 | options.imports.check(a, b); 41 | } 42 | }, 43 | setInstance: (i) => { 44 | instance = i; 45 | memory = instance.exports.memory; 46 | }, 47 | /** @param {WebAssembly.Instance} instance */ 48 | createExports: (instance) => { 49 | const js = swift.memory.heap; 50 | 51 | return { 52 | 53 | }; 54 | }, 55 | } 56 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | checkInt(): number; 9 | checkFloat(): number; 10 | checkDouble(): number; 11 | checkBool(): boolean; 12 | } 13 | export type Imports = { 14 | } 15 | export function createInstantiator(options: { 16 | imports: Imports; 17 | }, swift: any): Promise<{ 18 | addImports: (importObject: WebAssembly.Imports) => void; 19 | setInstance: (instance: WebAssembly.Instance) => void; 20 | createExports: (instance: WebAssembly.Instance) => Exports; 21 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | 39 | }, 40 | setInstance: (i) => { 41 | instance = i; 42 | memory = instance.exports.memory; 43 | }, 44 | /** @param {WebAssembly.Instance} instance */ 45 | createExports: (instance) => { 46 | const js = swift.memory.heap; 47 | 48 | return { 49 | checkInt: function bjs_checkInt() { 50 | const ret = instance.exports.bjs_checkInt(); 51 | return ret; 52 | }, 53 | checkFloat: function bjs_checkFloat() { 54 | const ret = instance.exports.bjs_checkFloat(); 55 | return ret; 56 | }, 57 | checkDouble: function bjs_checkDouble() { 58 | const ret = instance.exports.bjs_checkDouble(); 59 | return ret; 60 | }, 61 | checkBool: function bjs_checkBool() { 62 | const ret = instance.exports.bjs_checkBool() !== 0; 63 | return ret; 64 | }, 65 | }; 66 | }, 67 | } 68 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | checkNumber(): number; 11 | checkBoolean(): boolean; 12 | } 13 | export function createInstantiator(options: { 14 | imports: Imports; 15 | }, swift: any): Promise<{ 16 | addImports: (importObject: WebAssembly.Imports) => void; 17 | setInstance: (instance: WebAssembly.Instance) => void; 18 | createExports: (instance: WebAssembly.Instance) => Exports; 19 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_checkNumber"] = function bjs_checkNumber() { 40 | let ret = options.imports.checkNumber(); 41 | return ret; 42 | } 43 | TestModule["bjs_checkBoolean"] = function bjs_checkBoolean() { 44 | let ret = options.imports.checkBoolean(); 45 | return ret !== 0; 46 | } 47 | }, 48 | setInstance: (i) => { 49 | instance = i; 50 | memory = instance.exports.memory; 51 | }, 52 | /** @param {WebAssembly.Instance} instance */ 53 | createExports: (instance) => { 54 | const js = swift.memory.heap; 55 | 56 | return { 57 | 58 | }; 59 | }, 60 | } 61 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | checkString(a: string): void; 9 | } 10 | export type Imports = { 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | 39 | }, 40 | setInstance: (i) => { 41 | instance = i; 42 | memory = instance.exports.memory; 43 | }, 44 | /** @param {WebAssembly.Instance} instance */ 45 | createExports: (instance) => { 46 | const js = swift.memory.heap; 47 | 48 | return { 49 | checkString: function bjs_checkString(a) { 50 | const aBytes = textEncoder.encode(a); 51 | const aId = swift.memory.retain(aBytes); 52 | instance.exports.bjs_checkString(aId, aBytes.length); 53 | swift.memory.release(aId); 54 | }, 55 | }; 56 | }, 57 | } 58 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | checkString(a: string): void; 11 | checkStringWithLength(a: string, b: number): void; 12 | } 13 | export function createInstantiator(options: { 14 | imports: Imports; 15 | }, swift: any): Promise<{ 16 | addImports: (importObject: WebAssembly.Imports) => void; 17 | setInstance: (instance: WebAssembly.Instance) => void; 18 | createExports: (instance: WebAssembly.Instance) => Exports; 19 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_checkString"] = function bjs_checkString(a) { 40 | const aObject = swift.memory.getObject(a); 41 | swift.memory.release(a); 42 | options.imports.checkString(aObject); 43 | } 44 | TestModule["bjs_checkStringWithLength"] = function bjs_checkStringWithLength(a, b) { 45 | const aObject = swift.memory.getObject(a); 46 | swift.memory.release(a); 47 | options.imports.checkStringWithLength(aObject, b); 48 | } 49 | }, 50 | setInstance: (i) => { 51 | instance = i; 52 | memory = instance.exports.memory; 53 | }, 54 | /** @param {WebAssembly.Instance} instance */ 55 | createExports: (instance) => { 56 | const js = swift.memory.heap; 57 | 58 | return { 59 | 60 | }; 61 | }, 62 | } 63 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | checkString(): string; 9 | } 10 | export type Imports = { 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | 39 | }, 40 | setInstance: (i) => { 41 | instance = i; 42 | memory = instance.exports.memory; 43 | }, 44 | /** @param {WebAssembly.Instance} instance */ 45 | createExports: (instance) => { 46 | const js = swift.memory.heap; 47 | 48 | return { 49 | checkString: function bjs_checkString() { 50 | instance.exports.bjs_checkString(); 51 | const ret = tmpRetString; 52 | tmpRetString = undefined; 53 | return ret; 54 | }, 55 | }; 56 | }, 57 | } 58 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | checkString(): string; 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_checkString"] = function bjs_checkString() { 40 | let ret = options.imports.checkString(); 41 | tmpRetBytes = textEncoder.encode(ret); 42 | return tmpRetBytes.length; 43 | } 44 | }, 45 | setInstance: (i) => { 46 | instance = i; 47 | memory = instance.exports.memory; 48 | }, 49 | /** @param {WebAssembly.Instance} instance */ 50 | createExports: (instance) => { 51 | const js = swift.memory.heap; 52 | 53 | return { 54 | 55 | }; 56 | }, 57 | } 58 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | /// Represents a Swift heap object like a class instance or an actor instance. 8 | export interface SwiftHeapObject { 9 | /// Release the heap object. 10 | /// 11 | /// Note: Calling this method will release the heap object and it will no longer be accessible. 12 | release(): void; 13 | } 14 | export interface Greeter extends SwiftHeapObject { 15 | greet(): string; 16 | changeName(name: string): void; 17 | } 18 | export type Exports = { 19 | Greeter: { 20 | new(name: string): Greeter; 21 | } 22 | takeGreeter(greeter: Greeter): void; 23 | } 24 | export type Imports = { 25 | } 26 | export function createInstantiator(options: { 27 | imports: Imports; 28 | }, swift: any): Promise<{ 29 | addImports: (importObject: WebAssembly.Imports) => void; 30 | setInstance: (instance: WebAssembly.Instance) => void; 31 | createExports: (instance: WebAssembly.Instance) => Exports; 32 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | checkSimple(a: number): void; 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_checkSimple"] = function bjs_checkSimple(a) { 40 | options.imports.checkSimple(a); 41 | } 42 | }, 43 | setInstance: (i) => { 44 | instance = i; 45 | memory = instance.exports.memory; 46 | }, 47 | /** @param {WebAssembly.Instance} instance */ 48 | createExports: (instance) => { 49 | const js = swift.memory.heap; 50 | 51 | return { 52 | 53 | }; 54 | }, 55 | } 56 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | } 11 | export function createInstantiator(options: { 12 | imports: Imports; 13 | }, swift: any): Promise<{ 14 | addImports: (importObject: WebAssembly.Imports) => void; 15 | setInstance: (instance: WebAssembly.Instance) => void; 16 | createExports: (instance: WebAssembly.Instance) => Exports; 17 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_Greeter_greet"] = function bjs_Greeter_greet(self) { 40 | let ret = swift.memory.getObject(self).greet(); 41 | tmpRetBytes = textEncoder.encode(ret); 42 | return tmpRetBytes.length; 43 | } 44 | TestModule["bjs_Greeter_changeName"] = function bjs_Greeter_changeName(self, name) { 45 | const nameObject = swift.memory.getObject(name); 46 | swift.memory.release(name); 47 | swift.memory.getObject(self).changeName(nameObject); 48 | } 49 | }, 50 | setInstance: (i) => { 51 | instance = i; 52 | memory = instance.exports.memory; 53 | }, 54 | /** @param {WebAssembly.Instance} instance */ 55 | createExports: (instance) => { 56 | const js = swift.memory.heap; 57 | 58 | return { 59 | 60 | }; 61 | }, 62 | } 63 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | check(): void; 9 | } 10 | export type Imports = { 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | 39 | }, 40 | setInstance: (i) => { 41 | instance = i; 42 | memory = instance.exports.memory; 43 | }, 44 | /** @param {WebAssembly.Instance} instance */ 45 | createExports: (instance) => { 46 | const js = swift.memory.heap; 47 | 48 | return { 49 | check: function bjs_check() { 50 | instance.exports.bjs_check(); 51 | }, 52 | }; 53 | }, 54 | } 55 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.d.ts: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export type Exports = { 8 | } 9 | export type Imports = { 10 | check(): void; 11 | } 12 | export function createInstantiator(options: { 13 | imports: Imports; 14 | }, swift: any): Promise<{ 15 | addImports: (importObject: WebAssembly.Imports) => void; 16 | setInstance: (instance: WebAssembly.Instance) => void; 17 | createExports: (instance: WebAssembly.Instance) => Exports; 18 | }>; -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | export async function createInstantiator(options, swift) { 8 | let instance; 9 | let memory; 10 | const textDecoder = new TextDecoder("utf-8"); 11 | const textEncoder = new TextEncoder("utf-8"); 12 | 13 | let tmpRetString; 14 | let tmpRetBytes; 15 | return { 16 | /** @param {WebAssembly.Imports} importObject */ 17 | addImports: (importObject) => { 18 | const bjs = {}; 19 | importObject["bjs"] = bjs; 20 | bjs["return_string"] = function(ptr, len) { 21 | const bytes = new Uint8Array(memory.buffer, ptr, len); 22 | tmpRetString = textDecoder.decode(bytes); 23 | } 24 | bjs["init_memory"] = function(sourceId, bytesPtr) { 25 | const source = swift.memory.getObject(sourceId); 26 | const bytes = new Uint8Array(memory.buffer, bytesPtr); 27 | bytes.set(source); 28 | } 29 | bjs["make_jsstring"] = function(ptr, len) { 30 | const bytes = new Uint8Array(memory.buffer, ptr, len); 31 | return swift.memory.retain(textDecoder.decode(bytes)); 32 | } 33 | bjs["init_memory_with_result"] = function(ptr, len) { 34 | const target = new Uint8Array(memory.buffer, ptr, len); 35 | target.set(tmpRetBytes); 36 | tmpRetBytes = undefined; 37 | } 38 | const TestModule = importObject["TestModule"] = {}; 39 | TestModule["bjs_check"] = function bjs_check() { 40 | options.imports.check(); 41 | } 42 | }, 43 | setInstance: (i) => { 44 | instance = i; 45 | memory = instance.exports.memory; 46 | }, 47 | /** @param {WebAssembly.Instance} instance */ 48 | createExports: (instance) => { 49 | const js = swift.memory.heap; 50 | 51 | return { 52 | 53 | }; 54 | }, 55 | } 56 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes" : [ 3 | 4 | ], 5 | "functions" : [ 6 | { 7 | "abiName" : "bjs_check", 8 | "name" : "check", 9 | "parameters" : [ 10 | { 11 | "label" : "a", 12 | "name" : "a", 13 | "type" : { 14 | "int" : { 15 | 16 | } 17 | } 18 | }, 19 | { 20 | "label" : "b", 21 | "name" : "b", 22 | "type" : { 23 | "float" : { 24 | 25 | } 26 | } 27 | }, 28 | { 29 | "label" : "c", 30 | "name" : "c", 31 | "type" : { 32 | "double" : { 33 | 34 | } 35 | } 36 | }, 37 | { 38 | "label" : "d", 39 | "name" : "d", 40 | "type" : { 41 | "bool" : { 42 | 43 | } 44 | } 45 | } 46 | ], 47 | "returnType" : { 48 | "void" : { 49 | 50 | } 51 | } 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | @_extern(wasm, module: "bjs", name: "return_string") 7 | private func _return_string(_ ptr: UnsafePointer?, _ len: Int32) 8 | @_extern(wasm, module: "bjs", name: "init_memory") 9 | private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?) 10 | 11 | @_expose(wasm, "bjs_check") 12 | @_cdecl("bjs_check") 13 | public func _bjs_check(a: Int32, b: Float32, c: Float64, d: Int32) -> Void { 14 | check(a: Int(a), b: b, c: c, d: d == 1) 15 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes" : [ 3 | 4 | ], 5 | "functions" : [ 6 | { 7 | "abiName" : "bjs_checkInt", 8 | "name" : "checkInt", 9 | "parameters" : [ 10 | 11 | ], 12 | "returnType" : { 13 | "int" : { 14 | 15 | } 16 | } 17 | }, 18 | { 19 | "abiName" : "bjs_checkFloat", 20 | "name" : "checkFloat", 21 | "parameters" : [ 22 | 23 | ], 24 | "returnType" : { 25 | "float" : { 26 | 27 | } 28 | } 29 | }, 30 | { 31 | "abiName" : "bjs_checkDouble", 32 | "name" : "checkDouble", 33 | "parameters" : [ 34 | 35 | ], 36 | "returnType" : { 37 | "double" : { 38 | 39 | } 40 | } 41 | }, 42 | { 43 | "abiName" : "bjs_checkBool", 44 | "name" : "checkBool", 45 | "parameters" : [ 46 | 47 | ], 48 | "returnType" : { 49 | "bool" : { 50 | 51 | } 52 | } 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | @_extern(wasm, module: "bjs", name: "return_string") 7 | private func _return_string(_ ptr: UnsafePointer?, _ len: Int32) 8 | @_extern(wasm, module: "bjs", name: "init_memory") 9 | private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?) 10 | 11 | @_expose(wasm, "bjs_checkInt") 12 | @_cdecl("bjs_checkInt") 13 | public func _bjs_checkInt() -> Int32 { 14 | let ret = checkInt() 15 | return Int32(ret) 16 | } 17 | 18 | @_expose(wasm, "bjs_checkFloat") 19 | @_cdecl("bjs_checkFloat") 20 | public func _bjs_checkFloat() -> Float32 { 21 | let ret = checkFloat() 22 | return Float32(ret) 23 | } 24 | 25 | @_expose(wasm, "bjs_checkDouble") 26 | @_cdecl("bjs_checkDouble") 27 | public func _bjs_checkDouble() -> Float64 { 28 | let ret = checkDouble() 29 | return Float64(ret) 30 | } 31 | 32 | @_expose(wasm, "bjs_checkBool") 33 | @_cdecl("bjs_checkBool") 34 | public func _bjs_checkBool() -> Int32 { 35 | let ret = checkBool() 36 | return Int32(ret ? 1 : 0) 37 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes" : [ 3 | 4 | ], 5 | "functions" : [ 6 | { 7 | "abiName" : "bjs_checkString", 8 | "name" : "checkString", 9 | "parameters" : [ 10 | { 11 | "label" : "a", 12 | "name" : "a", 13 | "type" : { 14 | "string" : { 15 | 16 | } 17 | } 18 | } 19 | ], 20 | "returnType" : { 21 | "void" : { 22 | 23 | } 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | @_extern(wasm, module: "bjs", name: "return_string") 7 | private func _return_string(_ ptr: UnsafePointer?, _ len: Int32) 8 | @_extern(wasm, module: "bjs", name: "init_memory") 9 | private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?) 10 | 11 | @_expose(wasm, "bjs_checkString") 12 | @_cdecl("bjs_checkString") 13 | public func _bjs_checkString(aBytes: Int32, aLen: Int32) -> Void { 14 | let a = String(unsafeUninitializedCapacity: Int(aLen)) { b in 15 | _init_memory(aBytes, b.baseAddress.unsafelyUnwrapped) 16 | return Int(aLen) 17 | } 18 | checkString(a: a) 19 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes" : [ 3 | 4 | ], 5 | "functions" : [ 6 | { 7 | "abiName" : "bjs_checkString", 8 | "name" : "checkString", 9 | "parameters" : [ 10 | 11 | ], 12 | "returnType" : { 13 | "string" : { 14 | 15 | } 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | @_extern(wasm, module: "bjs", name: "return_string") 7 | private func _return_string(_ ptr: UnsafePointer?, _ len: Int32) 8 | @_extern(wasm, module: "bjs", name: "init_memory") 9 | private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?) 10 | 11 | @_expose(wasm, "bjs_checkString") 12 | @_cdecl("bjs_checkString") 13 | public func _bjs_checkString() -> Void { 14 | var ret = checkString() 15 | return ret.withUTF8 { ptr in 16 | _return_string(ptr.baseAddress, Int32(ptr.count)) 17 | } 18 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes" : [ 3 | { 4 | "constructor" : { 5 | "abiName" : "bjs_Greeter_init", 6 | "parameters" : [ 7 | { 8 | "label" : "name", 9 | "name" : "name", 10 | "type" : { 11 | "string" : { 12 | 13 | } 14 | } 15 | } 16 | ] 17 | }, 18 | "methods" : [ 19 | { 20 | "abiName" : "bjs_Greeter_greet", 21 | "name" : "greet", 22 | "parameters" : [ 23 | 24 | ], 25 | "returnType" : { 26 | "string" : { 27 | 28 | } 29 | } 30 | }, 31 | { 32 | "abiName" : "bjs_Greeter_changeName", 33 | "name" : "changeName", 34 | "parameters" : [ 35 | { 36 | "label" : "name", 37 | "name" : "name", 38 | "type" : { 39 | "string" : { 40 | 41 | } 42 | } 43 | } 44 | ], 45 | "returnType" : { 46 | "void" : { 47 | 48 | } 49 | } 50 | } 51 | ], 52 | "name" : "Greeter" 53 | } 54 | ], 55 | "functions" : [ 56 | { 57 | "abiName" : "bjs_takeGreeter", 58 | "name" : "takeGreeter", 59 | "parameters" : [ 60 | { 61 | "label" : "greeter", 62 | "name" : "greeter", 63 | "type" : { 64 | "swiftHeapObject" : { 65 | "_0" : "Greeter" 66 | } 67 | } 68 | } 69 | ], 70 | "returnType" : { 71 | "void" : { 72 | 73 | } 74 | } 75 | } 76 | ] 77 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | @_extern(wasm, module: "bjs", name: "return_string") 7 | private func _return_string(_ ptr: UnsafePointer?, _ len: Int32) 8 | @_extern(wasm, module: "bjs", name: "init_memory") 9 | private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?) 10 | 11 | @_expose(wasm, "bjs_takeGreeter") 12 | @_cdecl("bjs_takeGreeter") 13 | public func _bjs_takeGreeter(greeter: UnsafeMutableRawPointer) -> Void { 14 | takeGreeter(greeter: Unmanaged.fromOpaque(greeter).takeUnretainedValue()) 15 | } 16 | 17 | @_expose(wasm, "bjs_Greeter_init") 18 | @_cdecl("bjs_Greeter_init") 19 | public func _bjs_Greeter_init(nameBytes: Int32, nameLen: Int32) -> UnsafeMutableRawPointer { 20 | let name = String(unsafeUninitializedCapacity: Int(nameLen)) { b in 21 | _init_memory(nameBytes, b.baseAddress.unsafelyUnwrapped) 22 | return Int(nameLen) 23 | } 24 | let ret = Greeter(name: name) 25 | return Unmanaged.passRetained(ret).toOpaque() 26 | } 27 | 28 | @_expose(wasm, "bjs_Greeter_greet") 29 | @_cdecl("bjs_Greeter_greet") 30 | public func _bjs_Greeter_greet(_self: UnsafeMutableRawPointer) -> Void { 31 | var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().greet() 32 | return ret.withUTF8 { ptr in 33 | _return_string(ptr.baseAddress, Int32(ptr.count)) 34 | } 35 | } 36 | 37 | @_expose(wasm, "bjs_Greeter_changeName") 38 | @_cdecl("bjs_Greeter_changeName") 39 | public func _bjs_Greeter_changeName(_self: UnsafeMutableRawPointer, nameBytes: Int32, nameLen: Int32) -> Void { 40 | let name = String(unsafeUninitializedCapacity: Int(nameLen)) { b in 41 | _init_memory(nameBytes, b.baseAddress.unsafelyUnwrapped) 42 | return Int(nameLen) 43 | } 44 | Unmanaged.fromOpaque(_self).takeUnretainedValue().changeName(name: name) 45 | } 46 | 47 | @_expose(wasm, "bjs_Greeter_deinit") 48 | @_cdecl("bjs_Greeter_deinit") 49 | public func _bjs_Greeter_deinit(pointer: UnsafeMutableRawPointer) { 50 | Unmanaged.fromOpaque(pointer).release() 51 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes" : [ 3 | 4 | ], 5 | "functions" : [ 6 | { 7 | "abiName" : "bjs_check", 8 | "name" : "check", 9 | "parameters" : [ 10 | 11 | ], 12 | "returnType" : { 13 | "void" : { 14 | 15 | } 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | @_extern(wasm, module: "bjs", name: "return_string") 7 | private func _return_string(_ ptr: UnsafePointer?, _ len: Int32) 8 | @_extern(wasm, module: "bjs", name: "init_memory") 9 | private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?) 10 | 11 | @_expose(wasm, "bjs_check") 12 | @_cdecl("bjs_check") 13 | public func _bjs_check() -> Void { 14 | check() 15 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func checkArray(_ a: JSObject) -> Void { 19 | @_extern(wasm, module: "Check", name: "bjs_checkArray") 20 | func bjs_checkArray(_ a: Int32) -> Void 21 | bjs_checkArray(Int32(bitPattern: a.id)) 22 | } 23 | 24 | func checkArrayWithLength(_ a: JSObject, _ b: Double) -> Void { 25 | @_extern(wasm, module: "Check", name: "bjs_checkArrayWithLength") 26 | func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) -> Void 27 | bjs_checkArrayWithLength(Int32(bitPattern: a.id), b) 28 | } 29 | 30 | func checkArray(_ a: JSObject) -> Void { 31 | @_extern(wasm, module: "Check", name: "bjs_checkArray") 32 | func bjs_checkArray(_ a: Int32) -> Void 33 | bjs_checkArray(Int32(bitPattern: a.id)) 34 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func returnAnimatable() -> Animatable { 19 | @_extern(wasm, module: "Check", name: "bjs_returnAnimatable") 20 | func bjs_returnAnimatable() -> Int32 21 | let ret = bjs_returnAnimatable() 22 | return Animatable(takingThis: ret) 23 | } 24 | 25 | struct Animatable { 26 | let this: JSObject 27 | 28 | init(this: JSObject) { 29 | self.this = this 30 | } 31 | 32 | init(takingThis this: Int32) { 33 | self.this = JSObject(id: UInt32(bitPattern: this)) 34 | } 35 | 36 | func animate(_ keyframes: JSObject, _ options: JSObject) -> JSObject { 37 | @_extern(wasm, module: "Check", name: "bjs_Animatable_animate") 38 | func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) -> Int32 39 | let ret = bjs_Animatable_animate(Int32(bitPattern: self.this.id), Int32(bitPattern: keyframes.id), Int32(bitPattern: options.id)) 40 | return JSObject(id: UInt32(bitPattern: ret)) 41 | } 42 | 43 | func getAnimations(_ options: JSObject) -> JSObject { 44 | @_extern(wasm, module: "Check", name: "bjs_Animatable_getAnimations") 45 | func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) -> Int32 46 | let ret = bjs_Animatable_getAnimations(Int32(bitPattern: self.this.id), Int32(bitPattern: options.id)) 47 | return JSObject(id: UInt32(bitPattern: ret)) 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func check(_ a: Double, _ b: Bool) -> Void { 19 | @_extern(wasm, module: "Check", name: "bjs_check") 20 | func bjs_check(_ a: Float64, _ b: Int32) -> Void 21 | bjs_check(a, Int32(b ? 1 : 0)) 22 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func checkNumber() -> Double { 19 | @_extern(wasm, module: "Check", name: "bjs_checkNumber") 20 | func bjs_checkNumber() -> Float64 21 | let ret = bjs_checkNumber() 22 | return Double(ret) 23 | } 24 | 25 | func checkBoolean() -> Bool { 26 | @_extern(wasm, module: "Check", name: "bjs_checkBoolean") 27 | func bjs_checkBoolean() -> Int32 28 | let ret = bjs_checkBoolean() 29 | return ret == 1 30 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func checkString(_ a: String) -> Void { 19 | @_extern(wasm, module: "Check", name: "bjs_checkString") 20 | func bjs_checkString(_ a: Int32) -> Void 21 | var a = a 22 | let aId = a.withUTF8 { b in 23 | _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) 24 | } 25 | bjs_checkString(aId) 26 | } 27 | 28 | func checkStringWithLength(_ a: String, _ b: Double) -> Void { 29 | @_extern(wasm, module: "Check", name: "bjs_checkStringWithLength") 30 | func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) -> Void 31 | var a = a 32 | let aId = a.withUTF8 { b in 33 | _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) 34 | } 35 | bjs_checkStringWithLength(aId, b) 36 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func checkString() -> String { 19 | @_extern(wasm, module: "Check", name: "bjs_checkString") 20 | func bjs_checkString() -> Int32 21 | let ret = bjs_checkString() 22 | return String(unsafeUninitializedCapacity: Int(ret)) { b in 23 | _init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) 24 | return Int(ret) 25 | } 26 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func checkSimple(_ a: Double) -> Void { 19 | @_extern(wasm, module: "Check", name: "bjs_checkSimple") 20 | func bjs_checkSimple(_ a: Float64) -> Void 21 | bjs_checkSimple(a) 22 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | struct Greeter { 19 | let this: JSObject 20 | 21 | init(this: JSObject) { 22 | self.this = this 23 | } 24 | 25 | init(takingThis this: Int32) { 26 | self.this = JSObject(id: UInt32(bitPattern: this)) 27 | } 28 | 29 | init(_ name: String) { 30 | @_extern(wasm, module: "Check", name: "bjs_Greeter_init") 31 | func bjs_Greeter_init(_ name: Int32) -> Int32 32 | var name = name 33 | let nameId = name.withUTF8 { b in 34 | _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) 35 | } 36 | let ret = bjs_Greeter_init(nameId) 37 | self.this = ret 38 | } 39 | 40 | func greet() -> String { 41 | @_extern(wasm, module: "Check", name: "bjs_Greeter_greet") 42 | func bjs_Greeter_greet(_ self: Int32) -> Int32 43 | let ret = bjs_Greeter_greet(Int32(bitPattern: self.this.id)) 44 | return String(unsafeUninitializedCapacity: Int(ret)) { b in 45 | _init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret)) 46 | return Int(ret) 47 | } 48 | } 49 | 50 | func changeName(_ name: String) -> Void { 51 | @_extern(wasm, module: "Check", name: "bjs_Greeter_changeName") 52 | func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) -> Void 53 | var name = name 54 | let nameId = name.withUTF8 { b in 55 | _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count)) 56 | } 57 | bjs_Greeter_changeName(Int32(bitPattern: self.this.id), nameId) 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift: -------------------------------------------------------------------------------- 1 | // NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, 2 | // DO NOT EDIT. 3 | // 4 | // To update this file, just rebuild your project or run 5 | // `swift package bridge-js`. 6 | 7 | @_spi(JSObject_id) import JavaScriptKit 8 | 9 | @_extern(wasm, module: "bjs", name: "make_jsstring") 10 | private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32 11 | 12 | @_extern(wasm, module: "bjs", name: "init_memory_with_result") 13 | private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32) 14 | 15 | @_extern(wasm, module: "bjs", name: "free_jsobject") 16 | private func _free_jsobject(_ ptr: Int32) -> Void 17 | 18 | func check() -> Void { 19 | @_extern(wasm, module: "Check", name: "bjs_check") 20 | func bjs_check() -> Void 21 | bjs_check() 22 | } -------------------------------------------------------------------------------- /Plugins/PackageToJS/Fixtures/ContinuationLeakInTest/SwiftTesting/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Check", 6 | dependencies: [.package(name: "JavaScriptKit", path: "../../../../../")], 7 | targets: [ 8 | .testTarget( 9 | name: "CheckTests", 10 | dependencies: [ 11 | "JavaScriptKit", 12 | .product(name: "JavaScriptEventLoopTestSupport", package: "JavaScriptKit"), 13 | ], 14 | path: "Tests" 15 | ) 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Fixtures/ContinuationLeakInTest/SwiftTesting/Tests/CheckTests.swift: -------------------------------------------------------------------------------- 1 | import Testing 2 | 3 | @Test func never() async throws { 4 | let _: Void = await withUnsafeContinuation { _ in } 5 | } 6 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Fixtures/ContinuationLeakInTest/XCTest/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Check", 6 | dependencies: [.package(name: "JavaScriptKit", path: "../../../../../")], 7 | targets: [ 8 | .testTarget( 9 | name: "CheckTests", 10 | dependencies: [ 11 | "JavaScriptKit", 12 | .product(name: "JavaScriptEventLoopTestSupport", package: "JavaScriptKit"), 13 | ], 14 | path: "Tests" 15 | ) 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Fixtures/ContinuationLeakInTest/XCTest/Tests/CheckTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | final class CheckTests: XCTestCase { 4 | func testNever() async throws { 5 | let _: Void = await withUnsafeContinuation { _ in } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "PackageToJS", 7 | platforms: [.macOS(.v13)], 8 | targets: [ 9 | .target(name: "PackageToJS"), 10 | .testTarget( 11 | name: "PackageToJSTests", 12 | dependencies: ["PackageToJS"], 13 | exclude: ["__Snapshots__"] 14 | ), 15 | ] 16 | ) 17 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/README.md: -------------------------------------------------------------------------------- 1 | # PackageToJS 2 | 3 | A Swift Package Manager plugin that facilitates building and packaging Swift WebAssembly applications for JavaScript environments. 4 | 5 | ## Overview 6 | 7 | PackageToJS is a command plugin for Swift Package Manager that simplifies the process of compiling Swift code to WebAssembly and generating the necessary JavaScript bindings. It's an essential tool for SwiftWasm projects, especially those using JavaScriptKit to interact with JavaScript from Swift. 8 | 9 | ## Features 10 | 11 | - Build WebAssembly file and generate JavaScript wrappers 12 | - Test driver for Swift Testing and XCTest 13 | - Generated JS files can be consumed by JS bundler tools like Vite 14 | 15 | ## Requirements 16 | 17 | - Swift 6.0 or later 18 | - A compatible WebAssembly SDK 19 | 20 | ## Relationship with Carton 21 | 22 | PackageToJS is intended to replace Carton by providing a more integrated solution for building and packaging Swift WebAssembly applications. Unlike Carton, which offers a development server and hot-reloading, PackageToJS focuses solely on compilation and JavaScript wrapper generation. 23 | 24 | ## Internal Architecture 25 | 26 | PackageToJS consists of several components: 27 | - `PackageToJSPlugin.swift`: Main entry point for the Swift Package Manager plugin (Note that this file is not included when running unit tests for the plugin) 28 | - `PackageToJS.swift`: Core functionality for building and packaging 29 | - `MiniMake.swift`: Build system utilities 30 | - `ParseWasm.swift`: WebAssembly binary parsing 31 | - `Preprocess.swift`: Preprocessor for `./Templates` files 32 | 33 | ## Internal Testing 34 | 35 | To run the unit tests for the `PackageToJS` plugin, use the following command: 36 | 37 | ```bash 38 | swift test --package-path ./Plugins/PackageToJS 39 | ``` 40 | 41 | Please define the following environment variables when you want to run E2E tests: 42 | 43 | - `SWIFT_SDK_ID`: Specifies the Swift SDK identifier to use 44 | - `SWIFT_PATH`: Specifies the `bin` path to the Swift toolchain to use 45 | 46 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Sources/BridgeJSLink: -------------------------------------------------------------------------------- 1 | ../../BridgeJS/Sources/BridgeJSLink -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { Exports, Imports, ModuleSource } from './instantiate.js' 2 | 3 | export type Options = { 4 | /** 5 | * The WebAssembly module to instantiate 6 | * 7 | * If not provided, the module will be fetched from the default path. 8 | */ 9 | module?: ModuleSource 10 | /* #if HAS_IMPORTS */ 11 | /** 12 | * The imports to use for the module 13 | */ 14 | imports: Imports 15 | /* #endif */ 16 | } 17 | 18 | /** 19 | * Instantiate and initialize the module 20 | * 21 | * This is a convenience function for browser environments. 22 | * If you need a more flexible API, see `instantiate`. 23 | */ 24 | export declare function init(options?: Options): Promise<{ 25 | instance: WebAssembly.Instance, 26 | exports: Exports 27 | }> 28 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/index.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { instantiate } from './instantiate.js'; 3 | import { defaultBrowserSetup /* #if USE_SHARED_MEMORY */, createDefaultWorkerFactory /* #endif */} from './platforms/browser.js'; 4 | 5 | /** @type {import('./index.d').init} */ 6 | export async function init(_options) { 7 | /** @type {import('./index.d').Options} */ 8 | const options = _options || { 9 | /* #if HAS_IMPORTS */ 10 | /** @returns {import('./instantiate.d').Imports} */ 11 | get imports() { (() => { throw new Error("No imports provided") })() } 12 | /* #endif */ 13 | }; 14 | let module = options.module; 15 | if (!module) { 16 | module = fetch(new URL("@PACKAGE_TO_JS_MODULE_PATH@", import.meta.url)) 17 | } 18 | const instantiateOptions = await defaultBrowserSetup({ 19 | module, 20 | /* #if HAS_IMPORTS */ 21 | imports: options.imports, 22 | /* #endif */ 23 | /* #if USE_SHARED_MEMORY */ 24 | spawnWorker: createDefaultWorkerFactory() 25 | /* #endif */ 26 | }) 27 | return await instantiate(instantiateOptions); 28 | } 29 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@PACKAGE_TO_JS_PACKAGE_NAME@", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "private": true, 6 | "exports": { 7 | ".": "./index.js", 8 | "./wasm": "./@PACKAGE_TO_JS_MODULE_PATH@" 9 | }, 10 | "dependencies": { 11 | "@bjorn3/browser_wasi_shim": "0.3.0" 12 | }, 13 | "peerDependencies": { 14 | "playwright": "^1.51.0" 15 | }, 16 | "peerDependenciesMeta": { 17 | "playwright": { 18 | "optional": true 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/platforms/browser.d.ts: -------------------------------------------------------------------------------- 1 | import type { InstantiateOptions, ModuleSource/* #if HAS_IMPORTS */, Imports/* #endif */ } from "../instantiate.js" 2 | 3 | export function defaultBrowserSetup(options: { 4 | module: ModuleSource, 5 | /* #if IS_WASI */ 6 | args?: string[], 7 | onStdoutLine?: (line: string) => void, 8 | onStderrLine?: (line: string) => void, 9 | /* #endif */ 10 | /* #if HAS_IMPORTS */ 11 | imports: Imports, 12 | /* #endif */ 13 | /* #if USE_SHARED_MEMORY */ 14 | spawnWorker: (module: WebAssembly.Module, memory: WebAssembly.Memory, startArg: any) => Worker, 15 | /* #endif */ 16 | }): Promise 17 | 18 | export function createDefaultWorkerFactory(preludeScript?: string): (module: WebAssembly.Module, memory: WebAssembly.Memory, startArg: any) => Worker 19 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/platforms/browser.worker.js: -------------------------------------------------------------------------------- 1 | import { instantiateForThread } from "../instantiate.js" 2 | import { defaultBrowserThreadSetup } from "./browser.js" 3 | 4 | self.onmessage = async (event) => { 5 | const { module, memory, tid, startArg, preludeScript } = event.data; 6 | let options = await defaultBrowserThreadSetup(); 7 | if (preludeScript) { 8 | const prelude = await import(preludeScript); 9 | if (prelude.setupOptions) { 10 | options = prelude.setupOptions(options, { isMainThread: false }) 11 | } 12 | } 13 | await instantiateForThread(tid, startArg, { 14 | ...options, 15 | module, memory, 16 | imports: {}, 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/platforms/node.d.ts: -------------------------------------------------------------------------------- 1 | import type { InstantiateOptions } from "../instantiate.js" 2 | import type { Worker } from "node:worker_threads" 3 | 4 | export function defaultNodeSetup(options: { 5 | /* #if IS_WASI */ 6 | args?: string[], 7 | /* #endif */ 8 | onExit?: (code: number) => void, 9 | /* #if USE_SHARED_MEMORY */ 10 | spawnWorker: (module: WebAssembly.Module, memory: WebAssembly.Memory, startArg: any) => Worker, 11 | /* #endif */ 12 | }): Promise 13 | 14 | export function createDefaultWorkerFactory(preludeScript: string): (module: WebAssembly.Module, memory: WebAssembly.Memory, startArg: any) => Worker 15 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/test.browser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/test.d.ts: -------------------------------------------------------------------------------- 1 | import type { InstantiateOptions, instantiate } from "./instantiate"; 2 | 3 | export function testBrowser( 4 | options: { 5 | preludeScript?: string, 6 | args?: string[], 7 | } 8 | ): Promise 9 | 10 | export function testBrowserInPage( 11 | options: InstantiateOptions 12 | ): ReturnType 13 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Templates/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "noEmit": true, 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "moduleResolution": "node" 8 | }, 9 | "include": ["**/*.d.ts", "**/*.js"] 10 | } 11 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Tests/SnapshotTesting.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Testing 3 | 4 | func assertSnapshot( 5 | filePath: String = #filePath, 6 | function: String = #function, 7 | sourceLocation: SourceLocation = #_sourceLocation, 8 | variant: String? = nil, 9 | input: Data, 10 | fileExtension: String = "json" 11 | ) throws { 12 | let testFileName = URL(fileURLWithPath: filePath).deletingPathExtension().lastPathComponent 13 | let snapshotDir = URL(fileURLWithPath: filePath) 14 | .deletingLastPathComponent() 15 | .appendingPathComponent("__Snapshots__") 16 | .appendingPathComponent(testFileName) 17 | try FileManager.default.createDirectory(at: snapshotDir, withIntermediateDirectories: true) 18 | let snapshotFileName: String = 19 | "\(function[.. Comment { 27 | "Snapshot mismatch: \(actualFilePath) \(snapshotPath.path)" 28 | } 29 | if !ok { 30 | try input.write(to: URL(fileURLWithPath: actualFilePath)) 31 | } 32 | #expect(ok, buildComment(), sourceLocation: sourceLocation) 33 | } else { 34 | try input.write(to: snapshotPath) 35 | #expect(Bool(false), "Snapshot created at \(snapshotPath.path)", sourceLocation: sourceLocation) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Tests/TemplatesTests.swift: -------------------------------------------------------------------------------- 1 | import Testing 2 | import Foundation 3 | @testable import PackageToJS 4 | 5 | @Suite struct TemplatesTests { 6 | static let templatesPath = URL(fileURLWithPath: #filePath) 7 | .deletingLastPathComponent() 8 | .deletingLastPathComponent() 9 | .appendingPathComponent("Templates") 10 | 11 | /// `npx tsc -p Templates/tsconfig.json` 12 | @Test func tscCheck() throws { 13 | let tsc = Process() 14 | tsc.executableURL = try which("npx") 15 | tsc.arguments = ["tsc", "-p", Self.templatesPath.appending(path: "tsconfig.json").path] 16 | try tsc.run() 17 | tsc.waitUntilExit() 18 | #expect(tsc.terminationStatus == 0) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Tests/TemporaryDirectory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct MakeTemporaryDirectoryError: Error { 4 | let error: CInt 5 | } 6 | 7 | internal func withTemporaryDirectory(body: (URL, _ retain: inout Bool) throws -> T) throws -> T { 8 | // Create a temporary directory using mkdtemp 9 | var template = FileManager.default.temporaryDirectory.appendingPathComponent("PackageToJSTests.XXXXXX").path 10 | return try template.withUTF8 { template in 11 | let copy = UnsafeMutableBufferPointer.allocate(capacity: template.count + 1) 12 | template.copyBytes(to: copy) 13 | copy[template.count] = 0 14 | 15 | guard let result = mkdtemp(copy.baseAddress!) else { 16 | throw MakeTemporaryDirectoryError(error: errno) 17 | } 18 | let tempDir = URL(fileURLWithPath: String(cString: result)) 19 | var retain = false 20 | defer { 21 | if !retain { 22 | try? FileManager.default.removeItem(at: tempDir) 23 | } 24 | } 25 | return try body(tempDir, &retain) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Tests/__Snapshots__/TestsParserTests/testAllPassed.txt: -------------------------------------------------------------------------------- 1 |  PASSED  CounterTests 2 | ✔ testIncrement (0.002s) 3 | ✔ testIncrementTwice (0.001s) 4 |  PASSED  /.xctest 5 |  PASSED  All tests 6 | 7 | Test Suites: 1 passed, 1 total 8 | Tests: 2 passed, 2 total 9 | Ran all test suites. 10 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Tests/__Snapshots__/TestsParserTests/testAssertFailure.txt: -------------------------------------------------------------------------------- 1 | /tmp/Tests/CounterTests/CounterTests.swift:27: error: CounterTests.testAssertailure : XCTAssertEqual failed: ("1") is not equal to ("2") - 2 |  FAILED  CounterTests 3 | ✘ testAssertailure (0.001s) 4 |  FAILED  /.xctest 5 |  FAILED  All tests 6 | 7 | Test Suites: 1 failed, 1 total 8 | Tests: 1 failed, 1 total 9 | Ran all test suites. 10 | 11 | Failed test cases: 12 | ✘ CounterTests.testAssertailure 13 | 14 | Some tests failed. Use --verbose for raw test output. 15 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Tests/__Snapshots__/TestsParserTests/testCrash.txt: -------------------------------------------------------------------------------- 1 | CounterTests/CounterTests.swift:26: Fatal error: Crash 2 | wasm://wasm/CounterPackageTests.xctest-0ef3150a:1 3 | RuntimeError: unreachable 4 | at CounterPackageTests.xctest.$ss17_assertionFailure__4file4line5flagss5NeverOs12StaticStringV_SSAHSus6UInt32VtF (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[5087]:0x1475da) 5 | at CounterPackageTests.xctest.$s12CounterTestsAAC13testIncrementyyYaKFTY1_ (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[1448]:0x9a33b) 6 | at CounterPackageTests.xctest.swift::runJobInEstablishedExecutorContext(swift::Job*) (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[29848]:0x58cb39) 7 | at CounterPackageTests.xctest.swift_job_run (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[29863]:0x58d720) 8 | at CounterPackageTests.xctest.$sScJ16runSynchronously2onySce_tF (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[1571]:0x9fe5a) 9 | at CounterPackageTests.xctest.$s19JavaScriptEventLoopAAC10runAllJobsyyF (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[1675]:0xa32c4) 10 | at CounterPackageTests.xctest.$s19JavaScriptEventLoopAAC14insertJobQueue3jobyScJ_tFyycfU0_ (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[1674]:0xa30b7) 11 | at CounterPackageTests.xctest.$s19JavaScriptEventLoopAAC14insertJobQueue3jobyScJ_tFyycfU0_TA (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[1666]:0xa2c6b) 12 | at CounterPackageTests.xctest.$s19JavaScriptEventLoopAAC6create33_F9DB15AFB1FFBEDBFE9D13500E01F3F2LLAByFZyyyccfU0_0aB3Kit20ConvertibleToJSValue_pAE0Q0OcfU_ (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[1541]:0x9de13) 13 | at CounterPackageTests.xctest.$s19JavaScriptEventLoopAAC6create33_F9DB15AFB1FFBEDBFE9D13500E01F3F2LLAByFZyyyccfU0_0aB3Kit20ConvertibleToJSValue_pAE0Q0OcfU_TA (wasm://wasm/CounterPackageTests.xctest-0ef3150a:wasm-function[1540]:0x9dd8d) 14 | 15 | Test Suites: 1 unknown, 1 total 16 | Tests: 1 unknown, 1 total 17 | Ran all test suites. 18 | 19 | Failed test cases: 20 | ? CounterTests.testIncrement 21 | 22 | Some tests failed. Use --verbose for raw test output. 23 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Tests/__Snapshots__/TestsParserTests/testSkipped.txt: -------------------------------------------------------------------------------- 1 | /tmp/Tests/CounterTests/CounterTests.swift:25: CounterTests.testIncrement : Test skipped - Skip it 2 |  PASSED  CounterTests 3 | ➜ testIncrement (0.006s) 4 | ✔ testIncrementTwice (0.0s) 5 |  PASSED  /.xctest 6 |  PASSED  All tests 7 | 8 | Test Suites: 1 passed, 1 total 9 | Tests: 1 passed, 1 skipped, 2 total 10 | Ran all test suites. 11 | -------------------------------------------------------------------------------- /Plugins/PackageToJS/Tests/__Snapshots__/TestsParserTests/testThrowFailure.txt: -------------------------------------------------------------------------------- 1 | :0: error: CounterTests.testThrowFailure : threw error "TestError()" 2 |  FAILED  CounterTests 3 | ✘ testThrowFailure (0.002s) 4 |  FAILED  /.xctest 5 |  FAILED  All tests 6 | 7 | Test Suites: 1 failed, 1 total 8 | Tests: 1 failed, 1 total 9 | Ran all test suites. 10 | 11 | Failed test cases: 12 | ✘ CounterTests.testThrowFailure 13 | 14 | Some tests failed. Use --verbose for raw test output. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScriptKit 2 | 3 | [![Run unit tests](https://github.com/swiftwasm/JavaScriptKit/actions/workflows/test.yml/badge.svg)](https://github.com/swiftwasm/JavaScriptKit/actions/workflows/test.yml) 4 | [![](https://img.shields.io/badge/docc-read_documentation-blue)](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/documentation) 5 | 6 | Swift framework to interact with JavaScript through WebAssembly. 7 | 8 | ## Quick Start 9 | 10 | Check out the [Hello World](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/tutorials/javascriptkit/hello-world) tutorial for a step-by-step guide to getting started. 11 | 12 | ## Overview 13 | 14 | JavaScriptKit provides a seamless way to interact with JavaScript from Swift code when compiled to WebAssembly. It allows Swift developers to: 15 | 16 | - Access JavaScript objects and functions 17 | - Create closures that can be called from JavaScript 18 | - Convert between Swift and JavaScript data types 19 | - Use JavaScript promises with Swift's `async/await` 20 | - Work with multi-threading 21 | 22 | ```swift 23 | import JavaScriptKit 24 | 25 | // Access global JavaScript objects 26 | let document = JSObject.global.document 27 | 28 | // Create and manipulate DOM elements 29 | var div = document.createElement("div") 30 | div.innerText = "Hello from Swift!" 31 | _ = document.body.appendChild(div) 32 | 33 | // Handle events with Swift closures 34 | var button = document.createElement("button") 35 | button.innerText = "Click me" 36 | button.onclick = .object(JSClosure { _ in 37 | JSObject.global.alert!("Button clicked!") 38 | return .undefined 39 | }) 40 | _ = document.body.appendChild(button) 41 | ``` 42 | 43 | Check out the [examples](https://github.com/swiftwasm/JavaScriptKit/tree/main/Examples) for more detailed usage. 44 | 45 | ## Contributing 46 | 47 | Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to the project. 48 | 49 | ## Sponsoring 50 | 51 | [Become a gold or platinum sponsor](https://github.com/sponsors/swiftwasm/) and contact maintainers to add your logo on our README on Github with a link to your site. 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Runtime/.gitignore: -------------------------------------------------------------------------------- 1 | /lib 2 | /node_modules -------------------------------------------------------------------------------- /Runtime/.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/JavaScriptKit/8563ff73cf05e97ba4a2be36f713aca8d8fd7974/Runtime/.npmignore -------------------------------------------------------------------------------- /Runtime/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import typescript from "@rollup/plugin-typescript"; 2 | import dts from "rollup-plugin-dts"; 3 | 4 | /** @type {import('rollup').RollupOptions} */ 5 | const config = [ 6 | { 7 | input: "src/index.ts", 8 | output: [ 9 | { 10 | file: "lib/index.mjs", 11 | format: "esm", 12 | }, 13 | ], 14 | plugins: [typescript()], 15 | }, 16 | { 17 | input: "src/index.ts", 18 | output: { 19 | file: "lib/index.d.ts", 20 | format: "esm", 21 | }, 22 | plugins: [dts()], 23 | }, 24 | ]; 25 | 26 | export default config; 27 | -------------------------------------------------------------------------------- /Runtime/src/closure-heap.ts: -------------------------------------------------------------------------------- 1 | import { ExportedFunctions } from "./types.js"; 2 | 3 | /// Memory lifetime of closures in Swift are managed by Swift side 4 | export class SwiftClosureDeallocator { 5 | private functionRegistry: FinalizationRegistry; 6 | 7 | constructor(exports: ExportedFunctions) { 8 | if (typeof FinalizationRegistry === "undefined") { 9 | throw new Error( 10 | "The Swift part of JavaScriptKit was configured to require " + 11 | "the availability of JavaScript WeakRefs. Please build " + 12 | "with `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` to " + 13 | "disable features that use WeakRefs." 14 | ); 15 | } 16 | 17 | this.functionRegistry = new FinalizationRegistry((id) => { 18 | exports.swjs_free_host_function(id); 19 | }); 20 | } 21 | 22 | track(func: Function, func_ref: number) { 23 | this.functionRegistry.register(func, func_ref); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Runtime/src/find-global.ts: -------------------------------------------------------------------------------- 1 | interface GlobalVariable {} 2 | declare const global: GlobalVariable; 3 | 4 | export let globalVariable: any; 5 | if (typeof globalThis !== "undefined") { 6 | globalVariable = globalThis; 7 | } else if (typeof window !== "undefined") { 8 | globalVariable = window; 9 | } else if (typeof global !== "undefined") { 10 | globalVariable = global; 11 | } else if (typeof self !== "undefined") { 12 | globalVariable = self; 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/src/memory.ts: -------------------------------------------------------------------------------- 1 | import { SwiftRuntimeHeap } from "./object-heap.js"; 2 | import { pointer } from "./types.js"; 3 | 4 | export class Memory { 5 | readonly rawMemory: WebAssembly.Memory; 6 | 7 | private readonly heap = new SwiftRuntimeHeap(); 8 | 9 | constructor(exports: WebAssembly.Exports) { 10 | this.rawMemory = exports.memory as WebAssembly.Memory; 11 | } 12 | 13 | retain = (value: any) => this.heap.retain(value); 14 | getObject = (ref: number) => this.heap.referenceHeap(ref); 15 | release = (ref: number) => this.heap.release(ref); 16 | 17 | bytes = () => new Uint8Array(this.rawMemory.buffer); 18 | dataView = () => new DataView(this.rawMemory.buffer); 19 | 20 | writeBytes = (ptr: pointer, bytes: Uint8Array) => 21 | this.bytes().set(bytes, ptr); 22 | 23 | readUint32 = (ptr: pointer) => this.dataView().getUint32(ptr, true); 24 | readUint64 = (ptr: pointer) => this.dataView().getBigUint64(ptr, true); 25 | readInt64 = (ptr: pointer) => this.dataView().getBigInt64(ptr, true); 26 | readFloat64 = (ptr: pointer) => this.dataView().getFloat64(ptr, true); 27 | 28 | writeUint32 = (ptr: pointer, value: number) => 29 | this.dataView().setUint32(ptr, value, true); 30 | writeUint64 = (ptr: pointer, value: bigint) => 31 | this.dataView().setBigUint64(ptr, value, true); 32 | writeInt64 = (ptr: pointer, value: bigint) => 33 | this.dataView().setBigInt64(ptr, value, true); 34 | writeFloat64 = (ptr: pointer, value: number) => 35 | this.dataView().setFloat64(ptr, value, true); 36 | } 37 | -------------------------------------------------------------------------------- /Runtime/src/object-heap.ts: -------------------------------------------------------------------------------- 1 | import { globalVariable } from "./find-global.js"; 2 | import { ref } from "./types.js"; 3 | 4 | type SwiftRuntimeHeapEntry = { 5 | id: number; 6 | rc: number; 7 | }; 8 | export class SwiftRuntimeHeap { 9 | private _heapValueById: Map; 10 | private _heapEntryByValue: Map; 11 | private _heapNextKey: number; 12 | 13 | constructor() { 14 | this._heapValueById = new Map(); 15 | this._heapValueById.set(0, globalVariable); 16 | 17 | this._heapEntryByValue = new Map(); 18 | this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 }); 19 | 20 | // Note: 0 is preserved for global 21 | this._heapNextKey = 1; 22 | } 23 | 24 | retain(value: any) { 25 | const entry = this._heapEntryByValue.get(value); 26 | if (entry) { 27 | entry.rc++; 28 | return entry.id; 29 | } 30 | const id = this._heapNextKey++; 31 | this._heapValueById.set(id, value); 32 | this._heapEntryByValue.set(value, { id: id, rc: 1 }); 33 | return id; 34 | } 35 | 36 | release(ref: ref) { 37 | const value = this._heapValueById.get(ref); 38 | const entry = this._heapEntryByValue.get(value)!; 39 | entry.rc--; 40 | if (entry.rc != 0) return; 41 | 42 | this._heapEntryByValue.delete(value); 43 | this._heapValueById.delete(ref); 44 | } 45 | 46 | referenceHeap(ref: ref) { 47 | const value = this._heapValueById.get(ref); 48 | if (value === undefined) { 49 | throw new ReferenceError( 50 | "Attempted to read invalid reference " + ref 51 | ); 52 | } 53 | return value; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Runtime/src/types.ts: -------------------------------------------------------------------------------- 1 | export type ref = number; 2 | export type pointer = number; 3 | export type bool = number; 4 | export type JavaScriptValueKind = number; 5 | export type JavaScriptValueKindAndFlags = number; 6 | 7 | export interface ExportedFunctions { 8 | swjs_library_version(): number; 9 | swjs_library_features(): number; 10 | swjs_prepare_host_function_call(size: number): pointer; 11 | swjs_cleanup_host_function_call(argv: pointer): void; 12 | swjs_call_host_function( 13 | host_func_id: number, 14 | argv: pointer, 15 | argc: number, 16 | callback_func_ref: ref 17 | ): bool; 18 | 19 | swjs_free_host_function(host_func_id: number): void; 20 | 21 | swjs_enqueue_main_job_from_worker(unowned_job: number): void; 22 | swjs_wake_worker_thread(): void; 23 | swjs_receive_response(object: ref, transferring: pointer): void; 24 | swjs_receive_error(error: ref, context: number): void; 25 | } 26 | 27 | export const enum LibraryFeatures { 28 | WeakRefs = 1 << 0, 29 | } 30 | 31 | export function assertNever(x: never, message: string) { 32 | throw new Error(message); 33 | } 34 | 35 | export const MAIN_THREAD_TID = -1; 36 | -------------------------------------------------------------------------------- /Runtime/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": false, 4 | "importHelpers": true, 5 | "module": "esnext", 6 | "noEmit": true, 7 | "rootDir": "src", 8 | "strict": true, 9 | "target": "es2017", 10 | "lib": ["es2020", "DOM", "ESNext.WeakRef"], 11 | "skipLibCheck": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules"] 15 | } 16 | -------------------------------------------------------------------------------- /Sources/JavaScriptBigIntSupport/Int64+I64.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | 3 | extension UInt64: JavaScriptKit.ConvertibleToJSValue, JavaScriptKit.TypedArrayElement { 4 | public static var typedArrayClass: JSFunction { JSObject.global.BigUint64Array.function! } 5 | 6 | public var jsValue: JSValue { .bigInt(JSBigInt(unsigned: self)) } 7 | } 8 | 9 | extension Int64: JavaScriptKit.ConvertibleToJSValue, JavaScriptKit.TypedArrayElement { 10 | public static var typedArrayClass: JSFunction { JSObject.global.BigInt64Array.function! } 11 | 12 | public var jsValue: JSValue { .bigInt(JSBigInt(self)) } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/JavaScriptBigIntSupport/JSBigInt+I64.swift: -------------------------------------------------------------------------------- 1 | @_spi(JSObject_id) import JavaScriptKit 2 | import _CJavaScriptBigIntSupport 3 | 4 | extension JSBigInt: JavaScriptKit.JSBigIntExtended { 5 | public var int64Value: Int64 { 6 | swjs_bigint_to_i64(id, true) 7 | } 8 | 9 | public var uInt64Value: UInt64 { 10 | UInt64(bitPattern: swjs_bigint_to_i64(id, false)) 11 | } 12 | 13 | public convenience init(_ value: Int64) { 14 | self.init(id: swjs_i64_to_bigint(value, true)) 15 | } 16 | 17 | public convenience init(unsigned value: UInt64) { 18 | self.init(id: swjs_i64_to_bigint(Int64(bitPattern: value), false)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/JavaScriptEventLoop/WebWorkerDedicatedExecutor.swift: -------------------------------------------------------------------------------- 1 | #if !hasFeature(Embedded) 2 | import JavaScriptKit 3 | import _CJavaScriptEventLoop 4 | import _Concurrency 5 | 6 | #if canImport(Synchronization) 7 | import Synchronization 8 | #endif 9 | #if canImport(wasi_pthread) 10 | import wasi_pthread 11 | import WASILibc 12 | #endif 13 | 14 | /// A serial executor that runs on a dedicated web worker thread. 15 | /// 16 | /// This executor is useful for running actors on a dedicated web worker thread. 17 | /// 18 | /// ## Usage 19 | /// 20 | /// ```swift 21 | /// actor MyActor { 22 | /// let executor: WebWorkerDedicatedExecutor 23 | /// nonisolated var unownedExecutor: UnownedSerialExecutor { 24 | /// self.executor.asUnownedSerialExecutor() 25 | /// } 26 | /// init(executor: WebWorkerDedicatedExecutor) { 27 | /// self.executor = executor 28 | /// } 29 | /// } 30 | /// 31 | /// let executor = try await WebWorkerDedicatedExecutor() 32 | /// let actor = MyActor(executor: executor) 33 | /// ``` 34 | /// 35 | /// - SeeAlso: ``WebWorkerTaskExecutor`` 36 | @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) 37 | public final class WebWorkerDedicatedExecutor: SerialExecutor, TaskExecutor { 38 | 39 | private let underlying: WebWorkerTaskExecutor 40 | 41 | /// - Parameters: 42 | /// - timeout: The maximum time to wait for all worker threads to be started. Default is 3 seconds. 43 | /// - checkInterval: The interval to check if all worker threads are started. Default is 5 microseconds. 44 | /// - Throws: An error if any worker thread fails to initialize within the timeout period. 45 | public init(timeout: Duration = .seconds(3), checkInterval: Duration = .microseconds(5)) async throws { 46 | let underlying = try await WebWorkerTaskExecutor( 47 | numberOfThreads: 1, 48 | timeout: timeout, 49 | checkInterval: checkInterval 50 | ) 51 | self.underlying = underlying 52 | } 53 | 54 | /// Terminates the worker thread. 55 | public func terminate() { 56 | self.underlying.terminate() 57 | } 58 | 59 | // MARK: - SerialExecutor conformance 60 | 61 | public func enqueue(_ job: consuming ExecutorJob) { 62 | self.underlying.enqueue(job) 63 | } 64 | } 65 | #endif 66 | -------------------------------------------------------------------------------- /Sources/JavaScriptEventLoopTestSupport/JavaScriptEventLoopTestSupport.swift: -------------------------------------------------------------------------------- 1 | /// If you need to execute Swift async functions that can be resumed by JS 2 | /// event loop in your XCTest suites, please add `JavaScriptEventLoopTestSupport` 3 | /// to your test target dependencies. 4 | /// 5 | /// ```diff 6 | /// .testTarget( 7 | /// name: "MyAppTests", 8 | /// dependencies: [ 9 | /// "MyApp", 10 | /// + "JavaScriptEventLoopTestSupport", 11 | /// ] 12 | /// ) 13 | /// ``` 14 | /// 15 | /// Linking this module automatically activates JS event loop based global 16 | /// executor by calling `JavaScriptEventLoop.installGlobalExecutor()` 17 | 18 | import JavaScriptEventLoop 19 | 20 | // This module just expose 'JavaScriptEventLoop.installGlobalExecutor' to C ABI 21 | // See _CJavaScriptEventLoopTestSupport.c for why this is needed 22 | 23 | #if compiler(>=5.5) 24 | 25 | @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) 26 | @_cdecl("swift_javascriptkit_activate_js_executor_impl") 27 | func swift_javascriptkit_activate_js_executor_impl() { 28 | MainActor.assumeIsolated { 29 | JavaScriptEventLoop.installGlobalExecutor() 30 | } 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/BasicObjects/JSError.swift: -------------------------------------------------------------------------------- 1 | /// A wrapper around [the JavaScript `Error` 2 | /// class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) that 3 | /// exposes its properties in a type-safe way. 4 | public final class JSError: JSBridgedClass { 5 | /// The constructor function used to create new JavaScript `Error` objects. 6 | public static var constructor: JSFunction? { _constructor.wrappedValue } 7 | private static let _constructor = LazyThreadLocal(initialize: { JSObject.global.Error.function }) 8 | 9 | /// The underlying JavaScript `Error` object. 10 | public let jsObject: JSObject 11 | 12 | /// Creates a new instance of the JavaScript `Error` class with a given message. 13 | public init(message: String) { 14 | jsObject = Self.constructor!.new([message]) 15 | } 16 | 17 | public init(unsafelyWrapping jsObject: JSObject) { 18 | self.jsObject = jsObject 19 | } 20 | 21 | /// The error message of the underlying `Error` object. 22 | public var message: String { 23 | jsObject.message.string! 24 | } 25 | 26 | /// The name (usually corresponds to the name of the underlying class) of a given error. 27 | public var name: String { 28 | jsObject.name.string! 29 | } 30 | 31 | /// The JavaScript call stack that led to the creation of this error object. 32 | public var stack: String? { 33 | jsObject.stack.string 34 | } 35 | 36 | /// Creates a new `JSValue` from this `JSError` instance. 37 | public var jsValue: JSValue { 38 | .object(jsObject) 39 | } 40 | } 41 | 42 | extension JSError: CustomStringConvertible { 43 | /// The textual representation of this error. 44 | public var description: String { jsObject.description } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Deprecated.swift: -------------------------------------------------------------------------------- 1 | @available(*, deprecated, renamed: "JSObject") 2 | public typealias JSObjectRef = JSObject 3 | 4 | @available(*, deprecated, renamed: "JSArray") 5 | public typealias JSArrayRef = JSArray 6 | 7 | @available(*, deprecated, renamed: "JSFunction") 8 | public typealias JSFunctionRef = JSFunction 9 | 10 | @available(*, deprecated, renamed: "ConvertibleToJSValue") 11 | public typealias JSValueConvertible = ConvertibleToJSValue 12 | 13 | @available(*, deprecated, renamed: "ConstructibleFromJSValue") 14 | public typealias JSValueConstructible = ConstructibleFromJSValue 15 | 16 | @available(*, deprecated, renamed: "JSValueCompatible") 17 | public typealias JSValueCodable = JSValueCompatible 18 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Articles/JavaScript-Environment-Requirements.md: -------------------------------------------------------------------------------- 1 | # JavaScript Environment Requirements 2 | 3 | ## Required JavaScript Features 4 | 5 | The JavaScript package produced by the JavaScriptKit packaging plugin requires the following JavaScript features: 6 | 7 | - [`FinalizationRegistry`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry#browser_compatibility) 8 | - [WebAssembly BigInt to i64 conversion in JS API](https://caniuse.com/wasm-bigint) 9 | 10 | ## Browser Compatibility 11 | 12 | These JavaScript features are supported in the following browsers: 13 | 14 | - Chrome 85+ (August 2020) 15 | - Firefox 79+ (July 2020) 16 | - Desktop Safari 14.1+ (April 2021) 17 | - Mobile Safari 14.5+ (April 2021) 18 | - Edge 85+ (August 2020) 19 | - Node.js 15.0+ (October 2020) 20 | 21 | Older browsers will not be able to run applications built with JavaScriptKit unless polyfills are provided. 22 | 23 | ## Handling Missing Features 24 | 25 | ### FinalizationRegistry 26 | 27 | When using JavaScriptKit in environments without `FinalizationRegistry` support, you can: 28 | 29 | 1. Build with the opt-out flag: `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` 30 | 2. Then manually manage memory by calling `release()` on all `JSClosure` instances: 31 | 32 | ```swift 33 | let closure = JSClosure { args in 34 | // Your code here 35 | return .undefined 36 | } 37 | 38 | // Use the closure... 39 | 40 | // Then release it when done 41 | #if JAVASCRIPTKIT_WITHOUT_WEAKREFS 42 | closure.release() 43 | #endif 44 | ``` 45 | 46 | ### WebAssembly BigInt Support 47 | 48 | If you need to work with 64-bit integers in JavaScript, ensure your target environment supports WebAssembly BigInt conversions. For environments that don't support this feature, you'll need to avoid importing `JavaScriptBigIntSupport` 49 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Documentation.md: -------------------------------------------------------------------------------- 1 | # ``JavaScriptKit`` 2 | 3 | Swift framework to interact with JavaScript through WebAssembly. 4 | 5 | ## Overview 6 | 7 | **JavaScriptKit** provides a seamless way to interact with JavaScript from Swift code when compiled to WebAssembly. 8 | 9 | ## Quick Start 10 | 11 | Check out the tutorial for a step-by-step guide to getting started. 12 | 13 | ### Key Features 14 | 15 | - Access JavaScript objects and functions 16 | - Create closures that can be called from JavaScript 17 | - Convert between Swift and JavaScript data types 18 | - Use JavaScript promises with Swift's `async/await` 19 | - Work with multi-threading 20 | 21 | ### Example 22 | 23 | ```swift 24 | import JavaScriptKit 25 | 26 | // Access global JavaScript objects 27 | let document = JSObject.global.document 28 | 29 | // Create and manipulate DOM elements 30 | var div = document.createElement("div") 31 | div.innerText = "Hello from Swift!" 32 | _ = document.body.appendChild(div) 33 | 34 | // Handle events with Swift closures 35 | var button = document.createElement("button") 36 | button.innerText = "Click me" 37 | button.onclick = .object(JSClosure { _ in 38 | JSObject.global.alert!("Button clicked!") 39 | return .undefined 40 | }) 41 | _ = document.body.appendChild(button) 42 | ``` 43 | 44 | Check out the [examples](https://github.com/swiftwasm/JavaScriptKit/tree/main/Examples) for more detailed usage. 45 | 46 | ## Topics 47 | 48 | ### Tutorials 49 | 50 | - 51 | 52 | ### Articles 53 | 54 | - 55 | - 56 | - 57 | - 58 | - 59 | 60 | ### Core APIs 61 | 62 | - ``JSValue`` 63 | - ``JSObject`` 64 | - ``JS()`` 65 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-0-1-swift-version.txt: -------------------------------------------------------------------------------- 1 | $ swift --version 2 | Apple Swift version 6.0.3 (swift-6.0.3-RELEASE) 3 | or 4 | Swift version 6.0.3 (swift-6.0.3-RELEASE) 5 | 6 | $ swift sdk list 7 | 6.0.3-RELEASE-wasm32-unknown-wasi 8 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-0-2-select-sdk.txt: -------------------------------------------------------------------------------- 1 | $ swift --version 2 | Apple Swift version 6.0.3 (swift-6.0.3-RELEASE) 3 | or 4 | Swift version 6.0.3 (swift-6.0.3-RELEASE) 5 | 6 | $ swift sdk list 7 | 6.0.3-RELEASE-wasm32-unknown-wasi 8 | 9 | $ export SWIFT_SDK_ID=6.0.3-RELEASE-wasm32-unknown-wasi 10 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-1-init-package.txt: -------------------------------------------------------------------------------- 1 | $ swift package init --name Hello --type executable 2 | Creating executable package: Hello 3 | Creating Package.swift 4 | Creating .gitignore 5 | Creating Sources/ 6 | Creating Sources/main.swift 7 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-2-add-dependency.txt: -------------------------------------------------------------------------------- 1 | $ swift package init --name Hello --type executable 2 | Creating executable package: Hello 3 | Creating Package.swift 4 | Creating .gitignore 5 | Creating Sources/ 6 | Creating Sources/main.swift 7 | 8 | $ swift package add-dependency https://github.com/swiftwasm/JavaScriptKit.git --branch main 9 | Updating package manifest at Package.swift... done. 10 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-3-add-target-dependency.txt: -------------------------------------------------------------------------------- 1 | $ swift package init --name Hello --type executable 2 | Creating executable package: Hello 3 | Creating Package.swift 4 | Creating .gitignore 5 | Creating Sources/ 6 | Creating Sources/main.swift 7 | 8 | $ swift package add-dependency https://github.com/swiftwasm/JavaScriptKit.git --branch main 9 | Updating package manifest at Package.swift... done. 10 | 11 | $ swift package add-target-dependency --package JavaScriptKit JavaScriptKit Hello 12 | Updating package manifest at Package.swift... done. 13 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-1-main-swift.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | 3 | let document = JSObject.global.document 4 | var div = document.createElement("div") 5 | div.innerText = "Hello from Swift!" 6 | document.body.appendChild(div) 7 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-2-index-html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swift Web App 6 | 10 | 11 | 12 |

My Swift Web App

13 | 14 | 15 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-1-build.txt: -------------------------------------------------------------------------------- 1 | $ swift package --swift-sdk $SWIFT_SDK_ID js --use-cdn 2 | [37/37] Linking Hello.wasm 3 | Build of product 'Hello' complete! (5.16s) 4 | Packaging... 5 | ... 6 | Packaging finished 7 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-2-server.txt: -------------------------------------------------------------------------------- 1 | $ swift package --swift-sdk $SWIFT_SDK_ID js --use-cdn 2 | [37/37] Linking Hello.wasm 3 | Build of product 'Hello' complete! (5.16s) 4 | Packaging... 5 | ... 6 | Packaging finished 7 | $ npx serve 8 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/JavaScriptKit/8563ff73cf05e97ba4a2be36f713aca8d8fd7974/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-app.png -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-open.txt: -------------------------------------------------------------------------------- 1 | $ swift package --swift-sdk $SWIFT_SDK_ID js --use-cdn 2 | [37/37] Linking Hello.wasm 3 | Build of product 'Hello' complete! (5.16s) 4 | Packaging... 5 | ... 6 | Packaging finished 7 | $ npx serve 8 | $ open http://localhost:3000 9 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Resources/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/JavaScriptKit/8563ff73cf05e97ba4a2be36f713aca8d8fd7974/Sources/JavaScriptKit/Documentation.docc/Tutorials/Resources/image.png -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Documentation.docc/Tutorials/Table-of-Contents.tutorial: -------------------------------------------------------------------------------- 1 | @Tutorials(name: "JavaScriptKit") { 2 | @Intro(title: "Working with JavaScriptKit") { 3 | JavaScriptKit is a Swift package that allows you to interact with JavaScript APIs directly from Swift code when targeting WebAssembly. 4 | } 5 | @Chapter(name: "Hello World") { 6 | @Image(source: "image.png") 7 | @TutorialReference(tutorial: "doc:Hello-World") 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Features.swift: -------------------------------------------------------------------------------- 1 | enum LibraryFeatures { 2 | static let weakRefs: Int32 = 1 << 0 3 | } 4 | 5 | @_cdecl("_library_features") 6 | func _library_features() -> Int32 { 7 | var features: Int32 = 0 8 | #if !JAVASCRIPTKIT_WITHOUT_WEAKREFS 9 | features |= LibraryFeatures.weakRefs 10 | #endif 11 | return features 12 | } 13 | 14 | #if compiler(>=6.0) && hasFeature(Embedded) 15 | // cdecls currently don't work in embedded, and expose for wasm only works >=6.0 16 | @_expose(wasm, "swjs_library_features") 17 | public func _swjs_library_features() -> Int32 { _library_features() } 18 | #endif 19 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/FundamentalObjects/JSBigInt.swift: -------------------------------------------------------------------------------- 1 | import _CJavaScriptKit 2 | 3 | private var constructor: JSFunction { JSObject.global.BigInt.function! } 4 | 5 | /// A wrapper around [the JavaScript `BigInt` 6 | /// class](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) 7 | /// that exposes its properties in a type-safe and Swifty way. 8 | public final class JSBigInt: JSObject { 9 | @_spi(JSObject_id) 10 | override public init(id: JavaScriptObjectRef) { 11 | super.init(id: id) 12 | } 13 | 14 | /// Instantiate a new `JSBigInt` with given Int64 value in a slow path 15 | /// This doesn't require [JS-BigInt-integration](https://github.com/WebAssembly/JS-BigInt-integration) feature. 16 | public init(_slowBridge value: Int64) { 17 | let value = UInt64(bitPattern: value) 18 | super.init(id: swjs_i64_to_bigint_slow(UInt32(value & 0xffff_ffff), UInt32(value >> 32), true)) 19 | } 20 | 21 | /// Instantiate a new `JSBigInt` with given UInt64 value in a slow path 22 | /// This doesn't require [JS-BigInt-integration](https://github.com/WebAssembly/JS-BigInt-integration) feature. 23 | public init(_slowBridge value: UInt64) { 24 | super.init(id: swjs_i64_to_bigint_slow(UInt32(value & 0xffff_ffff), UInt32(value >> 32), false)) 25 | } 26 | 27 | override public var jsValue: JSValue { 28 | .bigInt(self) 29 | } 30 | 31 | public func clamped(bitSize: Int, signed: Bool) -> JSBigInt { 32 | if signed { 33 | return constructor.asIntN(bitSize, self).bigInt! 34 | } else { 35 | return constructor.asUintN(bitSize, self).bigInt! 36 | } 37 | } 38 | } 39 | 40 | public protocol JSBigIntExtended: JSBigInt { 41 | var int64Value: Int64 { get } 42 | var uInt64Value: UInt64 { get } 43 | 44 | init(_ value: Int64) 45 | init(unsigned value: UInt64) 46 | } 47 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/JSBridgedType.swift: -------------------------------------------------------------------------------- 1 | /// Use this protocol when your type has no single JavaScript class. 2 | /// For example, a union type of multiple classes or primitive values. 3 | public protocol JSBridgedType: JSValueCompatible, CustomStringConvertible { 4 | /// If your class is incompatible with the provided value, return `nil`. 5 | init?(from value: JSValue) 6 | } 7 | 8 | extension JSBridgedType { 9 | public static func construct(from value: JSValue) -> Self? { 10 | Self(from: value) 11 | } 12 | 13 | public var description: String { jsValue.description } 14 | } 15 | 16 | /// Conform to this protocol when your Swift class wraps a JavaScript class. 17 | public protocol JSBridgedClass: JSBridgedType { 18 | /// The constructor function for the JavaScript class 19 | static var constructor: JSFunction? { get } 20 | 21 | /// The JavaScript object wrapped by this instance. 22 | /// You may assume that `jsObject instanceof Self.constructor == true` 23 | var jsObject: JSObject { get } 24 | 25 | /// Create an instance wrapping the given JavaScript object. 26 | /// You may assume that `jsObject instanceof Self.constructor` 27 | init(unsafelyWrapping jsObject: JSObject) 28 | } 29 | 30 | extension JSBridgedClass { 31 | public var jsValue: JSValue { jsObject.jsValue } 32 | 33 | public init?(from value: JSValue) { 34 | guard let object = value.object else { return nil } 35 | self.init(from: object) 36 | } 37 | 38 | public init?(from object: JSObject) { 39 | guard let constructor = Self.constructor, object.isInstanceOf(constructor) else { return nil } 40 | self.init(unsafelyWrapping: object) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/JSException.swift: -------------------------------------------------------------------------------- 1 | /// `JSException` is a wrapper that handles exceptions thrown during JavaScript execution as Swift 2 | /// `Error` objects. 3 | /// When a JavaScript function throws an exception, it's wrapped as a `JSException` and propagated 4 | /// through Swift's error handling mechanism. 5 | /// 6 | /// Example: 7 | /// ```swift 8 | /// do { 9 | /// try jsFunction.throws() 10 | /// } catch let error as JSException { 11 | /// // Access the value thrown from JavaScript 12 | /// let jsErrorValue = error.thrownValue 13 | /// } 14 | /// ``` 15 | public struct JSException: Error, Equatable, CustomStringConvertible { 16 | /// The value thrown from JavaScript. 17 | /// This can be any JavaScript value (error object, string, number, etc.). 18 | public var thrownValue: JSValue { 19 | return _thrownValue 20 | } 21 | 22 | /// The actual JavaScript value that was thrown. 23 | /// 24 | /// Marked as `nonisolated(unsafe)` to satisfy `Sendable` requirement 25 | /// from `Error` protocol. 26 | private nonisolated(unsafe) let _thrownValue: JSValue 27 | 28 | /// A description of the exception. 29 | public let description: String 30 | 31 | /// The stack trace of the exception. 32 | public let stack: String? 33 | 34 | /// Initializes a new JSException instance with a value thrown from JavaScript. 35 | /// 36 | /// Only available within the package. This must be called on the thread where the exception object created. 37 | package init(_ thrownValue: JSValue) { 38 | self._thrownValue = thrownValue 39 | // Capture the stringified representation on the object owner thread 40 | // to bring useful info to the catching thread even if they are different threads. 41 | if let errorObject = thrownValue.object, let stack = errorObject.stack.string { 42 | self.description = "JSException(\(stack))" 43 | self.stack = stack 44 | } else { 45 | self.description = "JSException(\(thrownValue))" 46 | self.stack = nil 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Macros.swift: -------------------------------------------------------------------------------- 1 | /// A macro that exposes Swift functions, classes, and methods to JavaScript. 2 | /// 3 | /// Apply this macro to Swift declarations that you want to make callable from JavaScript: 4 | /// 5 | /// ```swift 6 | /// // Export a function to JavaScript 7 | /// @JS public func greet(name: String) -> String { 8 | /// return "Hello, \(name)!" 9 | /// } 10 | /// 11 | /// // Export a class and its members 12 | /// @JS class Counter { 13 | /// private var count = 0 14 | /// 15 | /// @JS init() {} 16 | /// 17 | /// @JS func increment() { 18 | /// count += 1 19 | /// } 20 | /// 21 | /// @JS func getValue() -> Int { 22 | /// return count 23 | /// } 24 | /// } 25 | /// ``` 26 | /// 27 | /// When you build your project with the BridgeJS plugin, these declarations will be 28 | /// accessible from JavaScript, and TypeScript declaration files (`.d.ts`) will be 29 | /// automatically generated to provide type safety. 30 | /// 31 | /// For detailed usage information, see the article . 32 | /// 33 | /// - Important: This feature is still experimental. No API stability is guaranteed, and the API may change in future releases. 34 | @attached(peer) 35 | public macro JS() = Builtin.ExternalMacro 36 | -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Runtime/index.d.ts: -------------------------------------------------------------------------------- 1 | ../../../Plugins/PackageToJS/Templates/runtime.d.ts -------------------------------------------------------------------------------- /Sources/JavaScriptKit/Runtime/index.mjs: -------------------------------------------------------------------------------- 1 | ../../../Plugins/PackageToJS/Templates/runtime.mjs -------------------------------------------------------------------------------- /Sources/_CJavaScriptBigIntSupport/_CJavaScriptKit+I64.c: -------------------------------------------------------------------------------- 1 | // empty file to appease build process 2 | -------------------------------------------------------------------------------- /Sources/_CJavaScriptBigIntSupport/include/_CJavaScriptKit+I64.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _CJavaScriptBigIntSupport_h 3 | #define _CJavaScriptBigIntSupport_h 4 | 5 | #include <_CJavaScriptKit.h> 6 | 7 | #if __wasm32__ 8 | # define IMPORT_JS_FUNCTION(name, returns, args) \ 9 | __attribute__((__import_module__("javascript_kit"), __import_name__(#name))) extern returns name args; 10 | #else 11 | # define IMPORT_JS_FUNCTION(name, returns, args) \ 12 | static inline returns name args { \ 13 | abort(); \ 14 | } 15 | #endif 16 | 17 | /// Converts the provided Int64 or UInt64 to a BigInt. 18 | /// 19 | /// @param value The value to convert. 20 | /// @param is_signed Whether to treat the value as a signed integer or not. 21 | IMPORT_JS_FUNCTION(swjs_i64_to_bigint, JavaScriptObjectRef, (const long long value, bool is_signed)) 22 | 23 | /// Converts the provided BigInt to an Int64 or UInt64. 24 | /// 25 | /// @param ref The target JavaScript object. 26 | /// @param is_signed Whether to treat the return value as a signed integer or not. 27 | IMPORT_JS_FUNCTION(swjs_bigint_to_i64, long long, (const JavaScriptObjectRef ref, bool is_signed)) 28 | 29 | #endif /* _CJavaScriptBigIntSupport_h */ 30 | -------------------------------------------------------------------------------- /Sources/_CJavaScriptBigIntSupport/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module _CJavaScriptBigIntSupport { 2 | header "_CJavaScriptKit+I64.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /Sources/_CJavaScriptEventLoop/_CJavaScriptEventLoop.c: -------------------------------------------------------------------------------- 1 | #include "_CJavaScriptEventLoop.h" 2 | 3 | _Thread_local void *swjs_thread_local_event_loop; 4 | 5 | _Thread_local void *swjs_thread_local_task_executor_worker; 6 | -------------------------------------------------------------------------------- /Sources/_CJavaScriptEventLoop/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module _CJavaScriptEventLoop { 2 | header "_CJavaScriptEventLoop.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /Sources/_CJavaScriptEventLoopTestSupport/_CJavaScriptEventLoopTestSupport.c: -------------------------------------------------------------------------------- 1 | // This 'ctor' function is called at startup time of this program. 2 | // It's invoked by '_start' of command-line or '_initialize' of reactor. 3 | // This ctor activate the event loop based global executor automatically 4 | // before running the test cases. For general applications, applications 5 | // have to activate the event loop manually on their responsibility. 6 | // However, XCTest framework doesn't provide a way to run arbitrary code 7 | // before running all of the test suites. So, we have to do it here. 8 | // 9 | // See also: https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md#current-unstable-abi 10 | 11 | extern void swift_javascriptkit_activate_js_executor_impl(void); 12 | 13 | // priority 0~100 is reserved by wasi-libc 14 | // https://github.com/WebAssembly/wasi-libc/blob/30094b6ed05f19cee102115215863d185f2db4f0/libc-bottom-half/sources/environ.c#L20 15 | __attribute__((constructor(/* priority */ 200))) 16 | void swift_javascriptkit_activate_js_executor(void) { 17 | swift_javascriptkit_activate_js_executor_impl(); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Sources/_CJavaScriptEventLoopTestSupport/include/dummy.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftwasm/JavaScriptKit/8563ff73cf05e97ba4a2be36f713aca8d8fd7974/Sources/_CJavaScriptEventLoopTestSupport/include/dummy.h -------------------------------------------------------------------------------- /Sources/_CJavaScriptKit/_CJavaScriptKit.c: -------------------------------------------------------------------------------- 1 | #include "_CJavaScriptKit.h" 2 | #if __wasm32__ 3 | # ifndef __wasi__ 4 | # if __has_include("malloc.h") 5 | # include 6 | # endif 7 | extern void *malloc(size_t size); 8 | extern void free(void *ptr); 9 | extern void *memset (void *, int, size_t); 10 | extern void *memcpy (void *__restrict, const void *__restrict, size_t); 11 | # else 12 | # include 13 | # include 14 | 15 | # endif 16 | /// The compatibility runtime library version. 17 | /// Notes: If you change any interface of runtime library, please increment 18 | /// this and `SwiftRuntime.version` in `./Runtime/src/index.ts`. 19 | __attribute__((export_name("swjs_library_version"))) 20 | int swjs_library_version(void) { 21 | return 708; 22 | } 23 | 24 | __attribute__((export_name("swjs_prepare_host_function_call"))) 25 | void *swjs_prepare_host_function_call(const int argc) { 26 | return malloc(argc * sizeof(RawJSValue)); 27 | } 28 | 29 | __attribute__((export_name("swjs_cleanup_host_function_call"))) 30 | void swjs_cleanup_host_function_call(void *argv_buffer) { 31 | free(argv_buffer); 32 | } 33 | 34 | // NOTE: This __wasi__ check is a hack for Embedded compatibility (assuming that if __wasi__ is defined, we are not building for Embedded) 35 | // cdecls don't work in Embedded, but @_expose(wasm) can be used with Swift >=6.0 36 | // the previously used `#if __Embedded` did not play well with SwiftPM (defines needed to be on every target up the chain) 37 | # ifdef __wasi__ 38 | bool _call_host_function_impl(const JavaScriptHostFuncRef host_func_ref, 39 | const RawJSValue *argv, const int argc, 40 | const JavaScriptObjectRef callback_func); 41 | 42 | __attribute__((export_name("swjs_call_host_function"))) 43 | bool swjs_call_host_function(const JavaScriptHostFuncRef host_func_ref, 44 | const RawJSValue *argv, const int argc, 45 | const JavaScriptObjectRef callback_func) { 46 | return _call_host_function_impl(host_func_ref, argv, argc, callback_func); 47 | } 48 | 49 | void _free_host_function_impl(const JavaScriptHostFuncRef host_func_ref); 50 | 51 | __attribute__((export_name("swjs_free_host_function"))) 52 | void swjs_free_host_function(const JavaScriptHostFuncRef host_func_ref) { 53 | _free_host_function_impl(host_func_ref); 54 | } 55 | 56 | int _library_features(void); 57 | 58 | __attribute__((export_name("swjs_library_features"))) 59 | int swjs_library_features(void) { 60 | return _library_features(); 61 | } 62 | # endif 63 | #endif 64 | 65 | int swjs_get_worker_thread_id_cached(void) { 66 | _Thread_local static int tid = 0; 67 | if (tid == 0) { 68 | tid = swjs_get_worker_thread_id(); 69 | } 70 | return tid; 71 | } 72 | -------------------------------------------------------------------------------- /Sources/_CJavaScriptKit/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module _CJavaScriptKit { 2 | header "_CJavaScriptKit.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /Tests/BridgeJSRuntimeTests/ExportAPITests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import JavaScriptKit 3 | 4 | @_extern(wasm, module: "BridgeJSRuntimeTests", name: "runJsWorks") 5 | @_extern(c) 6 | func runJsWorks() -> Void 7 | 8 | @JS func roundTripVoid() -> Void { 9 | return 10 | } 11 | 12 | @JS func roundTripInt(v: Int) -> Int { 13 | return v 14 | } 15 | @JS func roundTripFloat(v: Float) -> Float { 16 | return v 17 | } 18 | @JS func roundTripDouble(v: Double) -> Double { 19 | return v 20 | } 21 | @JS func roundTripBool(v: Bool) -> Bool { 22 | return v 23 | } 24 | @JS func roundTripString(v: String) -> String { 25 | return v 26 | } 27 | @JS func roundTripSwiftHeapObject(v: Greeter) -> Greeter { 28 | return v 29 | } 30 | 31 | @JS class Greeter { 32 | var name: String 33 | 34 | nonisolated(unsafe) static var onDeinit: () -> Void = {} 35 | 36 | @JS init(name: String) { 37 | self.name = name 38 | } 39 | 40 | @JS func greet() -> String { 41 | return "Hello, \(name)!" 42 | } 43 | @JS func changeName(name: String) { 44 | self.name = name 45 | } 46 | 47 | deinit { 48 | Self.onDeinit() 49 | } 50 | } 51 | 52 | @JS func takeGreeter(g: Greeter, name: String) { 53 | g.changeName(name: name) 54 | } 55 | 56 | class ExportAPITests: XCTestCase { 57 | func testAll() { 58 | var hasDeinitGreeter = false 59 | Greeter.onDeinit = { 60 | hasDeinitGreeter = true 61 | } 62 | runJsWorks() 63 | XCTAssertTrue(hasDeinitGreeter) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/JavaScriptBigIntSupportTests/JavaScriptBigIntSupportTests.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptBigIntSupport 2 | import JavaScriptKit 3 | import XCTest 4 | 5 | class JavaScriptBigIntSupportTests: XCTestCase { 6 | func testBigIntSupport() { 7 | // Test signed values 8 | func testSignedValue(_ value: Int64, file: StaticString = #filePath, line: UInt = #line) { 9 | let bigInt = JSBigInt(value) 10 | XCTAssertEqual(bigInt.description, value.description, file: file, line: line) 11 | let bigInt2 = JSBigInt(_slowBridge: value) 12 | XCTAssertEqual(bigInt2.description, value.description, file: file, line: line) 13 | } 14 | 15 | // Test unsigned values 16 | func testUnsignedValue(_ value: UInt64, file: StaticString = #filePath, line: UInt = #line) { 17 | let bigInt = JSBigInt(unsigned: value) 18 | XCTAssertEqual(bigInt.description, value.description, file: file, line: line) 19 | let bigInt2 = JSBigInt(_slowBridge: value) 20 | XCTAssertEqual(bigInt2.description, value.description, file: file, line: line) 21 | } 22 | 23 | // Test specific signed values 24 | testSignedValue(0) 25 | testSignedValue(1 << 62) 26 | testSignedValue(-2305) 27 | 28 | // Test random signed values 29 | for _ in 0..<100 { 30 | testSignedValue(.random(in: .min ... .max)) 31 | } 32 | 33 | // Test edge signed values 34 | testSignedValue(.min) 35 | testSignedValue(.max) 36 | 37 | // Test specific unsigned values 38 | testUnsignedValue(0) 39 | testUnsignedValue(1 << 62) 40 | testUnsignedValue(1 << 63) 41 | testUnsignedValue(.min) 42 | testUnsignedValue(.max) 43 | testUnsignedValue(~0) 44 | 45 | // Test random unsigned values 46 | for _ in 0..<100 { 47 | testUnsignedValue(.random(in: .min ... .max)) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tests/JavaScriptEventLoopTestSupportTests/JavaScriptEventLoopTestSupportTests.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | import XCTest 3 | 4 | final class JavaScriptEventLoopTestSupportTests: XCTestCase { 5 | func testAwaitMicrotask() async { 6 | let _: () = await withCheckedContinuation { cont in 7 | JSObject.global.queueMicrotask.function!( 8 | JSOneshotClosure { _ in 9 | cont.resume(returning: ()) 10 | return .undefined 11 | } 12 | ) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tests/JavaScriptEventLoopTests/JSTimerTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import JavaScriptKit 4 | 5 | final class JSTimerTests: XCTestCase { 6 | 7 | func testOneshotTimerCancelled() { 8 | let timeoutMilliseconds = 5.0 9 | var timeout: JSTimer! 10 | timeout = JSTimer(millisecondsDelay: timeoutMilliseconds, isRepeating: false) { 11 | XCTFail("timer should be cancelled") 12 | } 13 | _ = timeout 14 | timeout = nil 15 | } 16 | 17 | func testRepeatingTimerCancelled() async throws { 18 | var count = 0.0 19 | let maxCount = 5.0 20 | var interval: JSTimer? 21 | let start = JSDate().valueOf() 22 | let timeoutMilliseconds = 5.0 23 | 24 | await withCheckedContinuation { continuation in 25 | interval = JSTimer(millisecondsDelay: 5, isRepeating: true) { 26 | // ensure that JSTimer is living 27 | XCTAssertNotNil(interval) 28 | // verify that at least `timeoutMilliseconds * count` passed since the `timeout` 29 | // timer started 30 | XCTAssertTrue(start + timeoutMilliseconds * count <= JSDate().valueOf()) 31 | 32 | guard count < maxCount else { 33 | // stop the timer after `maxCount` reached 34 | interval = nil 35 | continuation.resume() 36 | return 37 | } 38 | 39 | count += 1 40 | } 41 | } 42 | withExtendedLifetime(interval) {} 43 | } 44 | 45 | func testTimer() async throws { 46 | let timeoutMilliseconds = 5.0 47 | var timeout: JSTimer! 48 | await withCheckedContinuation { continuation in 49 | timeout = JSTimer(millisecondsDelay: timeoutMilliseconds, isRepeating: false) { 50 | continuation.resume() 51 | } 52 | } 53 | withExtendedLifetime(timeout) {} 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tests/JavaScriptEventLoopTests/WebWorkerDedicatedExecutorTests.swift: -------------------------------------------------------------------------------- 1 | #if compiler(>=6.1) && _runtime(_multithreaded) 2 | import XCTest 3 | @testable import JavaScriptEventLoop 4 | 5 | final class WebWorkerDedicatedExecutorTests: XCTestCase { 6 | actor MyActor { 7 | let executor: WebWorkerDedicatedExecutor 8 | nonisolated var unownedExecutor: UnownedSerialExecutor { 9 | self.executor.asUnownedSerialExecutor() 10 | } 11 | 12 | init(executor: WebWorkerDedicatedExecutor) { 13 | self.executor = executor 14 | XCTAssertTrue(isMainThread()) 15 | } 16 | 17 | func onWorkerThread() async { 18 | XCTAssertFalse(isMainThread()) 19 | await Task.detached {}.value 20 | // Should keep on the thread after back from the other isolation domain 21 | XCTAssertFalse(isMainThread()) 22 | } 23 | } 24 | 25 | func testEnqueue() async throws { 26 | let executor = try await WebWorkerDedicatedExecutor() 27 | defer { executor.terminate() } 28 | let actor = MyActor(executor: executor) 29 | XCTAssertTrue(isMainThread()) 30 | await actor.onWorkerThread() 31 | XCTAssertTrue(isMainThread()) 32 | } 33 | } 34 | #endif 35 | -------------------------------------------------------------------------------- /Tests/JavaScriptKitTests/JSObjectTests.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | import XCTest 3 | 4 | final class JSObjectTests: XCTestCase { 5 | func testEmptyObject() { 6 | let object = JSObject() 7 | let keys = JSObject.global.Object.function!.keys.function!(object) 8 | XCTAssertEqual(keys.array?.count, 0) 9 | } 10 | 11 | func testInitWithDictionaryLiteral() { 12 | let object: JSObject = [ 13 | "key1": 1, 14 | "key2": "value2", 15 | "key3": .boolean(true), 16 | "key4": .object(JSObject()), 17 | "key5": [1, 2, 3].jsValue, 18 | "key6": ["key": "value"].jsValue, 19 | ] 20 | XCTAssertEqual(object.key1, .number(1)) 21 | XCTAssertEqual(object.key2, "value2") 22 | XCTAssertEqual(object.key3, .boolean(true)) 23 | let getKeys = JSObject.global.Object.function!.keys.function! 24 | XCTAssertEqual(getKeys(object.key4).array?.count, 0) 25 | XCTAssertEqual(object.key5.array.map(Array.init), [1, 2, 3]) 26 | XCTAssertEqual(object.key6.object?.key, "value") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/JavaScriptKitTests/JSStringTests.swift: -------------------------------------------------------------------------------- 1 | import JavaScriptKit 2 | import XCTest 3 | 4 | final class JSStringTests: XCTestCase { 5 | func testEquatable() { 6 | let string1 = JSString("Hello, world!") 7 | let string2 = JSString("Hello, world!") 8 | let string3 = JSString("Hello, world") 9 | XCTAssertEqual(string1, string1) 10 | XCTAssertEqual(string1, string2) 11 | XCTAssertNotEqual(string1, string3) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/JavaScriptKitTests/ThreadLocalTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import JavaScriptKit 4 | 5 | final class ThreadLocalTests: XCTestCase { 6 | class MyHeapObject {} 7 | struct MyStruct { 8 | var object: MyHeapObject 9 | } 10 | 11 | func testLeak() throws { 12 | struct Check { 13 | static let value = ThreadLocal() 14 | static let value2 = ThreadLocal(boxing: ()) 15 | } 16 | weak var weakObject: MyHeapObject? 17 | do { 18 | let object = MyHeapObject() 19 | weakObject = object 20 | Check.value.wrappedValue = object 21 | XCTAssertNotNil(Check.value.wrappedValue) 22 | XCTAssertTrue(Check.value.wrappedValue === object) 23 | Check.value.wrappedValue = nil 24 | } 25 | XCTAssertNil(weakObject) 26 | 27 | weak var weakObject2: MyHeapObject? 28 | do { 29 | let object = MyHeapObject() 30 | weakObject2 = object 31 | Check.value2.wrappedValue = MyStruct(object: object) 32 | XCTAssertNotNil(Check.value2.wrappedValue) 33 | XCTAssertTrue(Check.value2.wrappedValue!.object === object) 34 | Check.value2.wrappedValue = nil 35 | } 36 | XCTAssertNil(weakObject2) 37 | } 38 | 39 | func testLazyThreadLocal() throws { 40 | struct Check { 41 | static let value = LazyThreadLocal(initialize: { MyHeapObject() }) 42 | } 43 | let object1 = Check.value.wrappedValue 44 | let object2 = Check.value.wrappedValue 45 | XCTAssertTrue(object1 === object2) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-kit-swift", 3 | "version": "0.0.0", 4 | "description": "A runtime library of JavaScriptKit which is Swift framework to interact with JavaScript through WebAssembly.", 5 | "main": "Runtime/lib/index.js", 6 | "module": "Runtime/lib/index.mjs", 7 | "types": "Runtime/lib/index.d.ts", 8 | "files": [ 9 | "Runtime/lib" 10 | ], 11 | "scripts": { 12 | "build": "npm run build:clean && npm run build:ts", 13 | "build:clean": "rm -rf Runtime/lib", 14 | "build:ts": "cd Runtime; rollup -c", 15 | "prepublishOnly": "npm run build", 16 | "format": "prettier --write Runtime/src" 17 | }, 18 | "keywords": [ 19 | "Swift", 20 | "WebAssembly", 21 | "wasm" 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/swiftwasm/JavaScriptKit.git" 26 | }, 27 | "homepage": "https://github.com/swiftwasm/JavaScriptKit", 28 | "bugs": { 29 | "url": "https://github.com/swiftwasm/JavaScriptKit/issues" 30 | }, 31 | "publishConfig": { 32 | "access": "public" 33 | }, 34 | "author": "swiftwasm", 35 | "license": "MIT", 36 | "devDependencies": { 37 | "@bjorn3/browser_wasi_shim": "^0.4.1", 38 | "@rollup/plugin-typescript": "^12.1.2", 39 | "@types/node": "^22.13.14", 40 | "playwright": "^1.52.0", 41 | "prettier": "3.5.3", 42 | "rollup": "^4.37.0", 43 | "rollup-plugin-dts": "^6.2.1", 44 | "typescript": "^5.8.2" 45 | } 46 | } 47 | --------------------------------------------------------------------------------