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
66 changes: 66 additions & 0 deletions Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export class TypeProcessor {
this.seenTypes = new Map();
/** @type {string[]} Collected Swift code lines */
this.swiftLines = [];

/** @type {Set<string>} */
this.visitedDeclarationKeys = new Set();
}

/**
Expand Down Expand Up @@ -117,6 +120,69 @@ export class TypeProcessor {
this.visitFunctionDeclaration(node);
} else if (ts.isClassDeclaration(node)) {
this.visitClassDecl(node);
} else if (ts.isExportDeclaration(node)) {
this.visitExportDeclaration(node);
}
}

/**
* Visit an export declaration and process re-exports like:
* - export { Thing } from "./module";
* - export { Thing as Alias } from "./module";
* - export * from "./module";
* @param {ts.ExportDeclaration} node
*/
visitExportDeclaration(node) {
if (!node.moduleSpecifier) return;

const moduleSymbol = this.checker.getSymbolAtLocation(node.moduleSpecifier);
if (!moduleSymbol) {
this.diagnosticEngine.print("warning", "Failed to resolve module for export declaration", node);
return;
}

/** @type {ts.Symbol[]} */
let targetSymbols = [];

if (!node.exportClause) {
// export * from "..."
targetSymbols = this.checker.getExportsOfModule(moduleSymbol);
} else if (ts.isNamedExports(node.exportClause)) {
const moduleExports = this.checker.getExportsOfModule(moduleSymbol);
for (const element of node.exportClause.elements) {
const originalName = element.propertyName?.text ?? element.name.text;

const match = moduleExports.find(s => s.name === originalName);
if (match) {
targetSymbols.push(match);
continue;
}

// Fallback for unusual bindings/resolution failures.
const fallback = this.checker.getSymbolAtLocation(element.propertyName ?? element.name);
if (fallback) {
targetSymbols.push(fallback);
continue;
}

this.diagnosticEngine.print("warning", `Failed to resolve re-exported symbol '${originalName}'`, node);
}
} else {
// export * as ns from "..." is not currently supported by BridgeJS imports.
return;
}

for (const symbol of targetSymbols) {
const declarations = symbol.getDeclarations() ?? [];
for (const declaration of declarations) {
// Avoid duplicate emission when the same declaration is reached via multiple re-exports.
const sourceFile = declaration.getSourceFile();
const key = `${sourceFile.fileName}:${declaration.pos}:${declaration.end}`;
if (this.visitedDeclarationKeys.has(key)) continue;
this.visitedDeclarationKeys.add(key);

this.visitNode(declaration);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { jsRoundTripNumber } from "./Support/ReExportTarget"
export { JsGreeter } from "./Support/ReExportTarget"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function jsRoundTripNumber(v: number): number

export class JsGreeter {
constructor(name: string);
greet(): string;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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 JsGreeter {
greet(): string;
}
export type Exports = {
}
export type Imports = {
jsRoundTripNumber(v: number): number;
JsGreeter: {
new(name: string): JsGreeter;
}
}
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,241 @@
// 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_jsRoundTripNumber"] = function bjs_jsRoundTripNumber(v) {
try {
let ret = imports.jsRoundTripNumber(v);
return ret;
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_JsGreeter_init"] = function bjs_JsGreeter_init(name) {
try {
const nameObject = swift.memory.getObject(name);
swift.memory.release(name);
return swift.memory.retain(new imports.JsGreeter(nameObject));
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_JsGreeter_greet"] = function bjs_JsGreeter_greet(self) {
try {
let ret = swift.memory.getObject(self).greet();
tmpRetBytes = textEncoder.encode(ret);
return tmpRetBytes.length;
} catch (error) {
setException(error);
}
}
},
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 = {
};
_exports = exports;
return exports;
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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`.

@_spi(Experimental) import JavaScriptKit

@JSFunction func jsRoundTripNumber(_ v: Double) throws (JSException) -> Double

@JSClass struct JsGreeter {
@JSFunction init(_ name: String) throws (JSException)
@JSFunction func greet() throws (JSException) -> String
}
Loading