java-bridge 2.1.3 → 2.1.5-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,583 +0,0 @@
1
- import ts, { SyntaxKind } from 'typescript';
2
- import { importClassAsync, JavaClassInstance } from './.';
3
- import fs from 'fs';
4
- import path from 'path';
5
-
6
- const sourceFile = ts.createSourceFile(
7
- 'source.ts',
8
- '',
9
- ts.ScriptTarget.Latest,
10
- false,
11
- ts.ScriptKind.TS
12
- );
13
-
14
- export interface MethodDeclaration {
15
- returnType: string;
16
- parameters: string[];
17
- isStatic: boolean;
18
- }
19
-
20
- export interface ModuleDeclaration {
21
- name: string;
22
- contents: string;
23
- }
24
-
25
- export type ProgressCallback = (classname: string) => void;
26
-
27
- declare class ModifierClass extends JavaClassInstance {
28
- public static isPublic(val: number): Promise<boolean>;
29
- public static isStatic(val: number): Promise<boolean>;
30
- }
31
-
32
- declare class TypeClass extends JavaClassInstance {
33
- public getTypeName(): Promise<string>;
34
- }
35
-
36
- declare class DeclaredMethodClass extends JavaClassInstance {
37
- public getModifiers(): Promise<number>;
38
- public getName(): Promise<string>;
39
- public getReturnType(): Promise<TypeClass>;
40
- public getParameterTypes(): Promise<TypeClass[]>;
41
- }
42
-
43
- declare class DeclaredConstructorClass extends JavaClassInstance {
44
- public getModifiers(): Promise<number>;
45
- public getParameterTypes(): Promise<TypeClass[]>;
46
- }
47
-
48
- declare class ClassClass extends JavaClassInstance {
49
- public getDeclaredMethods(): Promise<DeclaredMethodClass[]>;
50
- public getDeclaredConstructors(): Promise<DeclaredConstructorClass[]>;
51
- }
52
-
53
- export default class TypescriptDefinitionGenerator {
54
- private usesBasicOrJavaType: boolean = false;
55
- private readonly additionalImports: string[] = [];
56
- private readonly importsToResolve: string[] = [];
57
-
58
- public constructor(
59
- private readonly classname: string,
60
- private readonly progressCallback: ProgressCallback | null = null,
61
- private readonly resolvedImports: string[] = []
62
- ) {}
63
-
64
- private static async convertMethods(
65
- methods: DeclaredMethodClass[]
66
- ): Promise<Record<string, MethodDeclaration[]>> {
67
- const Modifier = await importClassAsync<typeof ModifierClass>(
68
- 'java.lang.reflect.Modifier'
69
- );
70
-
71
- const result: Record<string, MethodDeclaration[]> = {};
72
- for (const method of methods) {
73
- const modifiers = await method.getModifiers();
74
- if (await Modifier.isPublic(modifiers)) {
75
- const name = await method.getName();
76
- const returnType = await method.getReturnType();
77
- const parameterTypes = await method.getParameterTypes();
78
-
79
- const data: MethodDeclaration = {
80
- returnType: await returnType.getTypeName(),
81
- parameters: await Promise.all(
82
- parameterTypes.map((p) => p.getTypeName())
83
- ),
84
- isStatic: await Modifier.isStatic(modifiers),
85
- };
86
-
87
- if (Object.hasOwn(result, name)) {
88
- result[name].push(data);
89
- } else {
90
- result[name] = [data];
91
- }
92
- }
93
- }
94
-
95
- return result;
96
- }
97
-
98
- private async convertConstructors(
99
- constructors: DeclaredConstructorClass[]
100
- ): Promise<ts.ClassElement[]> {
101
- const Modifier = await importClassAsync<typeof ModifierClass>(
102
- 'java.lang.reflect.Modifier'
103
- );
104
- const types: string[][] = [];
105
-
106
- for (const constructor of constructors) {
107
- const modifiers = await constructor.getModifiers();
108
- if (await Modifier.isPublic(modifiers)) {
109
- const parameterTypes = await constructor.getParameterTypes();
110
- types.push(
111
- await Promise.all(
112
- parameterTypes.map((p) => p.getTypeName())
113
- )
114
- );
115
- }
116
- }
117
-
118
- const tsConstructors = types.map((t, i) => {
119
- const params = t.map(this.convertParameter.bind(this));
120
- let declaration = ts.factory.createConstructorDeclaration(
121
- [ts.factory.createModifier(ts.SyntaxKind.PublicKeyword)],
122
- params,
123
- undefined
124
- );
125
- if (i === 0) {
126
- declaration = ts.addSyntheticLeadingComment(
127
- declaration,
128
- ts.SyntaxKind.SingleLineCommentTrivia,
129
- ` ================== Constructors ==================`,
130
- true
131
- );
132
- }
133
-
134
- if (t.length > 0) {
135
- declaration = ts.addSyntheticLeadingComment(
136
- declaration,
137
- ts.SyntaxKind.MultiLineCommentTrivia,
138
- '*\n' +
139
- t
140
- .map(
141
- (p, i) =>
142
- ` * @param var${i} original type: '${p}'\n`
143
- )
144
- .join('') +
145
- ' ',
146
- true
147
- );
148
- }
149
-
150
- return declaration;
151
- });
152
-
153
- const newInstanceMethods = types.map((t, i) => {
154
- return this.createMethod(
155
- {
156
- returnType: this.classname,
157
- parameters: t,
158
- isStatic: true,
159
- },
160
- 'newInstance',
161
- i,
162
- false
163
- );
164
- });
165
-
166
- return [...newInstanceMethods, ...tsConstructors];
167
- }
168
-
169
- private javaTypeToTypescriptType(
170
- javaType: string,
171
- isParam: boolean
172
- ): ts.TypeNode {
173
- switch (javaType) {
174
- case 'byte[]':
175
- case 'java.lang.Byte[]':
176
- return ts.factory.createTypeReferenceNode('Buffer');
177
- }
178
-
179
- if (javaType.endsWith('[]')) {
180
- return ts.factory.createArrayTypeNode(
181
- this.javaTypeToTypescriptType(
182
- javaType.substring(0, javaType.length - 2),
183
- isParam
184
- )
185
- );
186
- }
187
-
188
- switch (javaType) {
189
- case 'int':
190
- case 'java.lang.Integer':
191
- case 'long':
192
- case 'java.lang.Long':
193
- case 'float':
194
- case 'java.lang.Float':
195
- case 'double':
196
- case 'java.lang.Double':
197
- case 'byte':
198
- case 'java.lang.Byte':
199
- case 'short':
200
- case 'java.lang.Short':
201
- return ts.factory.createKeywordTypeNode(
202
- ts.SyntaxKind.NumberKeyword
203
- );
204
- case 'char':
205
- case 'java.lang.Character':
206
- case 'java.lang.String':
207
- return ts.factory.createKeywordTypeNode(
208
- ts.SyntaxKind.StringKeyword
209
- );
210
- case 'boolean':
211
- case 'java.lang.Boolean':
212
- return ts.factory.createKeywordTypeNode(
213
- ts.SyntaxKind.BooleanKeyword
214
- );
215
- case 'void':
216
- case 'java.lang.Void':
217
- return ts.factory.createKeywordTypeNode(
218
- ts.SyntaxKind.VoidKeyword
219
- );
220
- case 'java.lang.Object':
221
- this.usesBasicOrJavaType = true;
222
- return ts.factory.createTypeReferenceNode('BasicOrJavaType');
223
- default:
224
- if (!this.resolvedImports.includes(javaType)) {
225
- this.additionalImports.push(javaType);
226
- }
227
-
228
- this.importsToResolve.push(javaType);
229
- const isSelf = javaType === this.classname && isParam;
230
-
231
- return ts.factory.createTypeReferenceNode(
232
- javaType === this.classname
233
- ? javaType.substring(javaType.lastIndexOf('.') + 1) +
234
- (isSelf ? 'Class' : '')
235
- : javaType.replaceAll('.', '_')
236
- );
237
- }
238
- }
239
-
240
- private convertParameter(
241
- param: string,
242
- index: number
243
- ): ts.ParameterDeclaration {
244
- const name = 'var' + index;
245
- const type = this.javaTypeToTypescriptType(param, true);
246
- return ts.factory.createParameterDeclaration(
247
- undefined,
248
- undefined,
249
- name,
250
- undefined,
251
- type
252
- );
253
- }
254
-
255
- private convertParameters(params: MethodDeclaration) {
256
- return params.parameters.map(this.convertParameter.bind(this));
257
- }
258
-
259
- private static createMethodComment(declaration: MethodDeclaration) {
260
- return (
261
- '*\n' +
262
- declaration.parameters
263
- .map((p, i) => ` * @param var${i} original type: '${p}'\n`)
264
- .join('') +
265
- ` * @return original return type: '${declaration.returnType}'\n `
266
- );
267
- }
268
-
269
- private createMethod(
270
- m: MethodDeclaration,
271
- name: string,
272
- i: number,
273
- isSync: boolean
274
- ): ts.MethodDeclaration {
275
- const publicMod = ts.factory.createModifier(
276
- ts.SyntaxKind.PublicKeyword
277
- );
278
- const staticMod = ts.factory.createModifier(
279
- ts.SyntaxKind.StaticKeyword
280
- );
281
-
282
- const modifiers: ts.Modifier[] = [publicMod];
283
- if (m.isStatic) {
284
- modifiers.push(staticMod);
285
- }
286
-
287
- let returnType = this.javaTypeToTypescriptType(m.returnType, false);
288
- if (!isSync) {
289
- returnType = ts.factory.createTypeReferenceNode(
290
- ts.factory.createIdentifier('Promise'),
291
- [returnType]
292
- );
293
- }
294
-
295
- let declaration = ts.factory.createMethodDeclaration(
296
- modifiers,
297
- undefined,
298
- name + (isSync ? 'Sync' : ''),
299
- undefined,
300
- undefined,
301
- this.convertParameters(m),
302
- returnType,
303
- undefined
304
- );
305
-
306
- if (i === 0) {
307
- declaration = ts.addSyntheticLeadingComment(
308
- declaration,
309
- ts.SyntaxKind.SingleLineCommentTrivia,
310
- ` ================== Method ${name} ==================`,
311
- true
312
- );
313
- }
314
-
315
- return ts.addSyntheticLeadingComment(
316
- declaration,
317
- ts.SyntaxKind.MultiLineCommentTrivia,
318
- TypescriptDefinitionGenerator.createMethodComment(m),
319
- true
320
- );
321
- }
322
-
323
- private convertMethod(
324
- method: MethodDeclaration[],
325
- name: string
326
- ): ts.MethodDeclaration[] {
327
- const res: ts.MethodDeclaration[] = [];
328
-
329
- for (let i = 0; i < method.length; i++) {
330
- const m = method[i];
331
-
332
- res.push(
333
- this.createMethod(m, name, i, false),
334
- this.createMethod(m, name, i, true)
335
- );
336
- }
337
-
338
- return res;
339
- }
340
-
341
- private getAdditionalImports() {
342
- const getPath = (i: string) => {
343
- const thisSplit: (string | null)[] = this.classname.split('.');
344
- const importSplit: (string | null)[] = i.split('.');
345
-
346
- for (let j = 0; j < thisSplit.length; j++) {
347
- if (importSplit[j] === thisSplit[j]) {
348
- thisSplit[j] = null;
349
- importSplit[j] = null;
350
- } else {
351
- break;
352
- }
353
- }
354
-
355
- return (
356
- './' +
357
- thisSplit
358
- .filter((e) => !!e)
359
- .map(() => '')
360
- .join('../') +
361
- importSplit.filter((e) => !!e).join('/')
362
- );
363
- };
364
-
365
- const unique = <T>(value: T, index: number, self: T[]) => {
366
- return self.indexOf(value) === index;
367
- };
368
-
369
- return this.importsToResolve
370
- .filter((i) => i != this.classname)
371
- .filter(unique)
372
- .map((i) =>
373
- ts.factory.createImportDeclaration(
374
- undefined,
375
- ts.factory.createImportClause(
376
- false,
377
- undefined,
378
- ts.factory.createNamedImports([
379
- ts.factory.createImportSpecifier(
380
- false,
381
- ts.factory.createIdentifier(
382
- i.substring(i.lastIndexOf('.') + 1)
383
- ),
384
- ts.factory.createIdentifier(
385
- i.replaceAll('.', '_')
386
- )
387
- ),
388
- ])
389
- ),
390
- ts.factory.createStringLiteral(getPath(i))
391
- )
392
- );
393
- }
394
-
395
- private getImports(): ts.ImportDeclaration {
396
- const importElements = [
397
- ts.factory.createImportSpecifier(
398
- false,
399
- undefined,
400
- ts.factory.createIdentifier('importClass')
401
- ),
402
- ts.factory.createImportSpecifier(
403
- false,
404
- undefined,
405
- ts.factory.createIdentifier('JavaClassInstance')
406
- ),
407
- ];
408
-
409
- if (this.usesBasicOrJavaType) {
410
- importElements.push(
411
- ts.factory.createImportSpecifier(
412
- false,
413
- undefined,
414
- ts.factory.createIdentifier('BasicOrJavaType')
415
- )
416
- );
417
- }
418
-
419
- const imports = ts.factory.createNamedImports(importElements);
420
- return ts.factory.createImportDeclaration(
421
- undefined,
422
- ts.factory.createImportClause(false, undefined, imports),
423
- ts.factory.createStringLiteral('java-bridge')
424
- );
425
- }
426
-
427
- private getExportStatement(simpleName: string) {
428
- const statement = ts.factory.createClassDeclaration(
429
- [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
430
- simpleName,
431
- undefined,
432
- [
433
- ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
434
- ts.factory.createExpressionWithTypeArguments(
435
- ts.factory.createIdentifier(
436
- `importClass<typeof ${simpleName}Class>("${this.classname}")`
437
- ),
438
- undefined
439
- ),
440
- ]),
441
- ],
442
- []
443
- );
444
-
445
- return [
446
- ts.addSyntheticLeadingComment(
447
- statement,
448
- SyntaxKind.MultiLineCommentTrivia,
449
- `*\n * Class ${this.classname}.\n *\n` +
450
- ' * This actually imports the java class for further use.\n' +
451
- ` * The class ${simpleName}Class only defines types, this is the class you should actually import.\n` +
452
- ' * Please note that this statement imports the underlying java class at runtime, which may take a while.\n' +
453
- ' * This was generated by java-bridge.\n * You should probably not edit this.\n ',
454
- true
455
- ),
456
- ts.factory.createExportDefault(
457
- ts.factory.createIdentifier(simpleName)
458
- ),
459
- ];
460
- }
461
-
462
- private getText(nodes: (ts.Node | null)[]) {
463
- return nodes
464
- .map(
465
- (n) =>
466
- (n &&
467
- ts
468
- .createPrinter({ newLine: ts.NewLineKind.LineFeed })
469
- .printNode(
470
- ts.EmitHint.Unspecified,
471
- n,
472
- sourceFile
473
- )) ||
474
- ''
475
- )
476
- .join('\n');
477
- }
478
-
479
- public async generate(): Promise<ModuleDeclaration[]> {
480
- if (this.resolvedImports.includes(this.classname)) {
481
- return [];
482
- }
483
-
484
- this.resolvedImports.push(this.classname);
485
- if (this.progressCallback) {
486
- this.progressCallback(this.classname);
487
- }
488
-
489
- const Class = await importClassAsync(this.classname);
490
- const cls = Class.class as ClassClass;
491
-
492
- const simpleName = this.classname.substring(
493
- this.classname.lastIndexOf('.') + 1
494
- );
495
- const methods = await cls.getDeclaredMethods();
496
-
497
- const classMembers: ts.ClassElement[] = [];
498
-
499
- const convertedMethods =
500
- await TypescriptDefinitionGenerator.convertMethods(methods);
501
- for (const key of Object.keys(convertedMethods)) {
502
- const m = convertedMethods[key];
503
- classMembers.push(...this.convertMethod(m, key));
504
- }
505
-
506
- const constructors = await cls.getDeclaredConstructors();
507
- const convertedConstructors = await this.convertConstructors(
508
- constructors
509
- );
510
- classMembers.push(...convertedConstructors);
511
-
512
- let tsClass = ts.factory.createClassDeclaration(
513
- [
514
- ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
515
- ts.factory.createModifier(ts.SyntaxKind.DeclareKeyword),
516
- ],
517
- simpleName + 'Class',
518
- undefined,
519
- [
520
- ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
521
- ts.factory.createExpressionWithTypeArguments(
522
- ts.factory.createIdentifier('JavaClassInstance'),
523
- undefined
524
- ),
525
- ]),
526
- ],
527
- classMembers
528
- );
529
-
530
- tsClass = ts.addSyntheticLeadingComment(
531
- tsClass,
532
- ts.SyntaxKind.MultiLineCommentTrivia,
533
- `*\n * This class just defines types, you should import ${simpleName} instead of this.\n` +
534
- ' * This was generated by java-bridge.\n * You should probably not edit this.\n ',
535
- true
536
- );
537
-
538
- const sourceText = this.getText([
539
- this.getImports(),
540
- ...this.getAdditionalImports(),
541
- null,
542
- tsClass,
543
- null,
544
- ...this.getExportStatement(simpleName),
545
- ]);
546
-
547
- const res: ModuleDeclaration[] = [];
548
- for (const imported of this.additionalImports) {
549
- const generator = new TypescriptDefinitionGenerator(
550
- imported,
551
- this.progressCallback,
552
- this.resolvedImports
553
- );
554
- const generated = await generator.generate();
555
- res.push(...generated);
556
- }
557
-
558
- res.push({
559
- name: this.classname,
560
- contents: sourceText,
561
- });
562
-
563
- return res;
564
- }
565
-
566
- public static async save(
567
- declarations: ModuleDeclaration[],
568
- sourceDir: string
569
- ): Promise<void> {
570
- for (const declaration of declarations) {
571
- const p = declaration.name.split('.');
572
- p[p.length - 1] = p[p.length - 1] + '.ts';
573
-
574
- const filePath = path.join(sourceDir, ...p);
575
- await fs.promises.mkdir(path.dirname(filePath), {
576
- recursive: true,
577
- });
578
- await fs.promises.writeFile(filePath, declaration.contents, {
579
- encoding: 'utf8',
580
- });
581
- }
582
- }
583
- }
@@ -1,86 +0,0 @@
1
- import path from 'path';
2
- import fs, { readFileSync } from 'fs';
3
- import glob from 'glob';
4
-
5
- const { platform, arch } = process;
6
-
7
- function getModule(base: string): string {
8
- const local = path.join(__dirname, base + '.node');
9
-
10
- if (fs.existsSync(local)) {
11
- return local;
12
- } else {
13
- const module = base.replaceAll('.', '-').replace('java', 'java-bridge');
14
- // @ts-ignore
15
- if (__non_webpack_require__ && __non_webpack_require__.resolve) {
16
- // @ts-ignore
17
- return __non_webpack_require__.resolve(module);
18
- } else {
19
- return require.resolve(module);
20
- }
21
- }
22
- }
23
-
24
- function UnsupportedPlatform(): Error {
25
- return new Error(`Unsupported platform: ${platform} ${arch}`);
26
- }
27
-
28
- function isMusl() {
29
- // For Node 10
30
- if (!process.report || typeof process.report.getReport !== 'function') {
31
- try {
32
- return readFileSync('/usr/bin/ldd', 'utf8').includes('musl');
33
- } catch (e) {
34
- return true;
35
- }
36
- } else {
37
- const { glibcVersionRuntime } = (process.report.getReport() as any)
38
- .header;
39
- return !glibcVersionRuntime;
40
- }
41
- }
42
-
43
- export function getNativeLibPath(): string {
44
- switch (platform) {
45
- case 'android':
46
- switch (arch) {
47
- case 'arm64':
48
- return getModule('java.android-arm64');
49
- case 'arm':
50
- return getModule('java.android-arm-eabi');
51
- default:
52
- throw UnsupportedPlatform();
53
- }
54
- case 'win32':
55
- return getModule(`java.win32-${arch}-msvc`);
56
- case 'darwin':
57
- return getModule(`java.darwin-${arch}`);
58
- case 'freebsd':
59
- return getModule(`java.freebsd-${arch}`);
60
- case 'linux':
61
- switch (arch) {
62
- case 'x64':
63
- case 'arm64':
64
- return getModule(
65
- `java.linux-${arch}-${isMusl() ? 'musl' : 'gnu'}`
66
- );
67
- case 'arm':
68
- return getModule('java.linux-arm-gnueabihf');
69
- default:
70
- throw UnsupportedPlatform();
71
- }
72
- default:
73
- throw UnsupportedPlatform();
74
- }
75
- }
76
-
77
- export function getJavaLibPath(): string {
78
- const dir = path.join(__dirname, '..', 'java-src', 'build', 'libs');
79
- let files = glob.sync('*.jar', { cwd: dir });
80
-
81
- if (files.length === 0) {
82
- throw new Error(`No java lib found in ${dir}`);
83
- } else {
84
- return path.join(dir, files[0]);
85
- }
86
- }