mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-05-02 18:32:15 +00:00
Initial implementation of 'instanceof'
Works like an assignability check for now / does not yet honor nullables.
This commit is contained in:
parent
cea69a6de1
commit
7478c8a0d3
2
dist/assemblyscript.js
vendored
2
dist/assemblyscript.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js.map
vendored
2
dist/assemblyscript.js.map
vendored
File diff suppressed because one or more lines are too long
23
src/ast.ts
23
src/ast.ts
@ -45,6 +45,7 @@ export enum NodeKind {
|
||||
ELEMENTACCESS,
|
||||
FALSE,
|
||||
FUNCTION,
|
||||
INSTANCEOF,
|
||||
LITERAL,
|
||||
NEW,
|
||||
NULL,
|
||||
@ -346,6 +347,18 @@ export abstract class Node {
|
||||
return expr;
|
||||
}
|
||||
|
||||
static createInstanceOfExpression(
|
||||
expression: Expression,
|
||||
isType: CommonTypeNode,
|
||||
range: Range
|
||||
): InstanceOfExpression {
|
||||
var expr = new InstanceOfExpression();
|
||||
expr.range = range;
|
||||
expr.expression = expression; expression.parent = expr;
|
||||
expr.isType = isType; isType.parent = expr;
|
||||
return expr;
|
||||
}
|
||||
|
||||
static createIntegerLiteralExpression(
|
||||
value: I64,
|
||||
range: Range
|
||||
@ -1267,6 +1280,16 @@ export class FunctionExpression extends Expression {
|
||||
declaration: FunctionDeclaration;
|
||||
}
|
||||
|
||||
/** Represents an `instanceof` expression. */
|
||||
export class InstanceOfExpression extends Expression {
|
||||
kind = NodeKind.INSTANCEOF;
|
||||
|
||||
/** Expression being asserted. */
|
||||
expression: Expression;
|
||||
/** Type to test for. */
|
||||
isType: CommonTypeNode;
|
||||
}
|
||||
|
||||
/** Represents an integer literal expression. */
|
||||
export class IntegerLiteralExpression extends LiteralExpression {
|
||||
literalKind = LiteralKind.INTEGER;
|
||||
|
@ -94,6 +94,7 @@ import {
|
||||
ForStatement,
|
||||
IfStatement,
|
||||
ImportStatement,
|
||||
InstanceOfExpression,
|
||||
InterfaceDeclaration,
|
||||
NamespaceDeclaration,
|
||||
ReturnStatement,
|
||||
@ -2263,6 +2264,10 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
break;
|
||||
}
|
||||
case NodeKind.INSTANCEOF: {
|
||||
expr = this.compileInstanceOfExpression(<InstanceOfExpression>expression, contextualType);
|
||||
break;
|
||||
}
|
||||
case NodeKind.LITERAL: {
|
||||
expr = this.compileLiteralExpression(<LiteralExpression>expression, contextualType);
|
||||
break;
|
||||
@ -5873,6 +5878,18 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
compileInstanceOfExpression(
|
||||
expression: InstanceOfExpression,
|
||||
contextualType: Type
|
||||
): ExpressionRef {
|
||||
this.compileExpressionRetainType(expression.expression, this.options.usizeType, WrapMode.NONE);
|
||||
var type = this.currentType;
|
||||
var isType = this.program.resolveType(expression.isType);
|
||||
this.currentType = Type.bool;
|
||||
if (!isType) return this.module.createUnreachable();
|
||||
return this.module.createI32(type.isAssignableTo(isType, false) ? 1 : 0);
|
||||
}
|
||||
|
||||
compileLiteralExpression(
|
||||
expression: LiteralExpression,
|
||||
contextualType: Type,
|
||||
|
@ -52,6 +52,7 @@ import {
|
||||
ForStatement,
|
||||
IfStatement,
|
||||
ImportStatement,
|
||||
InstanceOfExpression,
|
||||
ReturnStatement,
|
||||
SwitchStatement,
|
||||
ThrowStatement,
|
||||
@ -160,6 +161,10 @@ export class ASTBuilder {
|
||||
this.visitFunctionExpression(<FunctionExpression>node);
|
||||
break;
|
||||
}
|
||||
case NodeKind.INSTANCEOF: {
|
||||
this.visitInstanceOfExpression(<InstanceOfExpression>node);
|
||||
break;
|
||||
}
|
||||
case NodeKind.LITERAL: {
|
||||
this.visitLiteralExpression(<LiteralExpression>node);
|
||||
break;
|
||||
@ -544,6 +549,12 @@ export class ASTBuilder {
|
||||
this.sb.push(node.value.toString(10));
|
||||
}
|
||||
|
||||
visitInstanceOfExpression(node: InstanceOfExpression): void {
|
||||
this.visitNode(node.expression);
|
||||
this.sb.push(" instanceof ");
|
||||
this.visitTypeNode(node.isType);
|
||||
}
|
||||
|
||||
visitIntegerLiteralExpression(node: IntegerLiteralExpression): void {
|
||||
this.sb.push(i64_to_string(node.value));
|
||||
}
|
||||
|
@ -3108,7 +3108,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
switch (token) {
|
||||
// AssertionExpression
|
||||
case Token.AS: {
|
||||
let toType = this.parseType(tn);
|
||||
let toType = this.parseType(tn); // reports
|
||||
if (!toType) return null;
|
||||
expr = Node.createAssertionExpression(
|
||||
AssertionKind.AS,
|
||||
@ -3118,9 +3118,20 @@ export class Parser extends DiagnosticEmitter {
|
||||
);
|
||||
break;
|
||||
}
|
||||
// InstanceOfExpression
|
||||
case Token.INSTANCEOF: {
|
||||
let isType = this.parseType(tn); // reports
|
||||
if (!isType) return null;
|
||||
expr = Node.createInstanceOfExpression(
|
||||
expr,
|
||||
isType,
|
||||
tn.range(startPos, tn.pos)
|
||||
);
|
||||
break;
|
||||
}
|
||||
// ElementAccessExpression
|
||||
case Token.OPENBRACKET: {
|
||||
next = this.parseExpression(tn);
|
||||
next = this.parseExpression(tn); // reports
|
||||
if (!next) return null;
|
||||
if (!tn.skip(Token.CLOSEBRACKET)) {
|
||||
this.error(
|
||||
|
49
tests/compiler/instanceof.optimized.wat
Normal file
49
tests/compiler/instanceof.optimized.wat
Normal file
@ -0,0 +1,49 @@
|
||||
(module
|
||||
(type $iiiiv (func (param i32 i32 i32 i32)))
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $Fi (func (param f64) (result i32)))
|
||||
(type $v (func))
|
||||
(import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32)))
|
||||
(memory $0 1)
|
||||
(data (i32.const 8) "\0d\00\00\00i\00n\00s\00t\00a\00n\00c\00e\00o\00f\00.\00t\00s")
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $instanceof/isI32<i32> (; 1 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(i32.const 1)
|
||||
)
|
||||
(func $instanceof/isI32<f64> (; 2 ;) (type $Fi) (param $0 f64) (result i32)
|
||||
(i32.const 0)
|
||||
)
|
||||
(func $start (; 3 ;) (type $v)
|
||||
(if
|
||||
(i32.eqz
|
||||
(call $instanceof/isI32<i32>
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 38)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(call $instanceof/isI32<f64>
|
||||
(f64.const 0)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 39)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
49
tests/compiler/instanceof.ts
Normal file
49
tests/compiler/instanceof.ts
Normal file
@ -0,0 +1,49 @@
|
||||
class A {}
|
||||
class B extends A {}
|
||||
|
||||
var a: A;
|
||||
var b: B;
|
||||
var i: i32;
|
||||
var f: f32;
|
||||
|
||||
assert( a instanceof A );
|
||||
assert( b instanceof A );
|
||||
assert(!(i instanceof A));
|
||||
assert(!(f instanceof A));
|
||||
|
||||
assert(!(a instanceof B));
|
||||
assert( b instanceof B );
|
||||
assert(!(i instanceof B));
|
||||
assert(!(f instanceof B));
|
||||
|
||||
assert(!(a instanceof i32));
|
||||
assert(!(b instanceof i32));
|
||||
assert( i instanceof i32 );
|
||||
assert(!(f instanceof i32));
|
||||
|
||||
assert(!(a instanceof f32));
|
||||
assert(!(b instanceof f32));
|
||||
assert(!(i instanceof f32));
|
||||
assert( f instanceof f32 );
|
||||
|
||||
function isI32<T>(v: T): bool {
|
||||
// should eliminate non-applicable branches (see fixture)
|
||||
if (v instanceof i32) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
assert( isI32(0));
|
||||
assert(!isI32(0.0));
|
||||
|
||||
// TODO: what about nullables?
|
||||
// var an: A | null;
|
||||
// var bn: B | null;
|
||||
//
|
||||
// assert(an instanceof A);
|
||||
// assert(bn instanceof A);
|
||||
//
|
||||
// assert(!(an instanceof B));
|
||||
// assert(bn instanceof B);
|
308
tests/compiler/instanceof.untouched.wat
Normal file
308
tests/compiler/instanceof.untouched.wat
Normal file
@ -0,0 +1,308 @@
|
||||
(module
|
||||
(type $iiiiv (func (param i32 i32 i32 i32)))
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $Fi (func (param f64) (result i32)))
|
||||
(type $v (func))
|
||||
(import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32)))
|
||||
(global $instanceof/a (mut i32) (i32.const 0))
|
||||
(global $instanceof/b (mut i32) (i32.const 0))
|
||||
(global $instanceof/i (mut i32) (i32.const 0))
|
||||
(global $instanceof/f (mut f32) (f32.const 0))
|
||||
(global $HEAP_BASE i32 (i32.const 40))
|
||||
(memory $0 1)
|
||||
(data (i32.const 8) "\0d\00\00\00i\00n\00s\00t\00a\00n\00c\00e\00o\00f\00.\00t\00s\00")
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $instanceof/isI32<i32> (; 1 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(return
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
(func $instanceof/isI32<f64> (; 2 ;) (type $Fi) (param $0 f64) (result i32)
|
||||
(return
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(func $start (; 3 ;) (type $v)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.const 1)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 9)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.const 1)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 10)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 11)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 12)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 14)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.const 1)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 15)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 16)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 17)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 19)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 20)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.const 1)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 21)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 22)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 24)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 25)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 26)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.const 1)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 27)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(call $instanceof/isI32<i32>
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 38)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i32.eqz
|
||||
(call $instanceof/isI32<f64>
|
||||
(f64.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(block
|
||||
(call $~lib/env/abort
|
||||
(i32.const 0)
|
||||
(i32.const 8)
|
||||
(i32.const 39)
|
||||
(i32.const 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user