jsii-pacmak 1.64.0 → 1.66.0
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 +5 -2
- package/lib/targets/dotnet/dotnetgenerator.js +27 -100
- package/lib/targets/dotnet/dotnettyperesolver.js +5 -4
- package/lib/targets/dotnet/runtime-type-checking.d.ts +13 -0
- package/lib/targets/dotnet/runtime-type-checking.js +146 -0
- package/lib/targets/dotnet.js +5 -4
- package/lib/targets/go/dependencies.d.ts +16 -0
- package/lib/targets/go/dependencies.js +59 -0
- package/lib/targets/go/emit-context.d.ts +2 -0
- package/lib/targets/go/package.d.ts +1 -0
- package/lib/targets/go/package.js +54 -33
- package/lib/targets/go/runtime/class-constructor.d.ts +2 -1
- package/lib/targets/go/runtime/class-constructor.js +4 -1
- package/lib/targets/go/runtime/method-call.d.ts +2 -2
- package/lib/targets/go/runtime/method-call.js +11 -5
- package/lib/targets/go/runtime/property-access.d.ts +3 -2
- package/lib/targets/go/runtime/property-access.js +7 -2
- package/lib/targets/go/runtime/runtime-type-checking.d.ts +29 -0
- package/lib/targets/go/runtime/runtime-type-checking.js +408 -0
- package/lib/targets/go/types/class.d.ts +10 -5
- package/lib/targets/go/types/class.js +60 -30
- package/lib/targets/go/types/enum.d.ts +2 -2
- package/lib/targets/go/types/enum.js +6 -2
- package/lib/targets/go/types/go-type-reference.d.ts +6 -1
- package/lib/targets/go/types/go-type-reference.js +37 -21
- package/lib/targets/go/types/go-type.d.ts +4 -1
- package/lib/targets/go/types/go-type.js +3 -0
- package/lib/targets/go/types/interface.d.ts +5 -3
- package/lib/targets/go/types/interface.js +40 -17
- package/lib/targets/go/types/struct.d.ts +7 -3
- package/lib/targets/go/types/struct.js +41 -2
- package/lib/targets/go/types/type-member.d.ts +8 -1
- package/lib/targets/go/types/type-member.js +63 -18
- package/lib/targets/go.d.ts +6 -2
- package/lib/targets/go.js +6 -4
- 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 +15 -7
- 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 -14
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).
|
|
@@ -6,6 +6,7 @@ const clone = require("clone");
|
|
|
6
6
|
const fs = require("fs-extra");
|
|
7
7
|
const http = require("http");
|
|
8
8
|
const https = require("https");
|
|
9
|
+
const reflect = require("jsii-reflect");
|
|
9
10
|
const path = require("path");
|
|
10
11
|
const generator_1 = require("../../generator");
|
|
11
12
|
const logging_1 = require("../../logging");
|
|
@@ -14,22 +15,23 @@ const dotnetruntimegenerator_1 = require("./dotnetruntimegenerator");
|
|
|
14
15
|
const dotnettyperesolver_1 = require("./dotnettyperesolver");
|
|
15
16
|
const filegenerator_1 = require("./filegenerator");
|
|
16
17
|
const nameutils_1 = require("./nameutils");
|
|
18
|
+
const runtime_type_checking_1 = require("./runtime-type-checking");
|
|
17
19
|
/**
|
|
18
20
|
* CODE GENERATOR V2
|
|
19
21
|
*/
|
|
20
22
|
class DotNetGenerator extends generator_1.Generator {
|
|
21
|
-
constructor(assembliesCurrentlyBeingCompiled,
|
|
22
|
-
super();
|
|
23
|
+
constructor(assembliesCurrentlyBeingCompiled, options) {
|
|
24
|
+
super(options);
|
|
23
25
|
this.assembliesCurrentlyBeingCompiled = assembliesCurrentlyBeingCompiled;
|
|
24
|
-
this.
|
|
26
|
+
this.nameutils = new nameutils_1.DotNetNameUtils();
|
|
25
27
|
// Flags that tracks if we have already wrote the first member of the class
|
|
26
28
|
this.firstMemberWritten = false;
|
|
27
|
-
this.nameutils = new nameutils_1.DotNetNameUtils();
|
|
28
29
|
// Override the openBlock to get a correct C# looking code block with the curly brace after the line
|
|
29
30
|
this.code.openBlock = function (text) {
|
|
30
31
|
this.line(text);
|
|
31
32
|
this.open('{');
|
|
32
33
|
};
|
|
34
|
+
this.rosetta = options.rosetta;
|
|
33
35
|
}
|
|
34
36
|
async load(packageRoot, assembly) {
|
|
35
37
|
await super.load(packageRoot, assembly);
|
|
@@ -271,7 +273,8 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
271
273
|
// the instance will be created in the kernel (where it'd fail on a sub-optimal error instead)...
|
|
272
274
|
this.code.line('[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]');
|
|
273
275
|
this.code.openBlock(`private static DeputyProps _MakeDeputyProps(${parametersDefinition})`);
|
|
274
|
-
this.emitUnionParameterValdation(
|
|
276
|
+
this.emitUnionParameterValdation(this.reflectAssembly.findType(cls.fqn)
|
|
277
|
+
.initializer?.parameters);
|
|
275
278
|
const args = parametersBase.length > 0
|
|
276
279
|
? `new object?[]{${parametersBase}}`
|
|
277
280
|
: `System.Array.Empty<object?>()`;
|
|
@@ -431,7 +434,7 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
431
434
|
}
|
|
432
435
|
else {
|
|
433
436
|
this.code.openBlock(`${access} ${staticKeyWord}${overrideKeyWord}${virtualKeyWord}${signature}`);
|
|
434
|
-
this.emitUnionParameterValdation(method.parameters);
|
|
437
|
+
this.emitUnionParameterValdation(this.reflectAssembly.findType(cls.fqn).allMethods.find((m) => m.name === method.name).parameters);
|
|
435
438
|
this.code.line(this.dotnetRuntimeGenerator.createInvokeMethodIdentifier(method, cls));
|
|
436
439
|
this.code.closeBlock();
|
|
437
440
|
}
|
|
@@ -442,96 +445,18 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
442
445
|
* @param parameters the list of parameters received by the function.
|
|
443
446
|
* @param noMangle use parameter names as-is (useful for setters, for example) instead of mangling them.
|
|
444
447
|
*/
|
|
445
|
-
emitUnionParameterValdation(parameters, { noMangle
|
|
446
|
-
|
|
447
|
-
|
|
448
|
+
emitUnionParameterValdation(parameters = [], opts = { noMangle: false }) {
|
|
449
|
+
if (!this.runtimeTypeChecking) {
|
|
450
|
+
// We were configured not to emit those, so bail out now.
|
|
448
451
|
return;
|
|
449
452
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
? param.name
|
|
454
|
-
: this.nameutils.convertParameterName(param.name);
|
|
455
|
-
if (param.optional) {
|
|
456
|
-
this.code.openBlock(`if (${name} != null)`);
|
|
457
|
-
}
|
|
458
|
-
validate.call(this, name, noMangle ? name : `argument {nameof(${name})}`, param.type, noMangle ? name : `{nameof(${name})}`);
|
|
459
|
-
if (param.optional) {
|
|
460
|
-
this.code.closeBlock();
|
|
461
|
-
}
|
|
453
|
+
const validator = runtime_type_checking_1.ParameterValidator.forParameters(parameters, this.nameutils, opts);
|
|
454
|
+
if (validator == null) {
|
|
455
|
+
return;
|
|
462
456
|
}
|
|
457
|
+
this.code.openBlock('if (Amazon.JSII.Runtime.Configuration.RuntimeTypeChecking)');
|
|
458
|
+
validator.emit(this.code, this.typeresolver);
|
|
463
459
|
this.code.closeBlock();
|
|
464
|
-
function validate(value, descr, type, parameterName) {
|
|
465
|
-
if (spec.isUnionTypeReference(type)) {
|
|
466
|
-
validateTypeUnion.call(this, value, descr, type, parameterName);
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
const collectionType = type;
|
|
470
|
-
if (collectionType.collection.kind === spec.CollectionKind.Array) {
|
|
471
|
-
validateArray.call(this, value, descr, collectionType.collection.elementtype, parameterName);
|
|
472
|
-
}
|
|
473
|
-
else if (collectionType.collection.kind === spec.CollectionKind.Map) {
|
|
474
|
-
validateMap.call(this, value, descr, collectionType.collection.elementtype, parameterName);
|
|
475
|
-
}
|
|
476
|
-
else {
|
|
477
|
-
throw new Error(`Unhandled collection kind: ${spec.describeTypeReference(type)}`);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
function validateArray(value, descr, elementType, parameterName) {
|
|
482
|
-
const varName = `__idx_${descr.replace(/[^a-z0-9_]/gi, '_')}`;
|
|
483
|
-
this.code.openBlock(`for (int ${varName} = 0 ; ${varName} < ${value}.Length ; ${varName}++)`);
|
|
484
|
-
validate.call(this, `${value}[${varName}]`, `${descr}[{${varName}}]`, elementType, parameterName);
|
|
485
|
-
this.code.closeBlock();
|
|
486
|
-
}
|
|
487
|
-
function validateMap(value, descr, elementType, parameterName) {
|
|
488
|
-
const varName = `__item_${descr.replace(/[^a-z0-9_]/gi, '_')}`;
|
|
489
|
-
this.code.openBlock(`foreach (var ${varName} in ${value})`);
|
|
490
|
-
validate.call(this, `${varName}.Value`, `${descr}[\\"{${varName}.Key}\\"]`, elementType, parameterName);
|
|
491
|
-
this.code.closeBlock();
|
|
492
|
-
}
|
|
493
|
-
function validateTypeUnion(value, descr, type, parameterName) {
|
|
494
|
-
this.code.indent('if (');
|
|
495
|
-
let emitAnd = false;
|
|
496
|
-
const typeRefs = type.union.types;
|
|
497
|
-
for (const typeRef of typeRefs) {
|
|
498
|
-
const prefix = emitAnd ? '&& ' : '';
|
|
499
|
-
const dotNetType = this.typeresolver.toDotNetType(typeRef);
|
|
500
|
-
// In the case of double, we test for all standard numeric types of .NET (these implicitly convert).
|
|
501
|
-
const test = dotNetType === 'double'
|
|
502
|
-
? [
|
|
503
|
-
'byte',
|
|
504
|
-
'decimal',
|
|
505
|
-
'double',
|
|
506
|
-
'float',
|
|
507
|
-
'int',
|
|
508
|
-
'long',
|
|
509
|
-
'sbyte',
|
|
510
|
-
'short',
|
|
511
|
-
'uint',
|
|
512
|
-
'ulong',
|
|
513
|
-
'ushort',
|
|
514
|
-
]
|
|
515
|
-
.map((numeric) => `${value} is ${numeric}`)
|
|
516
|
-
.join(' || ')
|
|
517
|
-
: `${value} is ${dotNetType}`;
|
|
518
|
-
this.code.line(`${prefix}!(${test})`);
|
|
519
|
-
emitAnd = true;
|
|
520
|
-
}
|
|
521
|
-
this.code.unindent(')');
|
|
522
|
-
this.code.openBlock('');
|
|
523
|
-
const placeholders = typeRefs
|
|
524
|
-
.map((typeRef) => {
|
|
525
|
-
const typeName = this.typeresolver.toDotNetTypeName(typeRef);
|
|
526
|
-
if (typeName.startsWith('"') && typeName.endsWith('"')) {
|
|
527
|
-
return typeName.slice(1, -1);
|
|
528
|
-
}
|
|
529
|
-
return `{${typeName}}`;
|
|
530
|
-
})
|
|
531
|
-
.join(', ');
|
|
532
|
-
this.code.line(`throw new System.ArgumentException($"Expected ${descr} to be one of: ${placeholders}; received {${value}.GetType().FullName}", $"${parameterName}");`);
|
|
533
|
-
this.code.closeBlock();
|
|
534
|
-
}
|
|
535
460
|
}
|
|
536
461
|
/**
|
|
537
462
|
* Founds out if a member (property or method) is already defined in one of the base classes
|
|
@@ -852,15 +777,15 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
852
777
|
}
|
|
853
778
|
}
|
|
854
779
|
// Emit setters
|
|
780
|
+
const reflectCls = this.reflectAssembly.findType(cls.fqn);
|
|
781
|
+
const syntheticParam = new reflect.Parameter(reflectCls.system, reflectCls, new reflect.Method(reflectCls.system, reflectCls.assembly, reflectCls, reflectCls, { name: '<synthetic>' }), {
|
|
782
|
+
name: 'value',
|
|
783
|
+
type: prop.type,
|
|
784
|
+
optional: prop.optional,
|
|
785
|
+
});
|
|
855
786
|
if (backingFieldName) {
|
|
856
787
|
this.code.openBlock('set');
|
|
857
|
-
this.emitUnionParameterValdation([
|
|
858
|
-
{
|
|
859
|
-
name: 'value',
|
|
860
|
-
type: prop.type,
|
|
861
|
-
optional: prop.optional,
|
|
862
|
-
},
|
|
863
|
-
], { noMangle: true });
|
|
788
|
+
this.emitUnionParameterValdation([syntheticParam], { noMangle: true });
|
|
864
789
|
this.code.line(`${backingFieldName} = value;`);
|
|
865
790
|
this.code.closeBlock();
|
|
866
791
|
}
|
|
@@ -874,7 +799,9 @@ class DotNetGenerator extends generator_1.Generator {
|
|
|
874
799
|
: 'SetInstanceProperty(value);';
|
|
875
800
|
if (containsUnionType(prop.type)) {
|
|
876
801
|
this.code.openBlock('set');
|
|
877
|
-
this.emitUnionParameterValdation([
|
|
802
|
+
this.emitUnionParameterValdation([syntheticParam], {
|
|
803
|
+
noMangle: true,
|
|
804
|
+
});
|
|
878
805
|
this.code.line(setCode);
|
|
879
806
|
this.code.closeBlock();
|
|
880
807
|
}
|
|
@@ -203,13 +203,14 @@ class DotNetTypeResolver {
|
|
|
203
203
|
* Translates a collection in jsii to the name of a native .NET collection
|
|
204
204
|
*/
|
|
205
205
|
toDotNetCollectionName(ref) {
|
|
206
|
+
const [_, dollar, quote, content] = /^(?:(\$)?("))?([^"]+)"?$/.exec(this.toDotNetTypeName(ref.collection.elementtype));
|
|
207
|
+
const interpolates = dollar || !quote ? '$' : '';
|
|
208
|
+
const elementTypeName = quote ? content : `{${content}}`;
|
|
206
209
|
switch (ref.collection.kind) {
|
|
207
210
|
case spec.CollectionKind.Array:
|
|
208
|
-
|
|
209
|
-
return `$"{${elementDotNetTypeName}}[]"`;
|
|
211
|
+
return `${interpolates}"${elementTypeName}[]"`;
|
|
210
212
|
case spec.CollectionKind.Map:
|
|
211
|
-
|
|
212
|
-
return `typeof(System.Collections.Generic.IDictionary<string, ${elementDotNetType}>).FullName`;
|
|
213
|
+
return `${interpolates}"System.Collections.Generic.IDictionary<string, ${elementTypeName}>"`;
|
|
213
214
|
default:
|
|
214
215
|
throw new Error(`Unsupported collection kind: ${ref.collection.kind}`);
|
|
215
216
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CodeMaker } from 'codemaker';
|
|
2
|
+
import { Parameter } from 'jsii-reflect';
|
|
3
|
+
import { DotNetTypeResolver } from './dotnettyperesolver';
|
|
4
|
+
import { DotNetNameUtils } from './nameutils';
|
|
5
|
+
export declare class ParameterValidator {
|
|
6
|
+
private readonly validations;
|
|
7
|
+
static forParameters(parameters: readonly Parameter[], nameUtils: DotNetNameUtils, { noMangle }: {
|
|
8
|
+
readonly noMangle: boolean;
|
|
9
|
+
}): ParameterValidator | undefined;
|
|
10
|
+
private constructor();
|
|
11
|
+
emit(code: CodeMaker, resolver: DotNetTypeResolver): void;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=runtime-type-checking.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ParameterValidator = void 0;
|
|
4
|
+
const spec_1 = require("@jsii/spec");
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
const jsii_reflect_1 = require("jsii-reflect");
|
|
7
|
+
class ParameterValidator {
|
|
8
|
+
constructor(validations) {
|
|
9
|
+
this.validations = validations;
|
|
10
|
+
}
|
|
11
|
+
static forParameters(parameters, nameUtils, { noMangle }) {
|
|
12
|
+
if (parameters.length === 0) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const parameterValidations = new Map();
|
|
16
|
+
for (const param of parameters) {
|
|
17
|
+
const expr = noMangle
|
|
18
|
+
? param.name
|
|
19
|
+
: nameUtils.convertParameterName(param.name);
|
|
20
|
+
const argName = `nameof(${expr})`;
|
|
21
|
+
const validations = new Array();
|
|
22
|
+
const validation = Validation.forTypeReference(argName, expr, `${noMangle ? '' : 'argument '}{${argName}}`, param.variadic
|
|
23
|
+
? new jsii_reflect_1.TypeReference(param.system, {
|
|
24
|
+
collection: {
|
|
25
|
+
kind: spec_1.CollectionKind.Array,
|
|
26
|
+
elementtype: param.type.spec,
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
: param.type, param.optional);
|
|
30
|
+
if (validation) {
|
|
31
|
+
validations.push(validation);
|
|
32
|
+
}
|
|
33
|
+
if (validations.length !== 0) {
|
|
34
|
+
parameterValidations.set(param, validations);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (parameterValidations.size === 0) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
return new ParameterValidator(parameterValidations);
|
|
41
|
+
}
|
|
42
|
+
emit(code, resolver) {
|
|
43
|
+
for (const [_parameter, validations] of this.validations) {
|
|
44
|
+
for (const validation of validations) {
|
|
45
|
+
validation.emit(code, resolver);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.ParameterValidator = ParameterValidator;
|
|
51
|
+
class Validation {
|
|
52
|
+
static forTypeReference(argument, expression, description, ref, allowNull) {
|
|
53
|
+
if (ref.unionOfTypes) {
|
|
54
|
+
return Validation.unionCheck(argument, expression, description, ref.unionOfTypes, allowNull);
|
|
55
|
+
}
|
|
56
|
+
else if (ref.arrayOfType) {
|
|
57
|
+
return Validation.collectionCheck(argument, expression, description, 'array', ref.arrayOfType);
|
|
58
|
+
}
|
|
59
|
+
else if (ref.mapOfType) {
|
|
60
|
+
return Validation.collectionCheck(argument, expression, description, 'map', ref.mapOfType);
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
static collectionCheck(argument, expression, description, type, elementType) {
|
|
65
|
+
const elementValidator = Validation.forTypeReference(argument, `${expression}[idx]`, `${description}[@{idx}]`, elementType, false);
|
|
66
|
+
if (elementValidator == null) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
class CollectionCheck extends Validation {
|
|
70
|
+
emit(code, resolver) {
|
|
71
|
+
// We need to come up with a unique-enough ID here... so we use a hash.
|
|
72
|
+
const prefix = type === 'array' ? '__idx' : '__item';
|
|
73
|
+
const varName = `${prefix}_${(0, crypto_1.createHash)('sha256')
|
|
74
|
+
.update(expression)
|
|
75
|
+
.digest('hex')
|
|
76
|
+
.slice(0, 6)}`;
|
|
77
|
+
if (type === 'array') {
|
|
78
|
+
code.openBlock(`for (var ${varName} = 0 ; ${varName} < ${expression}.Length ; ${varName}++)`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
code.openBlock(`foreach (var ${varName} in ${expression})`);
|
|
82
|
+
}
|
|
83
|
+
Validation.forTypeReference(argument, type === 'array' ? `${expression}[${varName}]` : `${varName}.Value`, `${description}[${type === 'array' ? `{${varName}}` : `"{${varName}.Key}"`}]`, elementType, false).emit(code, resolver);
|
|
84
|
+
code.closeBlock();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return new CollectionCheck();
|
|
88
|
+
}
|
|
89
|
+
static unionCheck(argument, expression, description, types, allowNull) {
|
|
90
|
+
const hasInterface = types.some((t) => t.type?.isInterfaceType());
|
|
91
|
+
class UnionCheck extends Validation {
|
|
92
|
+
emit(code, resolver) {
|
|
93
|
+
const validTypes = new Array();
|
|
94
|
+
const castVarName = `cast_${(0, crypto_1.createHash)('sha256')
|
|
95
|
+
.update(expression)
|
|
96
|
+
.digest('hex')
|
|
97
|
+
.slice(0, 6)}`;
|
|
98
|
+
code.openBlock(`switch (${expression})`);
|
|
99
|
+
for (const type of types) {
|
|
100
|
+
validTypes.push(resolver.toDotNetTypeName(type.spec));
|
|
101
|
+
const typeNames = [resolver.toDotNetType(type.spec)];
|
|
102
|
+
if (typeNames[0] === 'double') {
|
|
103
|
+
// For doubles, we accept any numeric value, really...
|
|
104
|
+
typeNames.push('byte', 'decimal', 'float', 'int', 'long', 'sbyte', 'short', 'uint', 'ulong', 'ushort');
|
|
105
|
+
}
|
|
106
|
+
for (const typeName of typeNames) {
|
|
107
|
+
code.indent(`case ${typeName} ${castVarName}:`);
|
|
108
|
+
Validation.forTypeReference(argument, castVarName, description, type, allowNull)?.emit(code, resolver);
|
|
109
|
+
code.line('break;');
|
|
110
|
+
code.unindent(false);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (hasInterface) {
|
|
114
|
+
code.indent(`case Amazon.JSII.Runtime.Deputy.AnonymousObject ${castVarName}:`);
|
|
115
|
+
code.line('// Not enough information to type-check...');
|
|
116
|
+
code.line('break;');
|
|
117
|
+
code.unindent(false);
|
|
118
|
+
}
|
|
119
|
+
code.indent('case null:');
|
|
120
|
+
const acceptedTypes = validTypes
|
|
121
|
+
.map((t) => t.startsWith('"')
|
|
122
|
+
? t.slice(1, t.length - 1)
|
|
123
|
+
: t.startsWith('$"')
|
|
124
|
+
? t.slice(2, t.length - 1)
|
|
125
|
+
: `{${t}}`)
|
|
126
|
+
.join(', ');
|
|
127
|
+
if (allowNull) {
|
|
128
|
+
code.line('break;');
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const message = JSON.stringify(`Expected ${description} to be one of: ${acceptedTypes}; received null`);
|
|
132
|
+
code.line(`throw new System.ArgumentException($${message}, ${argument});`);
|
|
133
|
+
}
|
|
134
|
+
code.unindent(false);
|
|
135
|
+
code.indent('default:');
|
|
136
|
+
const message = JSON.stringify(`Expected ${description} to be one of: ${acceptedTypes}; received {${expression}.GetType().FullName}`);
|
|
137
|
+
code.line(`throw new System.ArgumentException($${message}, ${argument});`);
|
|
138
|
+
code.unindent(false);
|
|
139
|
+
code.closeBlock();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return new UnionCheck();
|
|
143
|
+
}
|
|
144
|
+
constructor() { }
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=runtime-type-checking.js.map
|
package/lib/targets/dotnet.js
CHANGED
|
@@ -164,13 +164,14 @@ class DotnetBuilder {
|
|
|
164
164
|
}
|
|
165
165
|
makeTarget(module) {
|
|
166
166
|
return new Dotnet({
|
|
167
|
-
|
|
168
|
-
packageDir: module.moduleDirectory,
|
|
167
|
+
arguments: this.options.arguments,
|
|
169
168
|
assembly: module.assembly,
|
|
170
169
|
fingerprint: this.options.fingerprint,
|
|
171
170
|
force: this.options.force,
|
|
172
|
-
|
|
171
|
+
packageDir: module.moduleDirectory,
|
|
173
172
|
rosetta: this.options.rosetta,
|
|
173
|
+
runtimeTypeChecking: this.options.runtimeTypeChecking,
|
|
174
|
+
targetName: this.targetName,
|
|
174
175
|
}, this.modules.map((m) => m.name));
|
|
175
176
|
}
|
|
176
177
|
}
|
|
@@ -185,7 +186,7 @@ function projectLocation(module) {
|
|
|
185
186
|
class Dotnet extends target_1.Target {
|
|
186
187
|
constructor(options, assembliesCurrentlyBeingCompiled) {
|
|
187
188
|
super(options);
|
|
188
|
-
this.generator = new dotnetgenerator_1.DotNetGenerator(assembliesCurrentlyBeingCompiled, options
|
|
189
|
+
this.generator = new dotnetgenerator_1.DotNetGenerator(assembliesCurrentlyBeingCompiled, options);
|
|
189
190
|
}
|
|
190
191
|
static toPackageInfos(assm) {
|
|
191
192
|
const packageId = assm.targets.dotnet.packageId;
|