|
9 | 9 | compileVisitGlobals,
|
10 | 10 | compileVisitMembers,
|
11 | 11 | compileRTTI,
|
| 12 | + compileClassInstanceOf, |
12 | 13 | } from "./builtins";
|
13 | 14 |
|
14 | 15 | import {
|
@@ -160,6 +161,8 @@ import {
|
160 | 161 | UnaryPostfixExpression,
|
161 | 162 | UnaryPrefixExpression,
|
162 | 163 |
|
| 164 | + NamedTypeNode, |
| 165 | + |
163 | 166 | nodeIsConstantValue,
|
164 | 167 | findDecorator,
|
165 | 168 | isTypeOmitted
|
@@ -335,6 +338,8 @@ export class Compiler extends DiagnosticEmitter {
|
335 | 338 | inlineStack: Function[] = [];
|
336 | 339 | /** Lazily compiled library functions. */
|
337 | 340 | lazyLibraryFunctions: Set<Function> = new Set();
|
| 341 | + /** Pending class-specific instanceof helpers. */ |
| 342 | + pendingClassInstanceOf: Set<ClassPrototype> = new Set(); |
338 | 343 |
|
339 | 344 | /** Compiles a {@link Program} to a {@link Module} using the specified options. */
|
340 | 345 | static compile(program: Program): Module {
|
@@ -455,6 +460,11 @@ export class Compiler extends DiagnosticEmitter {
|
455 | 460 | }
|
456 | 461 | } while (lazyLibraryFunctions.size);
|
457 | 462 |
|
| 463 | + // compile pending class-specific instanceof helpers |
| 464 | + for (let prototype of this.pendingClassInstanceOf.values()) { |
| 465 | + compileClassInstanceOf(this, prototype); |
| 466 | + } |
| 467 | + |
458 | 468 | // finalize runtime features
|
459 | 469 | module.removeGlobal(BuiltinNames.rtti_base);
|
460 | 470 | if (this.runtimeFeatures & RuntimeFeatures.RTTI) compileRTTI(this);
|
@@ -7666,21 +7676,42 @@ export class Compiler extends DiagnosticEmitter {
|
7666 | 7676 | contextualType: Type,
|
7667 | 7677 | constraints: Constraints
|
7668 | 7678 | ): ExpressionRef {
|
7669 |
| - var module = this.module; |
7670 |
| - // NOTE that this differs from TypeScript in that the rhs is a type, not an expression. at the |
7671 |
| - // time of implementation, this seemed more useful because dynamic rhs expressions are not |
7672 |
| - // possible in AS anyway. also note that the code generated below must preserve side-effects of |
7673 |
| - // the LHS expression even when the result is a constant, i.e. return a block dropping `expr`. |
7674 | 7679 | var flow = this.currentFlow;
|
7675 |
| - var expr = this.compileExpression(expression.expression, this.options.usizeType); |
7676 |
| - var actualType = this.currentType; |
| 7680 | + var isType = expression.isType; |
| 7681 | + |
| 7682 | + // Mimic `instanceof CLASS` |
| 7683 | + if (isType.kind == NodeKind.NAMEDTYPE) { |
| 7684 | + let namedType = <NamedTypeNode>isType; |
| 7685 | + if (!(namedType.isNullable || namedType.hasTypeArguments)) { |
| 7686 | + let element = this.resolver.resolveTypeName(namedType.name, flow.actualFunction, ReportMode.SWALLOW); |
| 7687 | + if (element !== null && element.kind == ElementKind.CLASS_PROTOTYPE) { |
| 7688 | + let prototype = <ClassPrototype>element; |
| 7689 | + if (prototype.is(CommonFlags.GENERIC)) { |
| 7690 | + return this.makeInstanceofClass(expression, prototype); |
| 7691 | + } |
| 7692 | + } |
| 7693 | + } |
| 7694 | + } |
| 7695 | + |
| 7696 | + // Fall back to `instanceof TYPE` |
7677 | 7697 | var expectedType = this.resolver.resolveType(
|
7678 | 7698 | expression.isType,
|
7679 | 7699 | flow.actualFunction,
|
7680 | 7700 | makeMap(flow.contextualTypeArguments)
|
7681 | 7701 | );
|
| 7702 | + if (!expectedType) { |
| 7703 | + this.currentType = Type.bool; |
| 7704 | + return this.module.unreachable(); |
| 7705 | + } |
| 7706 | + return this.makeInstanceofType(expression, expectedType); |
| 7707 | + } |
| 7708 | + |
| 7709 | + private makeInstanceofType(expression: InstanceOfExpression, expectedType: Type): ExpressionRef { |
| 7710 | + var module = this.module; |
| 7711 | + var flow = this.currentFlow; |
| 7712 | + var expr = this.compileExpression(expression.expression, expectedType); |
| 7713 | + var actualType = this.currentType; |
7682 | 7714 | this.currentType = Type.bool;
|
7683 |
| - if (!expectedType) return module.unreachable(); |
7684 | 7715 |
|
7685 | 7716 | // instanceof <basic> - must be exact
|
7686 | 7717 | if (!expectedType.is(TypeFlags.REFERENCE)) {
|
@@ -7802,6 +7833,53 @@ export class Compiler extends DiagnosticEmitter {
|
7802 | 7833 | ], NativeType.I32);
|
7803 | 7834 | }
|
7804 | 7835 |
|
| 7836 | + private makeInstanceofClass(expression: InstanceOfExpression, prototype: ClassPrototype): ExpressionRef { |
| 7837 | + var module = this.module; |
| 7838 | + var expr = this.compileExpression(expression.expression, Type.auto); |
| 7839 | + var actualType = this.currentType; |
| 7840 | + var nativeSizeType = actualType.toNativeType(); |
| 7841 | + |
| 7842 | + this.currentType = Type.bool; |
| 7843 | + |
| 7844 | + // exclusively interested in class references here |
| 7845 | + var classReference = actualType.classReference; |
| 7846 | + if (actualType.is(TypeFlags.REFERENCE) && classReference !== null) { |
| 7847 | + |
| 7848 | + // static check |
| 7849 | + if (classReference.extends(prototype)) { |
| 7850 | + |
| 7851 | + // <nullable> instanceof <PROTOTYPE> - LHS must be != 0 |
| 7852 | + if (actualType.is(TypeFlags.NULLABLE)) { |
| 7853 | + return module.binary( |
| 7854 | + nativeSizeType == NativeType.I64 |
| 7855 | + ? BinaryOp.NeI64 |
| 7856 | + : BinaryOp.NeI32, |
| 7857 | + expr, |
| 7858 | + this.makeZero(actualType) |
| 7859 | + ); |
| 7860 | + |
| 7861 | + // <nonNullable> is just `true` |
| 7862 | + } else { |
| 7863 | + return module.block(null, [ |
| 7864 | + module.drop(expr), |
| 7865 | + module.i32(1) |
| 7866 | + ], NativeType.I32); |
| 7867 | + } |
| 7868 | + |
| 7869 | + // dynamic check against all possible concrete ids |
| 7870 | + } else if (prototype.extends(classReference.prototype)) { |
| 7871 | + this.pendingClassInstanceOf.add(prototype); |
| 7872 | + return module.call(prototype.internalName + "~instanceof", [ expr ], NativeType.I32); |
| 7873 | + } |
| 7874 | + } |
| 7875 | + |
| 7876 | + // false |
| 7877 | + return module.block(null, [ |
| 7878 | + module.drop(expr), |
| 7879 | + module.i32(0) |
| 7880 | + ], NativeType.I32); |
| 7881 | + } |
| 7882 | + |
7805 | 7883 | private compileLiteralExpression(
|
7806 | 7884 | expression: LiteralExpression,
|
7807 | 7885 | contextualType: Type,
|
|
0 commit comments