Skip to content

Commit 753b8db

Browse files
Merge pull request #428 from swiftwasm/yt/fix-skip-invalid-swift-id
BridgeJS: Skip importing TS declarations with invalid Swift identifiers
2 parents 99a288f + 095d4e7 commit 753b8db

File tree

6 files changed

+461
-6
lines changed

6 files changed

+461
-6
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ public struct ImportTS {
156156
func renderThunkDecl(name: String, parameters: [Parameter], returnType: BridgeType) -> DeclSyntax {
157157
return DeclSyntax(
158158
FunctionDeclSyntax(
159-
name: .identifier(name),
159+
name: .identifier(name.backtickIfNeeded()),
160160
signature: FunctionSignatureSyntax(
161161
parameterClause: FunctionParameterClauseSyntax(parametersBuilder: {
162162
for param in parameters {
@@ -315,7 +315,9 @@ public struct ImportTS {
315315
bindingsBuilder: {
316316
PatternBindingListSyntax {
317317
PatternBindingSyntax(
318-
pattern: IdentifierPatternSyntax(identifier: .identifier(property.name)),
318+
pattern: IdentifierPatternSyntax(
319+
identifier: .identifier(property.name.backtickIfNeeded())
320+
),
319321
typeAnnotation: TypeAnnotationSyntax(
320322
type: IdentifierTypeSyntax(name: .identifier(property.type.swiftType))
321323
),
@@ -466,3 +468,9 @@ extension BridgeType {
466468
}
467469
}
468470
}
471+
472+
extension String {
473+
func backtickIfNeeded() -> String {
474+
return self.isValidSwiftIdentifier(for: .variableName) ? self : "`\(self)`"
475+
}
476+
}

Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/processor.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ export class TypeProcessor {
142142
*/
143143
visitFunctionLikeDecl(node) {
144144
if (!node.name) return null;
145+
const name = node.name.getText();
146+
if (!isValidSwiftDeclName(name)) {
147+
return null;
148+
}
145149

146150
const signature = this.checker.getSignatureFromDeclaration(node);
147151
if (!signature) return null;
@@ -158,7 +162,7 @@ export class TypeProcessor {
158162
const documentation = this.getFullJSDocText(node);
159163

160164
return {
161-
name: node.name.getText(),
165+
name,
162166
parameters,
163167
returnType: bridgeReturnType,
164168
documentation,
@@ -206,11 +210,17 @@ export class TypeProcessor {
206210
*/
207211
visitPropertyDecl(node) {
208212
if (!node.name) return null;
213+
214+
const propertyName = node.name.getText();
215+
if (!isValidSwiftDeclName(propertyName)) {
216+
return null;
217+
}
218+
209219
const type = this.checker.getTypeAtLocation(node)
210220
const bridgeType = this.visitType(type, node);
211221
const isReadonly = node.modifiers?.some(m => m.kind === ts.SyntaxKind.ReadonlyKeyword) ?? false;
212222
const documentation = this.getFullJSDocText(node);
213-
return { name: node.name.getText(), type: bridgeType, isReadonly, documentation };
223+
return { name: propertyName, type: bridgeType, isReadonly, documentation };
214224
}
215225

216226
/**
@@ -225,7 +235,7 @@ export class TypeProcessor {
225235
}
226236

227237
/**
228-
* @param {ts.ClassDeclaration} node
238+
* @param {ts.ClassDeclaration} node
229239
* @returns {ImportTypeSkeleton | null}
230240
*/
231241
visitClassDecl(node) {
@@ -442,4 +452,16 @@ function isTypeReference(type) {
442452
isObjectType(type) &&
443453
(type.objectFlags & ts.ObjectFlags.Reference) !== 0
444454
);
445-
}
455+
}
456+
457+
/**
458+
* Check if a declaration name is valid for Swift generation
459+
* @param {string} name - Declaration name to check
460+
* @returns {boolean} True if the name is valid for Swift
461+
* @private
462+
*/
463+
export function isValidSwiftDeclName(name) {
464+
// https://docs.swift.org/swift-book/documentation/the-swift-programming-language/lexicalstructure/
465+
const swiftIdentifierRegex = /^[_\p{ID_Start}][\p{ID_Continue}\u{200C}\u{200D}]*$/u;
466+
return swiftIdentifierRegex.test(name);
467+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
interface ArrayBufferLike {
2+
readonly byteLength: number;
3+
readonly [Symbol.toStringTag]: string;
4+
slice(begin: number, end: number): ArrayBufferLike;
5+
}
6+
7+
interface WeirdNaming {
8+
normalProperty: string;
9+
"property-with-dashes": number;
10+
"123invalidStart": boolean;
11+
"property with spaces": string;
12+
readonly [Symbol.species]: any;
13+
[Symbol.asyncIterator](): AsyncIterator<any>;
14+
"@specialChar": number;
15+
"constructor": string; // This should be valid
16+
for: string;
17+
Any: string;
18+
as(): void;
19+
"try"(): void;
20+
}
21+
22+
export function createArrayBuffer(): ArrayBufferLike;
23+
export function createWeirdObject(): WeirdNaming;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 interface ArrayBufferLike {
8+
slice(begin: number, end: number): ArrayBufferLike;
9+
readonly byteLength: number;
10+
}
11+
export interface WeirdNaming {
12+
as(): void;
13+
normalProperty: string;
14+
for: string;
15+
Any: string;
16+
}
17+
export type Exports = {
18+
}
19+
export type Imports = {
20+
createArrayBuffer(): ArrayBufferLike;
21+
createWeirdObject(): WeirdNaming;
22+
}
23+
export function createInstantiator(options: {
24+
imports: Imports;
25+
}, swift: any): Promise<{
26+
addImports: (importObject: WebAssembly.Imports) => void;
27+
setInstance: (instance: WebAssembly.Instance) => void;
28+
createExports: (instance: WebAssembly.Instance) => Exports;
29+
}>;
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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+
let setException;
11+
const textDecoder = new TextDecoder("utf-8");
12+
const textEncoder = new TextEncoder("utf-8");
13+
14+
let tmpRetString;
15+
let tmpRetBytes;
16+
let tmpRetException;
17+
return {
18+
/**
19+
* @param {WebAssembly.Imports} importObject
20+
*/
21+
addImports: (importObject, importsContext) => {
22+
const bjs = {};
23+
importObject["bjs"] = bjs;
24+
const imports = options.getImports(importsContext);
25+
bjs["swift_js_return_string"] = function(ptr, len) {
26+
const bytes = new Uint8Array(memory.buffer, ptr, len);
27+
tmpRetString = textDecoder.decode(bytes);
28+
}
29+
bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) {
30+
const source = swift.memory.getObject(sourceId);
31+
const bytes = new Uint8Array(memory.buffer, bytesPtr);
32+
bytes.set(source);
33+
}
34+
bjs["swift_js_make_js_string"] = function(ptr, len) {
35+
const bytes = new Uint8Array(memory.buffer, ptr, len);
36+
return swift.memory.retain(textDecoder.decode(bytes));
37+
}
38+
bjs["swift_js_init_memory_with_result"] = function(ptr, len) {
39+
const target = new Uint8Array(memory.buffer, ptr, len);
40+
target.set(tmpRetBytes);
41+
tmpRetBytes = undefined;
42+
}
43+
bjs["swift_js_throw"] = function(id) {
44+
tmpRetException = swift.memory.retainByRef(id);
45+
}
46+
bjs["swift_js_retain"] = function(id) {
47+
return swift.memory.retainByRef(id);
48+
}
49+
bjs["swift_js_release"] = function(id) {
50+
swift.memory.release(id);
51+
}
52+
53+
const TestModule = importObject["TestModule"] = importObject["TestModule"] || {};
54+
TestModule["bjs_createArrayBuffer"] = function bjs_createArrayBuffer() {
55+
try {
56+
let ret = imports.createArrayBuffer();
57+
return swift.memory.retain(ret);
58+
} catch (error) {
59+
setException(error);
60+
return 0
61+
}
62+
}
63+
TestModule["bjs_createWeirdObject"] = function bjs_createWeirdObject() {
64+
try {
65+
let ret = imports.createWeirdObject();
66+
return swift.memory.retain(ret);
67+
} catch (error) {
68+
setException(error);
69+
return 0
70+
}
71+
}
72+
TestModule["bjs_ArrayBufferLike_byteLength_get"] = function bjs_ArrayBufferLike_byteLength_get(self) {
73+
try {
74+
let ret = swift.memory.getObject(self).byteLength;
75+
return ret;
76+
} catch (error) {
77+
setException(error);
78+
return 0
79+
}
80+
}
81+
TestModule["bjs_ArrayBufferLike_slice"] = function bjs_ArrayBufferLike_slice(self, begin, end) {
82+
try {
83+
let ret = swift.memory.getObject(self).slice(begin, end);
84+
return swift.memory.retain(ret);
85+
} catch (error) {
86+
setException(error);
87+
return 0
88+
}
89+
}
90+
TestModule["bjs_WeirdNaming_normalProperty_get"] = function bjs_WeirdNaming_normalProperty_get(self) {
91+
try {
92+
let ret = swift.memory.getObject(self).normalProperty;
93+
tmpRetBytes = textEncoder.encode(ret);
94+
return tmpRetBytes.length;
95+
} catch (error) {
96+
setException(error);
97+
}
98+
}
99+
TestModule["bjs_WeirdNaming_normalProperty_set"] = function bjs_WeirdNaming_normalProperty_set(self, newValue) {
100+
try {
101+
const newValueObject = swift.memory.getObject(newValue);
102+
swift.memory.release(newValue);
103+
swift.memory.getObject(self).normalProperty = newValueObject;
104+
} catch (error) {
105+
setException(error);
106+
}
107+
}
108+
TestModule["bjs_WeirdNaming_for_get"] = function bjs_WeirdNaming_for_get(self) {
109+
try {
110+
let ret = swift.memory.getObject(self).for;
111+
tmpRetBytes = textEncoder.encode(ret);
112+
return tmpRetBytes.length;
113+
} catch (error) {
114+
setException(error);
115+
}
116+
}
117+
TestModule["bjs_WeirdNaming_for_set"] = function bjs_WeirdNaming_for_set(self, newValue) {
118+
try {
119+
const newValueObject = swift.memory.getObject(newValue);
120+
swift.memory.release(newValue);
121+
swift.memory.getObject(self).for = newValueObject;
122+
} catch (error) {
123+
setException(error);
124+
}
125+
}
126+
TestModule["bjs_WeirdNaming_Any_get"] = function bjs_WeirdNaming_Any_get(self) {
127+
try {
128+
let ret = swift.memory.getObject(self).Any;
129+
tmpRetBytes = textEncoder.encode(ret);
130+
return tmpRetBytes.length;
131+
} catch (error) {
132+
setException(error);
133+
}
134+
}
135+
TestModule["bjs_WeirdNaming_Any_set"] = function bjs_WeirdNaming_Any_set(self, newValue) {
136+
try {
137+
const newValueObject = swift.memory.getObject(newValue);
138+
swift.memory.release(newValue);
139+
swift.memory.getObject(self).Any = newValueObject;
140+
} catch (error) {
141+
setException(error);
142+
}
143+
}
144+
TestModule["bjs_WeirdNaming_as"] = function bjs_WeirdNaming_as(self) {
145+
try {
146+
swift.memory.getObject(self).as();
147+
} catch (error) {
148+
setException(error);
149+
}
150+
}
151+
},
152+
setInstance: (i) => {
153+
instance = i;
154+
memory = instance.exports.memory;
155+
setException = (error) => {
156+
instance.exports._swift_js_exception.value = swift.memory.retain(error)
157+
}
158+
},
159+
/** @param {WebAssembly.Instance} instance */
160+
createExports: (instance) => {
161+
const js = swift.memory.heap;
162+
163+
return {
164+
165+
};
166+
},
167+
}
168+
}

0 commit comments

Comments
 (0)