|
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