jsii-pacmak 1.63.2 → 1.65.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.
- package/bin/jsii-pacmak.js +10 -0
- package/lib/builder.d.ts +12 -7
- package/lib/builder.js +4 -3
- package/lib/generator.d.ts +7 -1
- package/lib/generator.js +4 -1
- package/lib/index.d.ts +7 -1
- package/lib/index.js +7 -5
- package/lib/target.d.ts +3 -0
- package/lib/target.js +4 -3
- package/lib/targets/dotnet/dotnetgenerator.d.ts +12 -2
- package/lib/targets/dotnet/dotnetgenerator.js +278 -14
- package/lib/targets/dotnet/dotnettyperesolver.d.ts +12 -0
- package/lib/targets/dotnet/dotnettyperesolver.js +54 -0
- package/lib/targets/dotnet/filegenerator.d.ts +1 -1
- package/lib/targets/dotnet/filegenerator.js +14 -1
- package/lib/targets/dotnet.js +5 -4
- package/lib/targets/go/package.d.ts +0 -3
- package/lib/targets/go/package.js +12 -28
- package/lib/targets/go/types/go-type-reference.js +6 -3
- package/lib/targets/go/types/interface.d.ts +2 -2
- package/lib/targets/go/types/interface.js +12 -3
- package/lib/targets/go/util.d.ts +1 -1
- package/lib/targets/go/util.js +2 -12
- package/lib/targets/java.d.ts +5 -2
- package/lib/targets/java.js +8 -7
- package/lib/targets/js.d.ts +1 -0
- package/lib/targets/js.js +4 -0
- package/lib/targets/python/type-name.js +4 -4
- package/lib/targets/python.d.ts +3 -1
- package/lib/targets/python.js +43 -37
- package/lib/targets/version-utils.js +1 -1
- package/lib/version.d.ts +2 -2
- package/lib/version.js +3 -3
- package/package.json +16 -13
package/bin/jsii-pacmak.js
CHANGED
|
@@ -36,6 +36,15 @@ const version_1 = require("../lib/version");
|
|
|
36
36
|
type: 'boolean',
|
|
37
37
|
desc: 'generate code only (instead of building and packaging)',
|
|
38
38
|
default: false,
|
|
39
|
+
})
|
|
40
|
+
.option('runtime-type-checking', {
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
desc: [
|
|
43
|
+
'generate runtime type checking code where compile-time type checking is not possible.',
|
|
44
|
+
'Disabling this will generate less code, but will produce less helpful error messages when',
|
|
45
|
+
'developers pass invalid values to the generated bindings.',
|
|
46
|
+
].join(' '),
|
|
47
|
+
default: true,
|
|
39
48
|
})
|
|
40
49
|
.option('fingerprint', {
|
|
41
50
|
type: 'boolean',
|
|
@@ -154,6 +163,7 @@ const version_1 = require("../lib/version");
|
|
|
154
163
|
recurse: argv.recurse,
|
|
155
164
|
rosettaUnknownSnippets,
|
|
156
165
|
rosettaTablet: argv['rosetta-tablet'],
|
|
166
|
+
runtimeTypeChecking: argv['runtime-type-checking'],
|
|
157
167
|
targets: argv.targets?.map((target) => target),
|
|
158
168
|
updateNpmIgnoreFiles: argv.npmignore,
|
|
159
169
|
validateAssemblies: argv['validate-assemblies'],
|
package/lib/builder.d.ts
CHANGED
|
@@ -8,34 +8,39 @@ export interface BuildOptions {
|
|
|
8
8
|
* Whether to fingerprint the produced artifacts.
|
|
9
9
|
* @default true
|
|
10
10
|
*/
|
|
11
|
-
fingerprint?: boolean;
|
|
11
|
+
readonly fingerprint?: boolean;
|
|
12
12
|
/**
|
|
13
13
|
* Whether artifacts should be re-build even if their fingerprints look up-to-date.
|
|
14
14
|
* @default false
|
|
15
15
|
*/
|
|
16
|
-
force?: boolean;
|
|
16
|
+
readonly force?: boolean;
|
|
17
17
|
/**
|
|
18
18
|
* Arguments provided by the user (how they are used is target-dependent)
|
|
19
19
|
*/
|
|
20
|
-
arguments: {
|
|
20
|
+
readonly arguments: {
|
|
21
21
|
readonly [name: string]: any;
|
|
22
22
|
};
|
|
23
23
|
/**
|
|
24
24
|
* Only generate code, don't build
|
|
25
25
|
*/
|
|
26
|
-
codeOnly?: boolean;
|
|
26
|
+
readonly codeOnly?: boolean;
|
|
27
27
|
/**
|
|
28
28
|
* Whether or not to clean
|
|
29
29
|
*/
|
|
30
|
-
clean?: boolean;
|
|
30
|
+
readonly clean?: boolean;
|
|
31
31
|
/**
|
|
32
32
|
* Whether to add an additional subdirectory for the target language
|
|
33
33
|
*/
|
|
34
|
-
languageSubdirectory?: boolean;
|
|
34
|
+
readonly languageSubdirectory?: boolean;
|
|
35
35
|
/**
|
|
36
36
|
* The Rosetta instance to load examples from
|
|
37
37
|
*/
|
|
38
|
-
rosetta: Rosetta;
|
|
38
|
+
readonly rosetta: Rosetta;
|
|
39
|
+
/**
|
|
40
|
+
* Whether to generate runtime type checking code in places where compile-time
|
|
41
|
+
* type checking is not possible.
|
|
42
|
+
*/
|
|
43
|
+
readonly runtimeTypeChecking: boolean;
|
|
39
44
|
}
|
|
40
45
|
/**
|
|
41
46
|
* Interface for classes that can build language targets
|
package/lib/builder.js
CHANGED
|
@@ -63,13 +63,14 @@ class IndependentPackageBuilder {
|
|
|
63
63
|
}
|
|
64
64
|
makeTarget(module, options) {
|
|
65
65
|
return new this.targetConstructor({
|
|
66
|
-
|
|
67
|
-
packageDir: module.moduleDirectory,
|
|
66
|
+
arguments: options.arguments,
|
|
68
67
|
assembly: module.assembly,
|
|
69
68
|
fingerprint: options.fingerprint,
|
|
70
69
|
force: options.force,
|
|
71
|
-
|
|
70
|
+
packageDir: module.moduleDirectory,
|
|
72
71
|
rosetta: options.rosetta,
|
|
72
|
+
runtimeTypeChecking: options.runtimeTypeChecking,
|
|
73
|
+
targetName: this.targetName,
|
|
73
74
|
});
|
|
74
75
|
}
|
|
75
76
|
finalOutputDir(module, options) {
|
package/lib/generator.d.ts
CHANGED
|
@@ -20,6 +20,11 @@ export interface GeneratorOptions {
|
|
|
20
20
|
* If this property is set, the generator will add "Base" to abstract class names
|
|
21
21
|
*/
|
|
22
22
|
addBasePostfixToAbstractClassNames?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* If this property is set, the generator will add runtime type checking code in places
|
|
25
|
+
* where compile-time type checking is not possible.
|
|
26
|
+
*/
|
|
27
|
+
runtimeTypeChecking: boolean;
|
|
23
28
|
}
|
|
24
29
|
export interface IGenerator {
|
|
25
30
|
/**
|
|
@@ -72,7 +77,8 @@ export declare abstract class Generator implements IGenerator {
|
|
|
72
77
|
private _assembly?;
|
|
73
78
|
protected _reflectAssembly?: reflect.Assembly;
|
|
74
79
|
private fingerprint?;
|
|
75
|
-
constructor(options
|
|
80
|
+
constructor(options: GeneratorOptions);
|
|
81
|
+
protected get runtimeTypeChecking(): boolean;
|
|
76
82
|
protected get assembly(): spec.Assembly;
|
|
77
83
|
get reflectAssembly(): reflect.Assembly;
|
|
78
84
|
get metadata(): {
|
package/lib/generator.js
CHANGED
|
@@ -13,11 +13,14 @@ const version_1 = require("./version");
|
|
|
13
13
|
* Given a jsii module, it will invoke "events" to emit various elements.
|
|
14
14
|
*/
|
|
15
15
|
class Generator {
|
|
16
|
-
constructor(options
|
|
16
|
+
constructor(options) {
|
|
17
17
|
this.options = options;
|
|
18
18
|
this.excludeTypes = new Array();
|
|
19
19
|
this.code = new codemaker_1.CodeMaker();
|
|
20
20
|
}
|
|
21
|
+
get runtimeTypeChecking() {
|
|
22
|
+
return this.options.runtimeTypeChecking;
|
|
23
|
+
}
|
|
21
24
|
get assembly() {
|
|
22
25
|
if (!this._assembly) {
|
|
23
26
|
throw new Error('No assembly has been loaded! The #load() method must be called first!');
|
package/lib/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { configure as configureLogging } from './logging';
|
|
|
6
6
|
/**
|
|
7
7
|
* Generates code in the desired targets.
|
|
8
8
|
*/
|
|
9
|
-
export declare function pacmak({ argv, clean, codeOnly, fingerprint, force, forceSubdirectory, forceTarget, inputDirectories, outputDirectory, parallel, recurse, rosettaTablet, targets, timers,
|
|
9
|
+
export declare function pacmak({ argv, clean, codeOnly, fingerprint, force, forceSubdirectory, forceTarget, inputDirectories, outputDirectory, parallel, recurse, rosettaTablet, rosettaUnknownSnippets, runtimeTypeChecking, targets, timers, updateNpmIgnoreFiles, validateAssemblies, }: PacmakOptions): Promise<void>;
|
|
10
10
|
/**
|
|
11
11
|
* Options provided to the `pacmak` function.
|
|
12
12
|
*/
|
|
@@ -93,6 +93,12 @@ export interface PacmakOptions {
|
|
|
93
93
|
* @default undefined
|
|
94
94
|
*/
|
|
95
95
|
readonly rosettaTablet?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Whether to inject runtime type checks in places where compile-time type checking is not performed.
|
|
98
|
+
*
|
|
99
|
+
* @default true
|
|
100
|
+
*/
|
|
101
|
+
readonly runtimeTypeChecking?: boolean;
|
|
96
102
|
/**
|
|
97
103
|
* The list of targets for which code should be generated. Unless `forceTarget` is `true`, a given target will only
|
|
98
104
|
* be generated for assemblies that have configured it.
|
package/lib/index.js
CHANGED
|
@@ -16,7 +16,7 @@ Object.defineProperty(exports, "configureLogging", { enumerable: true, get: func
|
|
|
16
16
|
/**
|
|
17
17
|
* Generates code in the desired targets.
|
|
18
18
|
*/
|
|
19
|
-
async function pacmak({ argv = {}, clean = true, codeOnly = false, fingerprint = true, force = false, forceSubdirectory = true, forceTarget = false, inputDirectories, outputDirectory, parallel = true, recurse = false, rosettaTablet, targets = Object.values(targets_1.TargetName), timers = new timer_1.Timers(),
|
|
19
|
+
async function pacmak({ argv = {}, clean = true, codeOnly = false, fingerprint = true, force = false, forceSubdirectory = true, forceTarget = false, inputDirectories, outputDirectory, parallel = true, recurse = false, rosettaTablet, rosettaUnknownSnippets = undefined, runtimeTypeChecking = true, targets = Object.values(targets_1.TargetName), timers = new timer_1.Timers(), updateNpmIgnoreFiles = false, validateAssemblies = false, }) {
|
|
20
20
|
const rosetta = new jsii_rosetta_1.Rosetta({
|
|
21
21
|
unknownSnippets: rosettaUnknownSnippets,
|
|
22
22
|
prefixDisclaimer: true,
|
|
@@ -74,6 +74,7 @@ async function pacmak({ argv = {}, clean = true, codeOnly = false, fingerprint =
|
|
|
74
74
|
force,
|
|
75
75
|
perLanguageDirectory,
|
|
76
76
|
rosetta,
|
|
77
|
+
runtimeTypeChecking,
|
|
77
78
|
}))
|
|
78
79
|
.then(() => logging.info(`${targetSet.targetType} finished`), (err) => {
|
|
79
80
|
logging.warn(`${targetSet.targetType} failed`);
|
|
@@ -95,20 +96,21 @@ async function pacmak({ argv = {}, clean = true, codeOnly = false, fingerprint =
|
|
|
95
96
|
exports.pacmak = pacmak;
|
|
96
97
|
//#endregion
|
|
97
98
|
//#region Building
|
|
98
|
-
async function buildTargetsForLanguage(targetLanguage, modules, { argv, clean, codeOnly, fingerprint, force, perLanguageDirectory, rosetta, }) {
|
|
99
|
+
async function buildTargetsForLanguage(targetLanguage, modules, { argv, clean, codeOnly, fingerprint, force, perLanguageDirectory, rosetta, runtimeTypeChecking, }) {
|
|
99
100
|
// ``argv.target`` is guaranteed valid by ``yargs`` through the ``choices`` directive.
|
|
100
101
|
const factory = targets_1.ALL_BUILDERS[targetLanguage];
|
|
101
102
|
if (!factory) {
|
|
102
103
|
throw new Error(`Unsupported target: '${targetLanguage}'`);
|
|
103
104
|
}
|
|
104
105
|
return factory(modules, {
|
|
106
|
+
arguments: argv,
|
|
105
107
|
clean: clean,
|
|
106
108
|
codeOnly: codeOnly,
|
|
107
|
-
rosetta,
|
|
108
|
-
force: force,
|
|
109
109
|
fingerprint: fingerprint,
|
|
110
|
-
|
|
110
|
+
force: force,
|
|
111
111
|
languageSubdirectory: perLanguageDirectory,
|
|
112
|
+
rosetta,
|
|
113
|
+
runtimeTypeChecking,
|
|
112
114
|
}).buildModules();
|
|
113
115
|
}
|
|
114
116
|
function sliceTargets(modulesSorted, requestedTargets, force) {
|
package/lib/target.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare abstract class Target {
|
|
|
12
12
|
protected readonly targetName: string;
|
|
13
13
|
protected readonly assembly: reflect.Assembly;
|
|
14
14
|
protected readonly rosetta: Rosetta;
|
|
15
|
+
protected readonly runtimeTypeChecking: boolean;
|
|
15
16
|
protected abstract readonly generator: IGenerator;
|
|
16
17
|
constructor(options: TargetOptions);
|
|
17
18
|
/**
|
|
@@ -125,6 +126,8 @@ export interface TargetOptions {
|
|
|
125
126
|
assembly: reflect.Assembly;
|
|
126
127
|
/** The Rosetta instance */
|
|
127
128
|
rosetta: Rosetta;
|
|
129
|
+
/** Whether to generate runtime type-checking code */
|
|
130
|
+
runtimeTypeChecking: boolean;
|
|
128
131
|
/**
|
|
129
132
|
* Whether to fingerprint the produced artifacts.
|
|
130
133
|
* @default true
|
package/lib/target.js
CHANGED
|
@@ -8,12 +8,13 @@ const dependency_graph_1 = require("./dependency-graph");
|
|
|
8
8
|
const logging = require("./logging");
|
|
9
9
|
class Target {
|
|
10
10
|
constructor(options) {
|
|
11
|
-
this.
|
|
11
|
+
this.arguments = options.arguments;
|
|
12
12
|
this.assembly = options.assembly;
|
|
13
|
-
this.rosetta = options.rosetta;
|
|
14
13
|
this.fingerprint = options.fingerprint ?? true;
|
|
15
14
|
this.force = options.force ?? false;
|
|
16
|
-
this.
|
|
15
|
+
this.packageDir = options.packageDir;
|
|
16
|
+
this.rosetta = options.rosetta;
|
|
17
|
+
this.runtimeTypeChecking = options.runtimeTypeChecking;
|
|
17
18
|
this.targetName = options.targetName;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
@@ -7,13 +7,16 @@ import { Generator, Legalese } from '../../generator';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class DotNetGenerator extends Generator {
|
|
9
9
|
private readonly assembliesCurrentlyBeingCompiled;
|
|
10
|
+
private readonly nameutils;
|
|
10
11
|
private readonly rosetta;
|
|
11
12
|
private firstMemberWritten;
|
|
12
13
|
private typeresolver;
|
|
13
|
-
private readonly nameutils;
|
|
14
14
|
private dotnetRuntimeGenerator;
|
|
15
15
|
private dotnetDocGenerator;
|
|
16
|
-
constructor(assembliesCurrentlyBeingCompiled: string[],
|
|
16
|
+
constructor(assembliesCurrentlyBeingCompiled: string[], options: {
|
|
17
|
+
readonly rosetta: Rosetta;
|
|
18
|
+
readonly runtimeTypeChecking: boolean;
|
|
19
|
+
});
|
|
17
20
|
load(packageRoot: string, assembly: reflect.Assembly): Promise<void>;
|
|
18
21
|
/**
|
|
19
22
|
* Runs the generator (in-memory).
|
|
@@ -55,6 +58,13 @@ export declare class DotNetGenerator extends Generator {
|
|
|
55
58
|
protected onEnumMember(enm: spec.EnumType, member: spec.EnumMember): void;
|
|
56
59
|
private namespaceFor;
|
|
57
60
|
private emitMethod;
|
|
61
|
+
/**
|
|
62
|
+
* Emits type checks for values passed for type union parameters.
|
|
63
|
+
*
|
|
64
|
+
* @param parameters the list of parameters received by the function.
|
|
65
|
+
* @param noMangle use parameter names as-is (useful for setters, for example) instead of mangling them.
|
|
66
|
+
*/
|
|
67
|
+
private emitUnionParameterValdation;
|
|
58
68
|
/**
|
|
59
69
|
* Founds out if a member (property or method) is already defined in one of the base classes
|
|
60
70
|
*
|
|
@@ -3,9 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DotNetGenerator = void 0;
|
|
4
4
|
const spec = require("@jsii/spec");
|
|
5
5
|
const clone = require("clone");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
6
7
|
const fs = require("fs-extra");
|
|
8
|
+
const http = require("http");
|
|
9
|
+
const https = require("https");
|
|
7
10
|
const path = require("path");
|
|
8
11
|
const generator_1 = require("../../generator");
|
|
12
|
+
const logging_1 = require("../../logging");
|
|
9
13
|
const dotnetdocgenerator_1 = require("./dotnetdocgenerator");
|
|
10
14
|
const dotnetruntimegenerator_1 = require("./dotnetruntimegenerator");
|
|
11
15
|
const dotnettyperesolver_1 = require("./dotnettyperesolver");
|
|
@@ -15,18 +19,18 @@ const nameutils_1 = require("./nameutils");
|
|
|
15
19
|
* CODE GENERATOR V2
|
|
16
20
|
*/
|
|
17
21
|
class DotNetGenerator extends generator_1.Generator {
|
|
18
|
-
constructor(assembliesCurrentlyBeingCompiled,
|
|
19
|
-
super();
|
|
22
|
+
constructor(assembliesCurrentlyBeingCompiled, options) {
|
|
23
|
+
super(options);
|
|
20
24
|
this.assembliesCurrentlyBeingCompiled = assembliesCurrentlyBeingCompiled;
|
|
21
|
-
this.
|
|
25
|
+
this.nameutils = new nameutils_1.DotNetNameUtils();
|
|
22
26
|
// Flags that tracks if we have already wrote the first member of the class
|
|
23
27
|
this.firstMemberWritten = false;
|
|
24
|
-
this.nameutils = new nameutils_1.DotNetNameUtils();
|
|
25
28
|
// Override the openBlock to get a correct C# looking code block with the curly brace after the line
|
|
26
29
|
this.code.openBlock = function (text) {
|
|
27
30
|
this.line(text);
|
|
28
31
|
this.open('{');
|
|
29
32
|
};
|
|
33
|
+
this.rosetta = options.rosetta;
|
|
30
34
|
}
|
|
31
35
|
async load(packageRoot, assembly) {
|
|
32
36
|
await super.load(packageRoot, assembly);
|
|
@@ -48,7 +52,6 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
48
52
|
const tarballFileName = path.basename(tarball);
|
|
49
53
|
const filegen = new filegenerator_1.FileGenerator(this.assembly, tarballFileName, this.code);
|
|
50
54
|
filegen.generateAssemblyInfoFile();
|
|
51
|
-
filegen.generateProjectFile(this.typeresolver.namespaceDependencies);
|
|
52
55
|
// Calling super.save() dumps the tarball in the format name@version.jsii.tgz.
|
|
53
56
|
// This is not in sync with the Old .NET generator where the name is scope-name-version.tgz.
|
|
54
57
|
// Hence we are saving the files ourselves here:
|
|
@@ -59,6 +62,17 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
59
62
|
}
|
|
60
63
|
await fs.mkdirp(path.join(outdir, packageId));
|
|
61
64
|
await fs.copyFile(tarball, path.join(outdir, packageId, tarballFileName));
|
|
65
|
+
// Attempt to download the package icon from the configured URL so we can use the non-deprecated PackageIcon
|
|
66
|
+
// attribute. If this fails or is opted out (via $JSII_PACMAK_DOTNET_NO_DOWNLOAD_ICON being set), then only the
|
|
67
|
+
// deprecated PackageIconUrl will be emitted.
|
|
68
|
+
const iconFile = this.assembly.targets?.dotnet?.iconUrl != null &&
|
|
69
|
+
!process.env.JSII_PACMAK_DOTNET_NO_DOWNLOAD_ICON
|
|
70
|
+
? await tryDownloadResource(this.assembly.targets.dotnet.iconUrl, path.join(outdir, packageId)).catch((err) => {
|
|
71
|
+
(0, logging_1.debug)(`[dotnet] Unable to download package icon, will only use deprecated PackageIconUrl attribute: ${err.cause}`);
|
|
72
|
+
return Promise.resolve(undefined);
|
|
73
|
+
})
|
|
74
|
+
: undefined;
|
|
75
|
+
filegen.generateProjectFile(this.typeresolver.namespaceDependencies, iconFile);
|
|
62
76
|
// Create an anchor file for the current model
|
|
63
77
|
this.generateDependencyAnchorFile();
|
|
64
78
|
if (license) {
|
|
@@ -251,10 +265,18 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
251
265
|
// Create the constructors:
|
|
252
266
|
// Abstract classes have protected constructors.
|
|
253
267
|
const visibility = cls.abstract ? 'protected' : 'public';
|
|
268
|
+
this.code.openBlock(`${visibility} ${className}(${parametersDefinition}): base(_MakeDeputyProps(${parametersBase}))`);
|
|
269
|
+
this.code.closeBlock();
|
|
270
|
+
this.code.line();
|
|
271
|
+
// This private method is injected so we can validate arguments before deferring to the base constructor, where
|
|
272
|
+
// the instance will be created in the kernel (where it'd fail on a sub-optimal error instead)...
|
|
273
|
+
this.code.line('[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]');
|
|
274
|
+
this.code.openBlock(`private static DeputyProps _MakeDeputyProps(${parametersDefinition})`);
|
|
275
|
+
this.emitUnionParameterValdation(initializer.parameters);
|
|
254
276
|
const args = parametersBase.length > 0
|
|
255
277
|
? `new object?[]{${parametersBase}}`
|
|
256
278
|
: `System.Array.Empty<object?>()`;
|
|
257
|
-
this.code.
|
|
279
|
+
this.code.line(`return new DeputyProps(${args});`);
|
|
258
280
|
this.code.closeBlock();
|
|
259
281
|
this.code.line();
|
|
260
282
|
}
|
|
@@ -410,10 +432,125 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
410
432
|
}
|
|
411
433
|
else {
|
|
412
434
|
this.code.openBlock(`${access} ${staticKeyWord}${overrideKeyWord}${virtualKeyWord}${signature}`);
|
|
435
|
+
this.emitUnionParameterValdation(method.parameters);
|
|
413
436
|
this.code.line(this.dotnetRuntimeGenerator.createInvokeMethodIdentifier(method, cls));
|
|
414
437
|
this.code.closeBlock();
|
|
415
438
|
}
|
|
416
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Emits type checks for values passed for type union parameters.
|
|
442
|
+
*
|
|
443
|
+
* @param parameters the list of parameters received by the function.
|
|
444
|
+
* @param noMangle use parameter names as-is (useful for setters, for example) instead of mangling them.
|
|
445
|
+
*/
|
|
446
|
+
emitUnionParameterValdation(parameters, { noMangle = false } = {}) {
|
|
447
|
+
if (!this.runtimeTypeChecking) {
|
|
448
|
+
// We were configured not to emit those, so bail out now.
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const unionParameters = parameters?.filter(({ type }) => containsUnionType(type));
|
|
452
|
+
if (unionParameters == null || unionParameters.length === 0) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
this.code.openBlock('if (Amazon.JSII.Runtime.Configuration.RuntimeTypeChecking)');
|
|
456
|
+
for (const param of unionParameters) {
|
|
457
|
+
const name = noMangle
|
|
458
|
+
? param.name
|
|
459
|
+
: this.nameutils.convertParameterName(param.name);
|
|
460
|
+
if (param.optional) {
|
|
461
|
+
this.code.openBlock(`if (${name} != null)`);
|
|
462
|
+
}
|
|
463
|
+
validate.call(this, name, noMangle ? name : `argument {nameof(${name})}`, param.type, noMangle ? name : `{nameof(${name})}`);
|
|
464
|
+
if (param.optional) {
|
|
465
|
+
this.code.closeBlock();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
this.code.closeBlock();
|
|
469
|
+
function validate(value, descr, type, parameterName) {
|
|
470
|
+
if (spec.isUnionTypeReference(type)) {
|
|
471
|
+
validateTypeUnion.call(this, value, descr, type, parameterName);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
const collectionType = type;
|
|
475
|
+
if (collectionType.collection.kind === spec.CollectionKind.Array) {
|
|
476
|
+
validateArray.call(this, value, descr, collectionType.collection.elementtype, parameterName);
|
|
477
|
+
}
|
|
478
|
+
else if (collectionType.collection.kind === spec.CollectionKind.Map) {
|
|
479
|
+
validateMap.call(this, value, descr, collectionType.collection.elementtype, parameterName);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
throw new Error(`Unhandled collection kind: ${spec.describeTypeReference(type)}`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function validateArray(value, descr, elementType, parameterName) {
|
|
487
|
+
const varName = `__idx_${(0, crypto_1.createHash)('sha256')
|
|
488
|
+
.update(descr)
|
|
489
|
+
.digest('hex')
|
|
490
|
+
.slice(0, 6)}`;
|
|
491
|
+
this.code.openBlock(`for (int ${varName} = 0 ; ${varName} < ${value}.Length ; ${varName}++)`);
|
|
492
|
+
validate.call(this, `${value}[${varName}]`, `${descr}[{${varName}}]`, elementType, parameterName);
|
|
493
|
+
this.code.closeBlock();
|
|
494
|
+
}
|
|
495
|
+
function validateMap(value, descr, elementType, parameterName) {
|
|
496
|
+
const varName = `__item_${(0, crypto_1.createHash)('sha256')
|
|
497
|
+
.update(descr)
|
|
498
|
+
.digest('hex')
|
|
499
|
+
.slice(0, 6)}`;
|
|
500
|
+
this.code.openBlock(`foreach (var ${varName} in ${value})`);
|
|
501
|
+
validate.call(this, `${varName}.Value`, `${descr}[\\"{${varName}.Key}\\"]`, elementType, parameterName);
|
|
502
|
+
this.code.closeBlock();
|
|
503
|
+
}
|
|
504
|
+
function validateTypeUnion(value, descr, type, parameterName) {
|
|
505
|
+
this.code.indent('if (');
|
|
506
|
+
let emitAnd = false;
|
|
507
|
+
const typeRefs = type.union.types;
|
|
508
|
+
for (const typeRef of typeRefs) {
|
|
509
|
+
const prefix = emitAnd ? '&& ' : '';
|
|
510
|
+
const dotNetType = this.typeresolver.toDotNetType(typeRef);
|
|
511
|
+
// In the case of double, we test for all standard numeric types of .NET (these implicitly convert).
|
|
512
|
+
const test = dotNetType === 'double'
|
|
513
|
+
? [
|
|
514
|
+
'byte',
|
|
515
|
+
'decimal',
|
|
516
|
+
'double',
|
|
517
|
+
'float',
|
|
518
|
+
'int',
|
|
519
|
+
'long',
|
|
520
|
+
'sbyte',
|
|
521
|
+
'short',
|
|
522
|
+
'uint',
|
|
523
|
+
'ulong',
|
|
524
|
+
'ushort',
|
|
525
|
+
]
|
|
526
|
+
.map((numeric) => `${value} is ${numeric}`)
|
|
527
|
+
.join(' || ')
|
|
528
|
+
: `${value} is ${dotNetType}`;
|
|
529
|
+
this.code.line(`${prefix}!(${test})`);
|
|
530
|
+
emitAnd = true;
|
|
531
|
+
}
|
|
532
|
+
if (typeRefs.some((ref) => spec.isNamedTypeReference(ref) &&
|
|
533
|
+
spec.isInterfaceType(this.findType(ref.fqn)))) {
|
|
534
|
+
// AnonymousObject will convert to any interface type, even if unsafely. It is the opaque
|
|
535
|
+
// type returned when a non-intrinsically typed value is passed through an any or union
|
|
536
|
+
// return point. We basically cannot type-check that at runtime in a complete way.
|
|
537
|
+
this.code.line(`&& !(${value} is Amazon.JSII.Runtime.Deputy.AnonymousObject)`);
|
|
538
|
+
}
|
|
539
|
+
this.code.unindent(')');
|
|
540
|
+
this.code.openBlock('');
|
|
541
|
+
const placeholders = typeRefs
|
|
542
|
+
.map((typeRef) => {
|
|
543
|
+
const typeName = this.typeresolver.toDotNetTypeName(typeRef);
|
|
544
|
+
if (typeName.startsWith('"') && typeName.endsWith('"')) {
|
|
545
|
+
return typeName.slice(1, -1);
|
|
546
|
+
}
|
|
547
|
+
return `{${typeName}}`;
|
|
548
|
+
})
|
|
549
|
+
.join(', ');
|
|
550
|
+
this.code.line(`throw new System.ArgumentException($"Expected ${descr} to be one of: ${placeholders}; received {${value}.GetType().FullName}", $"${parameterName}");`);
|
|
551
|
+
this.code.closeBlock();
|
|
552
|
+
}
|
|
553
|
+
}
|
|
417
554
|
/**
|
|
418
555
|
* Founds out if a member (property or method) is already defined in one of the base classes
|
|
419
556
|
*
|
|
@@ -546,7 +683,8 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
546
683
|
: // An abstract class could extend a concrete class... We must walk up the inheritance tree in this case...
|
|
547
684
|
this.proxyMustUseNewModifier(base)));
|
|
548
685
|
}
|
|
549
|
-
return (type.interfaces
|
|
686
|
+
return (type.interfaces != null &&
|
|
687
|
+
type.interfaces.some((fqn) => this.findType(fqn).assembly === type.assembly));
|
|
550
688
|
}
|
|
551
689
|
/**
|
|
552
690
|
* Emits an Interface Datatype class
|
|
@@ -670,6 +808,17 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
670
808
|
const access = this.renderAccessLevel(prop);
|
|
671
809
|
const staticKeyWord = prop.static ? 'static ' : '';
|
|
672
810
|
const propName = this.nameutils.convertPropertyName(prop.name);
|
|
811
|
+
const propTypeFQN = this.typeresolver.toDotNetType(prop.type);
|
|
812
|
+
const isOptional = prop.optional ? '?' : '';
|
|
813
|
+
// We need to use a backing field so we can perform type checking if the property type is a union, and this is a struct.
|
|
814
|
+
const backingFieldName = spec.isInterfaceType(cls) && datatype && containsUnionType(prop.type)
|
|
815
|
+
? // We down-case the first letter, private fields are conventionally named with a _ prefix, and a camelCase name.
|
|
816
|
+
`_${propName.replace(/[A-Z]/, (c) => c.toLowerCase())}`
|
|
817
|
+
: undefined;
|
|
818
|
+
if (backingFieldName != null) {
|
|
819
|
+
this.code.line(`private ${propTypeFQN}${isOptional} ${backingFieldName};`);
|
|
820
|
+
this.code.line();
|
|
821
|
+
}
|
|
673
822
|
this.dotnetDocGenerator.emitDocs(prop, {
|
|
674
823
|
api: 'member',
|
|
675
824
|
fqn: definingType.fqn,
|
|
@@ -701,12 +850,13 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
701
850
|
isVirtualKeyWord = 'virtual ';
|
|
702
851
|
}
|
|
703
852
|
}
|
|
704
|
-
const propTypeFQN = this.typeresolver.toDotNetType(prop.type);
|
|
705
|
-
const isOptional = prop.optional ? '?' : '';
|
|
706
853
|
const statement = `${access} ${isAbstractKeyword}${isVirtualKeyWord}${staticKeyWord}${isOverrideKeyWord}${propTypeFQN}${isOptional} ${propName}`;
|
|
707
854
|
this.code.openBlock(statement);
|
|
708
855
|
// Emit getters
|
|
709
|
-
if (
|
|
856
|
+
if (backingFieldName != null) {
|
|
857
|
+
this.code.line(`get => ${backingFieldName};`);
|
|
858
|
+
}
|
|
859
|
+
else if (datatype || prop.const || prop.abstract) {
|
|
710
860
|
this.code.line('get;');
|
|
711
861
|
}
|
|
712
862
|
else {
|
|
@@ -720,16 +870,34 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
720
870
|
}
|
|
721
871
|
}
|
|
722
872
|
// Emit setters
|
|
723
|
-
if (
|
|
873
|
+
if (backingFieldName) {
|
|
874
|
+
this.code.openBlock('set');
|
|
875
|
+
this.emitUnionParameterValdation([
|
|
876
|
+
{
|
|
877
|
+
name: 'value',
|
|
878
|
+
type: prop.type,
|
|
879
|
+
optional: prop.optional,
|
|
880
|
+
},
|
|
881
|
+
], { noMangle: true });
|
|
882
|
+
this.code.line(`${backingFieldName} = value;`);
|
|
883
|
+
this.code.closeBlock();
|
|
884
|
+
}
|
|
885
|
+
else if (datatype || (!prop.immutable && prop.abstract)) {
|
|
724
886
|
this.code.line('set;');
|
|
725
887
|
}
|
|
726
888
|
else {
|
|
727
889
|
if (!prop.immutable) {
|
|
728
|
-
|
|
729
|
-
|
|
890
|
+
const setCode = prop.static
|
|
891
|
+
? `SetStaticProperty(typeof(${className}), value);`
|
|
892
|
+
: 'SetInstanceProperty(value);';
|
|
893
|
+
if (containsUnionType(prop.type)) {
|
|
894
|
+
this.code.openBlock('set');
|
|
895
|
+
this.emitUnionParameterValdation([{ name: 'value', optional: prop.optional, type: prop.type }], { noMangle: true });
|
|
896
|
+
this.code.line(setCode);
|
|
897
|
+
this.code.closeBlock();
|
|
730
898
|
}
|
|
731
899
|
else {
|
|
732
|
-
this.code.line(
|
|
900
|
+
this.code.line(`set => ${setCode}`);
|
|
733
901
|
}
|
|
734
902
|
}
|
|
735
903
|
}
|
|
@@ -872,4 +1040,100 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
872
1040
|
}
|
|
873
1041
|
}
|
|
874
1042
|
exports.DotNetGenerator = DotNetGenerator;
|
|
1043
|
+
async function tryDownloadResource(urlText, into) {
|
|
1044
|
+
const url = new URL(urlText);
|
|
1045
|
+
let request;
|
|
1046
|
+
switch (url.protocol) {
|
|
1047
|
+
case 'http:':
|
|
1048
|
+
request = http.get;
|
|
1049
|
+
break;
|
|
1050
|
+
case 'https:':
|
|
1051
|
+
request = https.get;
|
|
1052
|
+
break;
|
|
1053
|
+
default:
|
|
1054
|
+
// Unhandled protocol... ignoring
|
|
1055
|
+
(0, logging_1.debug)(`Unsupported URL protocol for resource download: ${url.protocol} (full URL: ${urlText})`);
|
|
1056
|
+
return undefined;
|
|
1057
|
+
}
|
|
1058
|
+
return new Promise((ok, ko) => request(url, (res) => {
|
|
1059
|
+
switch (res.statusCode) {
|
|
1060
|
+
case 200:
|
|
1061
|
+
let fileName = path.basename(url.pathname);
|
|
1062
|
+
// Ensure there is a content-appropriate extension on the result...
|
|
1063
|
+
switch (res.headers['content-type']) {
|
|
1064
|
+
case 'image/gif':
|
|
1065
|
+
if (!fileName.endsWith('.gif')) {
|
|
1066
|
+
fileName = `${fileName}.gif`;
|
|
1067
|
+
}
|
|
1068
|
+
break;
|
|
1069
|
+
case 'image/jpeg':
|
|
1070
|
+
if (!fileName.endsWith('.jpg')) {
|
|
1071
|
+
fileName = `${fileName}.jpg`;
|
|
1072
|
+
}
|
|
1073
|
+
break;
|
|
1074
|
+
case 'image/png':
|
|
1075
|
+
if (!fileName.endsWith('.png')) {
|
|
1076
|
+
fileName = `${fileName}.png`;
|
|
1077
|
+
}
|
|
1078
|
+
break;
|
|
1079
|
+
default:
|
|
1080
|
+
// Nothing to do...
|
|
1081
|
+
}
|
|
1082
|
+
const filePath = path.join('resources', fileName);
|
|
1083
|
+
try {
|
|
1084
|
+
fs.mkdirpSync(path.join(into, 'resources'));
|
|
1085
|
+
}
|
|
1086
|
+
catch (err) {
|
|
1087
|
+
return ko(err);
|
|
1088
|
+
}
|
|
1089
|
+
try {
|
|
1090
|
+
const fd = fs.openSync(path.join(into, filePath), fs.constants.O_CREAT |
|
|
1091
|
+
fs.constants.O_TRUNC |
|
|
1092
|
+
fs.constants.O_WRONLY);
|
|
1093
|
+
res
|
|
1094
|
+
.once('error', (cause) => {
|
|
1095
|
+
try {
|
|
1096
|
+
fs.closeSync(fd);
|
|
1097
|
+
}
|
|
1098
|
+
catch {
|
|
1099
|
+
// IGNORE
|
|
1100
|
+
}
|
|
1101
|
+
ko(cause);
|
|
1102
|
+
})
|
|
1103
|
+
.on('data', (chunk) => {
|
|
1104
|
+
const buff = Buffer.from(chunk);
|
|
1105
|
+
let offset = 0;
|
|
1106
|
+
while (offset < buff.length) {
|
|
1107
|
+
try {
|
|
1108
|
+
offset += fs.writeSync(fd, buff, offset);
|
|
1109
|
+
}
|
|
1110
|
+
catch (err) {
|
|
1111
|
+
return ko(err);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
})
|
|
1115
|
+
.once('close', () => {
|
|
1116
|
+
try {
|
|
1117
|
+
fs.closeSync(fd);
|
|
1118
|
+
ok(filePath);
|
|
1119
|
+
}
|
|
1120
|
+
catch (err) {
|
|
1121
|
+
ko(err);
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
catch (err) {
|
|
1126
|
+
return ko(err);
|
|
1127
|
+
}
|
|
1128
|
+
break;
|
|
1129
|
+
default:
|
|
1130
|
+
ko(new Error(`GET ${urlText} -- HTTP ${res.statusCode ?? 0} (${res.statusMessage ?? 'Unknown Error'})`));
|
|
1131
|
+
}
|
|
1132
|
+
}).once('error', ko));
|
|
1133
|
+
}
|
|
1134
|
+
function containsUnionType(typeRef) {
|
|
1135
|
+
return (spec.isUnionTypeReference(typeRef) ||
|
|
1136
|
+
(spec.isCollectionTypeReference(typeRef) &&
|
|
1137
|
+
containsUnionType(typeRef.collection.elementtype)));
|
|
1138
|
+
}
|
|
875
1139
|
//# sourceMappingURL=dotnetgenerator.js.map
|