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.
Files changed (55) hide show
  1. package/bin/jsii-pacmak.js +10 -0
  2. package/lib/builder.d.ts +12 -7
  3. package/lib/builder.js +4 -3
  4. package/lib/generator.d.ts +7 -1
  5. package/lib/generator.js +4 -1
  6. package/lib/index.d.ts +7 -1
  7. package/lib/index.js +7 -5
  8. package/lib/target.d.ts +3 -0
  9. package/lib/target.js +4 -3
  10. package/lib/targets/dotnet/dotnetgenerator.d.ts +5 -2
  11. package/lib/targets/dotnet/dotnetgenerator.js +27 -100
  12. package/lib/targets/dotnet/dotnettyperesolver.js +5 -4
  13. package/lib/targets/dotnet/runtime-type-checking.d.ts +13 -0
  14. package/lib/targets/dotnet/runtime-type-checking.js +146 -0
  15. package/lib/targets/dotnet.js +5 -4
  16. package/lib/targets/go/dependencies.d.ts +16 -0
  17. package/lib/targets/go/dependencies.js +59 -0
  18. package/lib/targets/go/emit-context.d.ts +2 -0
  19. package/lib/targets/go/package.d.ts +1 -0
  20. package/lib/targets/go/package.js +54 -33
  21. package/lib/targets/go/runtime/class-constructor.d.ts +2 -1
  22. package/lib/targets/go/runtime/class-constructor.js +4 -1
  23. package/lib/targets/go/runtime/method-call.d.ts +2 -2
  24. package/lib/targets/go/runtime/method-call.js +11 -5
  25. package/lib/targets/go/runtime/property-access.d.ts +3 -2
  26. package/lib/targets/go/runtime/property-access.js +7 -2
  27. package/lib/targets/go/runtime/runtime-type-checking.d.ts +29 -0
  28. package/lib/targets/go/runtime/runtime-type-checking.js +408 -0
  29. package/lib/targets/go/types/class.d.ts +10 -5
  30. package/lib/targets/go/types/class.js +60 -30
  31. package/lib/targets/go/types/enum.d.ts +2 -2
  32. package/lib/targets/go/types/enum.js +6 -2
  33. package/lib/targets/go/types/go-type-reference.d.ts +6 -1
  34. package/lib/targets/go/types/go-type-reference.js +37 -21
  35. package/lib/targets/go/types/go-type.d.ts +4 -1
  36. package/lib/targets/go/types/go-type.js +3 -0
  37. package/lib/targets/go/types/interface.d.ts +5 -3
  38. package/lib/targets/go/types/interface.js +40 -17
  39. package/lib/targets/go/types/struct.d.ts +7 -3
  40. package/lib/targets/go/types/struct.js +41 -2
  41. package/lib/targets/go/types/type-member.d.ts +8 -1
  42. package/lib/targets/go/types/type-member.js +63 -18
  43. package/lib/targets/go.d.ts +6 -2
  44. package/lib/targets/go.js +6 -4
  45. package/lib/targets/java.d.ts +5 -2
  46. package/lib/targets/java.js +8 -7
  47. package/lib/targets/js.d.ts +1 -0
  48. package/lib/targets/js.js +4 -0
  49. package/lib/targets/python/type-name.js +4 -4
  50. package/lib/targets/python.d.ts +3 -1
  51. package/lib/targets/python.js +15 -7
  52. package/lib/targets/version-utils.js +1 -1
  53. package/lib/version.d.ts +2 -2
  54. package/lib/version.js +3 -3
  55. package/package.json +16 -14
@@ -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
- targetName: this.targetName,
67
- packageDir: module.moduleDirectory,
66
+ arguments: options.arguments,
68
67
  assembly: module.assembly,
69
68
  fingerprint: options.fingerprint,
70
69
  force: options.force,
71
- arguments: options.arguments,
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) {
@@ -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?: GeneratorOptions);
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, rosettaUnknownSnippets, updateNpmIgnoreFiles, validateAssemblies, }: PacmakOptions): Promise<void>;
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(), rosettaUnknownSnippets = undefined, updateNpmIgnoreFiles = false, validateAssemblies = false, }) {
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
- arguments: argv,
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.packageDir = options.packageDir;
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.arguments = options.arguments;
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[], rosetta: Rosetta);
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, rosetta) {
22
- super();
23
+ constructor(assembliesCurrentlyBeingCompiled, options) {
24
+ super(options);
23
25
  this.assembliesCurrentlyBeingCompiled = assembliesCurrentlyBeingCompiled;
24
- this.rosetta = rosetta;
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(initializer.parameters);
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 = false } = {}) {
446
- const unionParameters = parameters?.filter(({ type }) => containsUnionType(type));
447
- if (unionParameters == null || unionParameters.length === 0) {
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
- this.code.openBlock('if (Amazon.JSII.Runtime.Configuration.RuntimeTypeChecking)');
451
- for (const param of unionParameters) {
452
- const name = noMangle
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([{ name: 'value', optional: prop.optional, type: prop.type }], { noMangle: true });
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
- const elementDotNetTypeName = this.toDotNetTypeName(ref.collection.elementtype);
209
- return `$"{${elementDotNetTypeName}}[]"`;
211
+ return `${interpolates}"${elementTypeName}[]"`;
210
212
  case spec.CollectionKind.Map:
211
- const elementDotNetType = this.toDotNetType(ref.collection.elementtype);
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
@@ -164,13 +164,14 @@ class DotnetBuilder {
164
164
  }
165
165
  makeTarget(module) {
166
166
  return new Dotnet({
167
- targetName: this.targetName,
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
- arguments: this.options.arguments,
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.rosetta);
189
+ this.generator = new dotnetgenerator_1.DotNetGenerator(assembliesCurrentlyBeingCompiled, options);
189
190
  }
190
191
  static toPackageInfos(assm) {
191
192
  const packageId = assm.targets.dotnet.packageId;