public emit()

in packages/jsii-pacmak/lib/targets/dotnet/runtime-type-checking.ts [164:269]


      public emit(code: CodeMaker, resolver: DotNetTypeResolver): void {
        const validTypes = new Array<string>();

        const castVarName = `cast_${createHash('sha256')
          .update(expression)
          .digest('hex')
          .slice(0, 6)}`;

        code.openBlock(`switch (${expression})`);
        for (const type of types) {
          validTypes.push(resolver.toDotNetTypeName(type.spec!));

          /**
           * Filter to remove classes and interfaces from a set of type references that
           * are implied by another entry in the set. Practically this is meant to remove
           * types from a set if a parent type of it is also present in the set, keeping
           * only the most generic declaration.
           *
           * This is useful because the TypeScript compiler and jsii do not guarantee that
           * all entries in a type union are unrelated, but the C# compiler treats dead
           * code as an error, and will refuse to compile (error CS8120) a pattern-matching
           * switch case if it cannot be matched (for example, if it matches on a child of
           * a type that was previously matched on already).
           */
          if (
            (type.type?.isClassType() || type.type?.isInterfaceType()) &&
            types.some(
              (other) =>
                other !== type &&
                other.type != null &&
                type.type!.extends(other.type),
            )
          ) {
            continue;
          }

          const typeNames = [resolver.toDotNetType(type.spec!)];
          if (typeNames[0] === 'double') {
            // For doubles, we accept any numeric value, really...
            typeNames.push(
              'byte',
              'decimal',
              'float',
              'int',
              'long',
              'sbyte',
              'short',
              'uint',
              'ulong',
              'ushort',
            );
          }

          for (const typeName of typeNames) {
            code.indent(`case ${typeName} ${castVarName}:`);
            Validation.forTypeReference(
              argument,
              castVarName,
              description,
              type,
              allowNull,
            )?.emit(code, resolver);
            code.line('break;');
            code.unindent(false);
          }
        }
        if (hasInterface) {
          code.indent(
            `case Amazon.JSII.Runtime.Deputy.AnonymousObject ${castVarName}:`,
          );
          code.line('// Not enough information to type-check...');
          code.line('break;');
          code.unindent(false);
        }
        code.indent('case null:');

        const acceptedTypes = validTypes
          .map((t) =>
            t.startsWith('"')
              ? t.slice(1, t.length - 1)
              : t.startsWith('$"')
                ? t.slice(2, t.length - 1)
                : `{${t}}`,
          )
          .join(', ');
        if (allowNull) {
          code.line('break;');
        } else {
          const message = JSON.stringify(
            `Expected ${description} to be one of: ${acceptedTypes}; received null`,
          );
          code.line(
            `throw new System.ArgumentException($${message}, ${argument});`,
          );
        }
        code.unindent(false);
        code.indent('default:');
        const message = JSON.stringify(
          `Expected ${description} to be one of: ${acceptedTypes}; received {${expression}.GetType().FullName}`,
        );
        code.line(
          `throw new System.ArgumentException($${message}, ${argument});`,
        );
        code.unindent(false);
        code.closeBlock();
      }