Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ public final class SwiftToSkeleton {
return nil
}
let swiftCallName = SwiftToSkeleton.computeSwiftCallName(for: typeDecl, itemName: typeDecl.name.text)

// A type annotated with @JSClass is a JavaScript object wrapper (imported),
// even if it is declared as a Swift class.
if let classDecl = typeDecl.as(ClassDeclSyntax.self), classDecl.attributes.hasAttribute(name: "JSClass") {
return .jsObject(swiftCallName)
}
if let actorDecl = typeDecl.as(ActorDeclSyntax.self), actorDecl.attributes.hasAttribute(name: "JSClass") {
return .jsObject(swiftCallName)
}

return .swiftHeapObject(swiftCallName)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@JSClass class Foo {
@JSFunction init() throws(JSException)
}

@JS func makeFoo() throws(JSException) -> Foo {
return try Foo()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
// DO NOT EDIT.
//
// To update this file, just rebuild your project or run
// `swift package bridge-js`.

export interface Foo {
}
export type Exports = {
makeFoo(): Foo;
}
export type Imports = {
Foo: {
new(): Foo;
}
}
export function createInstantiator(options: {
imports: Imports;
}, swift: any): Promise<{
addImports: (importObject: WebAssembly.Imports) => void;
setInstance: (instance: WebAssembly.Instance) => void;
createExports: (instance: WebAssembly.Instance) => Exports;
}>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
// DO NOT EDIT.
//
// To update this file, just rebuild your project or run
// `swift package bridge-js`.

export async function createInstantiator(options, swift) {
let instance;
let memory;
let setException;
const textDecoder = new TextDecoder("utf-8");
const textEncoder = new TextEncoder("utf-8");
let tmpRetString;
let tmpRetBytes;
let tmpRetException;
let tmpRetOptionalBool;
let tmpRetOptionalInt;
let tmpRetOptionalFloat;
let tmpRetOptionalDouble;
let tmpRetOptionalHeapObject;
let tmpRetTag;
let tmpRetStrings = [];
let tmpRetInts = [];
let tmpRetF32s = [];
let tmpRetF64s = [];
let tmpParamInts = [];
let tmpParamF32s = [];
let tmpParamF64s = [];
let tmpRetPointers = [];
let tmpParamPointers = [];
const enumHelpers = {};
const structHelpers = {};

let _exports = null;
let bjs = null;

return {
/**
* @param {WebAssembly.Imports} importObject
*/
addImports: (importObject, importsContext) => {
bjs = {};
importObject["bjs"] = bjs;
const imports = options.getImports(importsContext);
bjs["swift_js_return_string"] = function(ptr, len) {
const bytes = new Uint8Array(memory.buffer, ptr, len);
tmpRetString = textDecoder.decode(bytes);
}
bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) {
const source = swift.memory.getObject(sourceId);
const bytes = new Uint8Array(memory.buffer, bytesPtr);
bytes.set(source);
}
bjs["swift_js_make_js_string"] = function(ptr, len) {
const bytes = new Uint8Array(memory.buffer, ptr, len);
return swift.memory.retain(textDecoder.decode(bytes));
}
bjs["swift_js_init_memory_with_result"] = function(ptr, len) {
const target = new Uint8Array(memory.buffer, ptr, len);
target.set(tmpRetBytes);
tmpRetBytes = undefined;
}
bjs["swift_js_throw"] = function(id) {
tmpRetException = swift.memory.retainByRef(id);
}
bjs["swift_js_retain"] = function(id) {
return swift.memory.retainByRef(id);
}
bjs["swift_js_release"] = function(id) {
swift.memory.release(id);
}
bjs["swift_js_push_tag"] = function(tag) {
tmpRetTag = tag;
}
bjs["swift_js_push_int"] = function(v) {
tmpRetInts.push(v | 0);
}
bjs["swift_js_push_f32"] = function(v) {
tmpRetF32s.push(Math.fround(v));
}
bjs["swift_js_push_f64"] = function(v) {
tmpRetF64s.push(v);
}
bjs["swift_js_push_string"] = function(ptr, len) {
const bytes = new Uint8Array(memory.buffer, ptr, len);
const value = textDecoder.decode(bytes);
tmpRetStrings.push(value);
}
bjs["swift_js_pop_param_int32"] = function() {
return tmpParamInts.pop();
}
bjs["swift_js_pop_param_f32"] = function() {
return tmpParamF32s.pop();
}
bjs["swift_js_pop_param_f64"] = function() {
return tmpParamF64s.pop();
}
bjs["swift_js_push_pointer"] = function(pointer) {
tmpRetPointers.push(pointer);
}
bjs["swift_js_pop_param_pointer"] = function() {
return tmpParamPointers.pop();
}
bjs["swift_js_return_optional_bool"] = function(isSome, value) {
if (isSome === 0) {
tmpRetOptionalBool = null;
} else {
tmpRetOptionalBool = value !== 0;
}
}
bjs["swift_js_return_optional_int"] = function(isSome, value) {
if (isSome === 0) {
tmpRetOptionalInt = null;
} else {
tmpRetOptionalInt = value | 0;
}
}
bjs["swift_js_return_optional_float"] = function(isSome, value) {
if (isSome === 0) {
tmpRetOptionalFloat = null;
} else {
tmpRetOptionalFloat = Math.fround(value);
}
}
bjs["swift_js_return_optional_double"] = function(isSome, value) {
if (isSome === 0) {
tmpRetOptionalDouble = null;
} else {
tmpRetOptionalDouble = value;
}
}
bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) {
if (isSome === 0) {
tmpRetString = null;
} else {
const bytes = new Uint8Array(memory.buffer, ptr, len);
tmpRetString = textDecoder.decode(bytes);
}
}
bjs["swift_js_return_optional_object"] = function(isSome, objectId) {
if (isSome === 0) {
tmpRetString = null;
} else {
tmpRetString = swift.memory.getObject(objectId);
}
}
bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) {
if (isSome === 0) {
tmpRetOptionalHeapObject = null;
} else {
tmpRetOptionalHeapObject = pointer;
}
}
bjs["swift_js_get_optional_int_presence"] = function() {
return tmpRetOptionalInt != null ? 1 : 0;
}
bjs["swift_js_get_optional_int_value"] = function() {
const value = tmpRetOptionalInt;
tmpRetOptionalInt = undefined;
return value;
}
bjs["swift_js_get_optional_string"] = function() {
const str = tmpRetString;
tmpRetString = undefined;
if (str == null) {
return -1;
} else {
const bytes = textEncoder.encode(str);
tmpRetBytes = bytes;
return bytes.length;
}
}
bjs["swift_js_get_optional_float_presence"] = function() {
return tmpRetOptionalFloat != null ? 1 : 0;
}
bjs["swift_js_get_optional_float_value"] = function() {
const value = tmpRetOptionalFloat;
tmpRetOptionalFloat = undefined;
return value;
}
bjs["swift_js_get_optional_double_presence"] = function() {
return tmpRetOptionalDouble != null ? 1 : 0;
}
bjs["swift_js_get_optional_double_value"] = function() {
const value = tmpRetOptionalDouble;
tmpRetOptionalDouble = undefined;
return value;
}
bjs["swift_js_get_optional_heap_object_pointer"] = function() {
const pointer = tmpRetOptionalHeapObject;
tmpRetOptionalHeapObject = undefined;
return pointer || 0;
}
const TestModule = importObject["TestModule"] = importObject["TestModule"] || {};
TestModule["bjs_Foo_init"] = function bjs_Foo_init() {
try {
return swift.memory.retain(new imports.Foo());
} catch (error) {
setException(error);
return 0
}
}
},
setInstance: (i) => {
instance = i;
memory = instance.exports.memory;

setException = (error) => {
instance.exports._swift_js_exception.value = swift.memory.retain(error)
}
},
/** @param {WebAssembly.Instance} instance */
createExports: (instance) => {
const js = swift.memory.heap;
const exports = {
makeFoo: function bjs_makeFoo() {
const ret = instance.exports.bjs_makeFoo();
const ret1 = swift.memory.getObject(ret);
swift.memory.release(ret);
if (tmpRetException) {
const error = swift.memory.getObject(tmpRetException);
swift.memory.release(tmpRetException);
tmpRetException = undefined;
throw error;
}
return ret1;
},
};
_exports = exports;
return exports;
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"classes" : [

],
"enums" : [

],
"exposeToGlobal" : false,
"functions" : [
{
"abiName" : "bjs_makeFoo",
"effects" : {
"isAsync" : false,
"isStatic" : false,
"isThrows" : true
},
"name" : "makeFoo",
"parameters" : [

],
"returnType" : {
"jsObject" : {
"_0" : "Foo"
}
}
}
],
"protocols" : [

],
"structs" : [

]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@_expose(wasm, "bjs_makeFoo")
@_cdecl("bjs_makeFoo")
public func _bjs_makeFoo() -> Int32 {
#if arch(wasm32)
do {
let ret = try makeFoo()
return ret.bridgeJSLowerReturn()
} catch let error {
if let error = error.thrownValue.object {
withExtendedLifetime(error) {
_swift_js_throw(Int32(bitPattern: $0.id))
}
} else {
let jsError = JSError(message: String(describing: error))
withExtendedLifetime(jsError.jsObject) {
_swift_js_throw(Int32(bitPattern: $0.id))
}
}
return 0
}
#else
fatalError("Only available on WebAssembly")
#endif
}
11 changes: 10 additions & 1 deletion Tests/BridgeJSRuntimeTests/ExportAPITests.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import XCTest
import JavaScriptKit
@_spi(Experimental) import JavaScriptKit
import JavaScriptEventLoop

@_extern(wasm, module: "BridgeJSRuntimeTests", name: "runJsWorks")
Expand Down Expand Up @@ -33,6 +33,15 @@ func runJsWorks() -> Void
return v
}

@JSClass struct Foo {
@JSGetter var value: String
@JSFunction init(_ value: String) throws(JSException)
}

@JS func makeImportedFoo(value: String) throws(JSException) -> Foo {
return try Foo(value)
}

struct TestError: Error {
let message: String
}
Expand Down
Loading