Lower instanceof in CocoJS; implement IsInst in CocoIL
This commit is contained in:
parent
3f25aca7dd
commit
aa44b46608
5 changed files with 313 additions and 29 deletions
|
@ -14,7 +14,8 @@ import {Script} from "./script";
|
|||
|
||||
const defaultExport: string = "default"; // the ES6 default export name.
|
||||
|
||||
// A mapping from TypeScript binary operator to CocoIL AST operator.
|
||||
// A mapping from TypeScript binary operator to CocoIL AST operator. Note that InstanceOf is not a binary operator;
|
||||
// it is instead lowered to a specific CocoIL IsInstExpression.
|
||||
let binaryOperators = new Map<ts.SyntaxKind, ast.BinaryOperator>([
|
||||
// Arithmetic
|
||||
[ ts.SyntaxKind.PlusToken, "+" ],
|
||||
|
@ -63,7 +64,6 @@ let binaryOperators = new Map<ts.SyntaxKind, ast.BinaryOperator>([
|
|||
|
||||
// Intrinsics
|
||||
// TODO: [ ts.SyntaxKind.InKeyword, "in" ],
|
||||
// TODO: [ ts.SyntaxKind.InstanceOfKeyword, "instanceof" ],
|
||||
]);
|
||||
|
||||
// A mapping from TypeScript unary prefix operator to CocoIL AST operator.
|
||||
|
@ -2577,32 +2577,50 @@ export class Transformer {
|
|||
}
|
||||
}
|
||||
|
||||
private async transformBinaryOperatorExpression(node: ts.BinaryExpression): Promise<ast.BinaryOperatorExpression> {
|
||||
// A few operators aren't faithfully emulated; in those cases, log warnings.
|
||||
if (log.v(3)) {
|
||||
switch (node.operatorToken.kind) {
|
||||
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
log.out(3).info(
|
||||
`ECMAScript operator '${ts.SyntaxKind[node.operatorToken.kind]}' not supported; ` +
|
||||
`until pulumi/coconut#50 is implemented, be careful about subtle behavioral differences`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
private async transformBinaryOperatorExpression(node: ts.BinaryExpression): Promise<ast.Expression> {
|
||||
if (node.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword) {
|
||||
// If this is an instanceof operator, the RHS is going to be a constructor for a type. The
|
||||
// IsInstExpression that this lowers to needs a type token, so fetch that out.
|
||||
let rhsSym: ts.Symbol = this.checker().getSymbolAtLocation(node.right);
|
||||
contract.assert(!!rhsSym);
|
||||
let rhsType: tokens.TypeToken | undefined = await this.resolveTokenFromSymbol(rhsSym);
|
||||
contract.assert(!!rhsType);
|
||||
return <ast.IsInstExpression>{
|
||||
kind: ast.isInstExpressionKind,
|
||||
expression: await this.transformExpression(node.left),
|
||||
type: <ast.TypeToken>{
|
||||
kind: ast.typeTokenKind,
|
||||
tok: rhsType!,
|
||||
},
|
||||
};
|
||||
}
|
||||
else {
|
||||
// A few operators aren't faithfully emulated; in those cases, log warnings.
|
||||
if (log.v(3)) {
|
||||
switch (node.operatorToken.kind) {
|
||||
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
|
||||
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
log.out(3).info(
|
||||
`ECMAScript operator '${ts.SyntaxKind[node.operatorToken.kind]}' not supported; ` +
|
||||
`until pulumi/coconut#50 is implemented, be careful about subtle behavioral differences`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let operator: ast.BinaryOperator | undefined = binaryOperators.get(node.operatorToken.kind);
|
||||
contract.assert(!!operator, `Expected binary operator for: ${ts.SyntaxKind[node.operatorToken.kind]}`);
|
||||
return this.withLocation(node, <ast.BinaryOperatorExpression>{
|
||||
kind: ast.binaryOperatorExpressionKind,
|
||||
operator: operator,
|
||||
left: await this.transformExpression(node.left),
|
||||
right: await this.transformExpression(node.right),
|
||||
});
|
||||
let operator: ast.BinaryOperator | undefined = binaryOperators.get(node.operatorToken.kind);
|
||||
contract.assert(!!operator, `Expected binary operator for: ${ts.SyntaxKind[node.operatorToken.kind]}`);
|
||||
return this.withLocation(node, <ast.BinaryOperatorExpression>{
|
||||
kind: ast.binaryOperatorExpressionKind,
|
||||
operator: operator,
|
||||
left: await this.transformExpression(node.left),
|
||||
right: await this.transformExpression(node.right),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async transformBinarySequenceExpression(node: ts.BinaryExpression): Promise<ast.SequenceExpression> {
|
||||
|
|
|
@ -75,6 +75,103 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"C": {
|
||||
"kind": "Class",
|
||||
"name": {
|
||||
"kind": "Identifier",
|
||||
"ident": "C",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 12,
|
||||
"column": 7
|
||||
},
|
||||
"end": {
|
||||
"line": 12,
|
||||
"column": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
"members": {},
|
||||
"abstract": false,
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 12,
|
||||
"column": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 12,
|
||||
"column": 11
|
||||
}
|
||||
}
|
||||
},
|
||||
"c": {
|
||||
"kind": "ModuleProperty",
|
||||
"name": {
|
||||
"kind": "Identifier",
|
||||
"ident": "c",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 13,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 13,
|
||||
"column": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"kind": "TypeToken",
|
||||
"tok": "dynamic",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 13,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 13,
|
||||
"column": 21
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isc": {
|
||||
"kind": "ModuleProperty",
|
||||
"name": {
|
||||
"kind": "Identifier",
|
||||
"ident": "isc",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 14,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 14,
|
||||
"column": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"kind": "TypeToken",
|
||||
"tok": "bool",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 14,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 14,
|
||||
"column": 36
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".init": {
|
||||
"kind": "ModuleMethod",
|
||||
"name": {
|
||||
|
@ -273,6 +370,155 @@
|
|||
"column": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "ExpressionStatement",
|
||||
"expression": {
|
||||
"kind": "BinaryOperatorExpression",
|
||||
"left": {
|
||||
"kind": "LoadLocationExpression",
|
||||
"name": {
|
||||
"kind": "Token",
|
||||
"tok": "basic/casts:index:c",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 13,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 13,
|
||||
"column": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 13,
|
||||
"column": 14
|
||||
},
|
||||
"end": {
|
||||
"line": 13,
|
||||
"column": 21
|
||||
}
|
||||
}
|
||||
},
|
||||
"operator": "=",
|
||||
"right": {
|
||||
"kind": "NewExpression",
|
||||
"type": {
|
||||
"kind": "TypeToken",
|
||||
"tok": "basic/casts:index:C",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 13,
|
||||
"column": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 13,
|
||||
"column": 19
|
||||
}
|
||||
}
|
||||
},
|
||||
"arguments": [],
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 13,
|
||||
"column": 14
|
||||
},
|
||||
"end": {
|
||||
"line": 13,
|
||||
"column": 21
|
||||
}
|
||||
}
|
||||
},
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 13,
|
||||
"column": 14
|
||||
},
|
||||
"end": {
|
||||
"line": 13,
|
||||
"column": 21
|
||||
}
|
||||
}
|
||||
},
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 13,
|
||||
"column": 14
|
||||
},
|
||||
"end": {
|
||||
"line": 13,
|
||||
"column": 21
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "ExpressionStatement",
|
||||
"expression": {
|
||||
"kind": "BinaryOperatorExpression",
|
||||
"left": {
|
||||
"kind": "LoadLocationExpression",
|
||||
"name": {
|
||||
"kind": "Token",
|
||||
"tok": "basic/casts:index:isc",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 14,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 14,
|
||||
"column": 8
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"operator": "=",
|
||||
"right": {
|
||||
"kind": "IsInstExpression",
|
||||
"expression": {
|
||||
"kind": "LoadLocationExpression",
|
||||
"name": {
|
||||
"kind": "Token",
|
||||
"tok": "basic/casts:index:c",
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 14,
|
||||
"column": 21
|
||||
},
|
||||
"end": {
|
||||
"line": 14,
|
||||
"column": 22
|
||||
}
|
||||
}
|
||||
},
|
||||
"loc": {
|
||||
"file": "index.ts",
|
||||
"start": {
|
||||
"line": 14,
|
||||
"column": 21
|
||||
},
|
||||
"end": {
|
||||
"line": 14,
|
||||
"column": 22
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"kind": "TypeToken",
|
||||
"tok": "basic/casts:index:C"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -296,7 +542,7 @@
|
|||
"column": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 12,
|
||||
"line": 16,
|
||||
"column": 1
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,3 +9,7 @@ let b: any = <any>a; // ok.
|
|||
// TODO: a way to baseline expected runtime failures.
|
||||
// let d: number = <number>b; // dynamically rejected.
|
||||
|
||||
class C {}
|
||||
let c: any = new C();
|
||||
let isc: boolean = (c instanceof C);
|
||||
|
||||
|
|
|
@ -99,6 +99,8 @@ func (a *astBinder) After(node ast.Node) {
|
|||
a.checkBinaryOperatorExpression(n)
|
||||
case *ast.CastExpression:
|
||||
a.checkCastExpression(n)
|
||||
case *ast.IsInstExpression:
|
||||
a.checkIsInstExpression(n)
|
||||
case *ast.TypeOfExpression:
|
||||
a.checkTypeOfExpression(n)
|
||||
case *ast.ConditionalExpression:
|
||||
|
@ -685,6 +687,11 @@ func (a *astBinder) checkCastExpression(node *ast.CastExpression) {
|
|||
a.b.ctx.RegisterType(node, to)
|
||||
}
|
||||
|
||||
func (a *astBinder) checkIsInstExpression(node *ast.IsInstExpression) {
|
||||
// An isinst produces a bool indicating whether the expression is of the target type.
|
||||
a.b.ctx.RegisterType(node, types.Bool)
|
||||
}
|
||||
|
||||
func (a *astBinder) checkTypeOfExpression(node *ast.TypeOfExpression) {
|
||||
// A typeof produces a string representation of the expression's type.
|
||||
a.b.ctx.RegisterType(node, types.String)
|
||||
|
|
|
@ -2006,8 +2006,17 @@ func (e *evaluator) evalCastExpression(node *ast.CastExpression) (*rt.Object, *r
|
|||
}
|
||||
|
||||
func (e *evaluator) evalIsInstExpression(node *ast.IsInstExpression) (*rt.Object, *rt.Unwind) {
|
||||
contract.Failf("Evaluation of %v nodes not yet implemented", reflect.TypeOf(node))
|
||||
return nil, nil
|
||||
// Evaluate the underlying expression.
|
||||
obj, uw := e.evalExpression(node.Expression)
|
||||
if uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
|
||||
// Now check the type and produce a bool object indicating whether the type is good.
|
||||
from := obj.Type()
|
||||
to := e.ctx.LookupType(node.Type)
|
||||
isinst := types.CanConvert(from, to)
|
||||
return e.alloc.NewBool(node, isinst), nil
|
||||
}
|
||||
|
||||
func (e *evaluator) evalTypeOfExpression(node *ast.TypeOfExpression) (*rt.Object, *rt.Unwind) {
|
||||
|
|
Loading…
Reference in a new issue