diff --git a/src/ast.ts b/src/ast.ts index ee33029cee..2980df67b7 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -114,29 +114,6 @@ export function nodeIsConstantValue(kind: NodeKind): bool { return false; } -/** Checks if a node might be callable. */ -export function nodeIsCallable(kind: NodeKind): bool { - switch (kind) { - case NodeKind.IDENTIFIER: - case NodeKind.ASSERTION: // if kind=NONNULL - case NodeKind.CALL: - case NodeKind.ELEMENTACCESS: - case NodeKind.PARENTHESIZED: - case NodeKind.PROPERTYACCESS: - case NodeKind.SUPER: return true; - } - return false; -} - -/** Checks if a node might be callable with generic arguments. */ -export function nodeIsGenericCallable(kind: NodeKind): bool { - switch (kind) { - case NodeKind.IDENTIFIER: - case NodeKind.PROPERTYACCESS: return true; - } - return false; -} - /** Base class of all nodes. */ export abstract class Node { @@ -428,14 +405,14 @@ export abstract class Node { } static createNewExpression( - expression: Expression, + typeName: TypeName, typeArgs: TypeNode[] | null, args: Expression[], range: Range ): NewExpression { var expr = new NewExpression(); expr.range = range; - expr.expression = expression; + expr.typeName = typeName; expr.typeArguments = typeArgs; expr.arguments = args; return expr; @@ -1489,8 +1466,35 @@ export class IntegerLiteralExpression extends LiteralExpression { } /** Represents a `new` expression. Like a call but with its own kind. */ -export class NewExpression extends CallExpression { +export class NewExpression extends Expression { kind = NodeKind.NEW; + + /** Type being constructed. */ + typeName: TypeName; + /** Provided type arguments. */ + typeArguments: TypeNode[] | null; + /** Provided arguments. */ + arguments: Expression[]; + + /** Gets the type arguments range for reporting. */ + get typeArgumentsRange(): Range { + var typeArguments = this.typeArguments; + var numTypeArguments: i32; + if (typeArguments && (numTypeArguments = typeArguments.length)) { + return Range.join(typeArguments[0].range, typeArguments[numTypeArguments - 1].range); + } + return this.typeName.range; + } + + /** Gets the arguments range for reporting. */ + get argumentsRange(): Range { + var args = this.arguments; + var numArguments = args.length; + if (numArguments) { + return Range.join(args[0].range, args[numArguments - 1].range); + } + return this.typeName.range; + } } /** Represents a `null` expression. */ @@ -1639,12 +1643,10 @@ export class Source extends Node { statements: Statement[]; /** Full source text. */ text: string; - /** Tokenizer reference. */ - tokenizer: Tokenizer | null = null; /** Source map index. */ debugInfoIndex: i32 = -1; /** Re-exported sources. */ - exportPaths: Set | null = null; + exportPaths: string[] | null = null; /** Constructs a new source node. */ constructor(normalizedPath: string, text: string, kind: SourceKind) { diff --git a/src/compiler.ts b/src/compiler.ts index cd3be833aa..a5eeaa8329 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7726,15 +7726,12 @@ export class Compiler extends DiagnosticEmitter { var flow = this.currentFlow; // obtain the class being instantiated - var target = this.resolver.lookupExpression( // reports - expression.expression, - flow - ); + var target = this.resolver.resolveTypeName(expression.typeName, flow.actualFunction); if (!target) return module.unreachable(); if (target.kind != ElementKind.CLASS_PROTOTYPE) { this.error( DiagnosticCode.This_expression_is_not_constructable, - expression.expression.range + expression.typeName.range ); return this.module.unreachable(); } diff --git a/src/extra/ast.ts b/src/extra/ast.ts index 384473196f..da80807295 100644 --- a/src/extra/ast.ts +++ b/src/extra/ast.ts @@ -543,9 +543,12 @@ export class ASTBuilder { } visitCallExpression(node: CallExpression): void { - var sb = this.sb; this.visitNode(node.expression); - var typeArguments = node.typeArguments; + this.visitArguments(node.typeArguments, node.arguments); + } + + private visitArguments(typeArguments: TypeNode[] | null, args: Expression[]): void { + var sb = this.sb; if (typeArguments) { let numTypeArguments = typeArguments.length; if (numTypeArguments) { @@ -560,7 +563,6 @@ export class ASTBuilder { } else { sb.push("("); } - var args = node.arguments; var numArgs = args.length; if (numArgs) { this.visitNode(args[0]); @@ -757,7 +759,8 @@ export class ASTBuilder { visitNewExpression(node: NewExpression): void { this.sb.push("new "); - this.visitCallExpression(node); + this.visitTypeName(node.typeName); + this.visitArguments(node.typeArguments, node.arguments); } visitParenthesizedExpression(node: ParenthesizedExpression): void { diff --git a/src/parser.ts b/src/parser.ts index 55003b2aae..ca689452f3 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -37,6 +37,7 @@ import { Source, SourceKind, TypeNode, + TypeName, NamedTypeNode, FunctionTypeNode, ArrowKind, @@ -84,9 +85,7 @@ import { VoidStatement, WhileStatement, - mangleInternalPath, - nodeIsCallable, - nodeIsGenericCallable + mangleInternalPath } from "./ast"; /** Parser interface. */ @@ -149,7 +148,6 @@ export class Parser extends DiagnosticEmitter { // tokenize and parse var tn = new Tokenizer(source, program.diagnostics); tn.onComment = this.onComment; - source.tokenizer = tn; var statements = source.statements; while (!tn.skip(Token.ENDOFFILE)) { let statement = this.parseTopLevelStatement(tn, null); @@ -400,6 +398,33 @@ export class Parser extends DiagnosticEmitter { return this.program; } + // types + + /** Parses a type name. */ + parseTypeName( + tn: Tokenizer + ): TypeName | null { + + // at: Identifier ('.' Identifier)* + + var first = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); + var current = first; + while (tn.skip(Token.DOT)) { + if (tn.skip(Token.IDENTIFIER)) { + let next = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); + current.next = next; + current = next; + } else { + this.error( + DiagnosticCode.Identifier_expected, + tn.range(tn.pos) + ); + return null; + } + } + return first; + } + /** Parses a type. */ parseType( tn: Tokenizer, @@ -407,6 +432,8 @@ export class Parser extends DiagnosticEmitter { suppressErrors: bool = false ): TypeNode | null { + // before: Type + // NOTE: this parses our limited subset var token = tn.next(); var startPos = tn.tokenPos; @@ -504,26 +531,11 @@ export class Parser extends DiagnosticEmitter { // Identifier } else if (token == Token.IDENTIFIER) { - let first = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); - let current = first; + let name = this.parseTypeName(tn); + if (!name) return null; let parameters: NamedTypeNode[] | null = null; let nullable = false; - // Identifier ('.' Identifier)+ - while (tn.skip(Token.DOT)) { - if (tn.skip(Token.IDENTIFIER)) { - let next = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); - current.next = next; - current = next; - } else { - this.error( - DiagnosticCode.Identifier_expected, - tn.range(tn.pos) - ); - return null; - } - } - // Name if (tn.skip(Token.LESSTHAN)) { do { @@ -556,7 +568,7 @@ export class Parser extends DiagnosticEmitter { return null; } } - type = Node.createNamedType(first, parameters || [], nullable, tn.range(startPos, tn.pos)); + type = Node.createNamedType(name, parameters || [], nullable, tn.range(startPos, tn.pos)); } else { if (!suppressErrors) { this.error( @@ -798,7 +810,7 @@ export class Parser extends DiagnosticEmitter { let name = tn.readIdentifier(); let expression: Expression = Node.createIdentifierExpression(name, tn.range(startPos, tn.pos)); while (tn.skip(Token.DOT)) { - if (tn.skipIdentifier()) { + if (tn.skipIdentifier(IdentifierHandling.PREFER)) { name = tn.readIdentifier(); expression = Node.createPropertyAccessExpression( expression, @@ -2357,8 +2369,9 @@ export class Parser extends DiagnosticEmitter { let ret = Node.createExportStatement(null, path, isDeclare, tn.range(startPos, tn.pos)); let internalPath = assert(ret.internalPath); let source = tn.source; - if (!source.exportPaths) source.exportPaths = new Set(); - source.exportPaths.add(internalPath); + let exportPaths = source.exportPaths; + if (!exportPaths) source.exportPaths = [ internalPath ]; + else if (!exportPaths.includes(internalPath)) exportPaths.push(internalPath); if (!this.seenlog.has(internalPath)) { this.dependees.set(internalPath, this.currentSource); this.backlog.push(internalPath); @@ -3250,68 +3263,87 @@ export class Parser extends DiagnosticEmitter { } // expressions - // see: http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm#climbing parseExpressionStart( tn: Tokenizer ): Expression | null { - var token = tn.next(IdentifierHandling.PREFER); var startPos = tn.tokenPos; - var precedence = determinePrecedenceStart(token); - if (precedence != Precedence.NONE) { - let operand: Expression | null; + switch (token) { - // TODO: SpreadExpression, YieldExpression (currently become unsupported UnaryPrefixExpressions) + // TODO: SpreadExpression, YieldExpression + case Token.DOT_DOT_DOT: + case Token.YIELD: // fallthrough to unsupported UnaryPrefixExpression - // NewExpression - if (token == Token.NEW) { - operand = this.parseExpression(tn, Precedence.CALL); + // UnaryPrefixExpression + case Token.EXCLAMATION: + case Token.TILDE: + case Token.PLUS: + case Token.MINUS: + case Token.TYPEOF: + case Token.VOID: + case Token.DELETE: { + let operand = this.parseExpression(tn, Precedence.UNARY_PREFIX); if (!operand) return null; - if (operand.kind == NodeKind.CALL) { - return Node.createNewExpression( - (operand).expression, - (operand).typeArguments, - (operand).arguments, - tn.range(startPos, tn.pos) - ); - } else { - this.error( - DiagnosticCode.This_expression_is_not_constructable, - operand.range - ); - } - return null; - } else { - operand = this.parseExpression(tn, precedence); + return Node.createUnaryPrefixExpression(token, operand, tn.range(startPos, tn.pos)); + } + case Token.PLUS_PLUS: + case Token.MINUS_MINUS: { + let operand = this.parseExpression(tn, Precedence.UNARY_PREFIX); if (!operand) return null; + switch (operand.kind) { + case NodeKind.IDENTIFIER: + case NodeKind.ELEMENTACCESS: + case NodeKind.PROPERTYACCESS: break; + default: { + this.error( + DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, + operand.range + ); + } + } + return Node.createUnaryPrefixExpression(token, operand, tn.range(startPos, tn.pos)); } - // UnaryPrefixExpression - if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS) { - if ( - operand.kind != NodeKind.IDENTIFIER && - operand.kind != NodeKind.ELEMENTACCESS && - operand.kind != NodeKind.PROPERTYACCESS - ) { + // NewExpression + case Token.NEW: { + if (!tn.skipIdentifier()) { this.error( - DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, - operand.range + DiagnosticCode.Identifier_expected, + tn.range() ); + return null; + } + let typeName = this.parseTypeName(tn); + if (!typeName) return null; + let typeArguments: TypeNode[] | null = null; + let arguments_: Expression[] | null = null; + if ( + tn.skip(Token.OPENPAREN) + || + (typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn)) !== null + ) { + arguments_ = this.parseArguments(tn); + if (!arguments_) return null; + } else { + arguments_ = []; // new Type; } + return Node.createNewExpression( + typeName, + typeArguments, + arguments_, + tn.range(startPos, tn.pos) + ); } - return Node.createUnaryPrefixExpression(token, operand, tn.range(startPos, tn.pos)); - } - - var expr: Expression | null = null; - switch (token) { + // Special IdentifierExpression case Token.NULL: return Node.createNullExpression(tn.range()); case Token.TRUE: return Node.createTrueExpression(tn.range()); case Token.FALSE: return Node.createFalseExpression(tn.range()); + case Token.THIS: return Node.createThisExpression(tn.range()); + case Token.CONSTRUCTOR: return Node.createConstructorExpression(tn.range()); - // ParenthesizedExpression - // FunctionExpression + // ParenthesizedExpression or FunctionExpression case Token.OPENPAREN: { // determine whether this is a function expression @@ -3389,8 +3421,8 @@ export class Parser extends DiagnosticEmitter { tn.reset(state); // parse parenthesized - expr = this.parseExpression(tn); - if (!expr) return null; + let inner = this.parseExpression(tn); + if (!inner) return null; if (!tn.skip(Token.CLOSEPAREN)) { this.error( DiagnosticCode._0_expected, @@ -3398,12 +3430,14 @@ export class Parser extends DiagnosticEmitter { ); return null; } - return Node.createParenthesizedExpression(expr, tn.range(startPos, tn.pos)); + inner = Node.createParenthesizedExpression(inner, tn.range(startPos, tn.pos)); + return this.maybeParseCallExpression(tn, inner); } // ArrayLiteralExpression case Token.OPENBRACKET: { let elementExpressions = new Array(); while (!tn.skip(Token.CLOSEBRACKET)) { + let expr: Expression | null; if (tn.peek() == Token.COMMA) { expr = null; // omitted } else { @@ -3484,7 +3518,7 @@ export class Parser extends DiagnosticEmitter { ); return null; } - expr = this.parseExpression(tn, Precedence.CALL); + let expr = this.parseExpression(tn, Precedence.CALL); if (!expr) return null; return Node.createAssertionExpression( AssertionKind.PREFIX, @@ -3514,13 +3548,7 @@ export class Parser extends DiagnosticEmitter { startPos ); } - return identifier; - } - case Token.THIS: { - return Node.createThisExpression(tn.range(startPos, tn.pos)); - } - case Token.CONSTRUCTOR: { - return Node.createConstructorExpression(tn.range(startPos, tn.pos)); + return this.maybeParseCallExpression(tn, identifier, true); } case Token.SUPER: { if (tn.peek() != Token.DOT && tn.nextToken != Token.OPENPAREN) { @@ -3529,7 +3557,8 @@ export class Parser extends DiagnosticEmitter { tn.range() ); } - return Node.createSuperExpression(tn.range(startPos, tn.pos)); + let expr = Node.createSuperExpression(tn.range(startPos, tn.pos)); + return this.maybeParseCallExpression(tn, expr); } case Token.STRINGLITERAL: { return Node.createStringLiteralExpression(tn.readString(), tn.range(startPos, tn.pos)); @@ -3558,7 +3587,9 @@ export class Parser extends DiagnosticEmitter { ); } case Token.FUNCTION: { - return this.parseFunctionExpression(tn); + let expr = this.parseFunctionExpression(tn); + if (!expr) return null; + return this.maybeParseCallExpression(tn, expr); } case Token.CLASS: { return this.parseClassExpression(tn); @@ -3639,20 +3670,19 @@ export class Parser extends DiagnosticEmitter { precedence: Precedence = Precedence.COMMA ): Expression | null { assert(precedence != Precedence.NONE); - var expr = this.parseExpressionStart(tn); if (!expr) return null; - expr = this.maybeParseCallExpression(tn, expr); // simple call like on an Identifier - var startPos = expr.range.start; - var token: Token; - var next: Expression | null = null; + + // precedence climbing + // see: http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm#climbing var nextPrecedence: Precedence; while ( - (nextPrecedence = determinePrecedence(token = tn.peek())) >= precedence - ) { // precedence climbing - tn.next(); + (nextPrecedence = determinePrecedence(tn.peek())) >= precedence + ) { + let token = tn.next(); switch (token) { + // AssertionExpression case Token.AS: { let toType = this.parseType(tn); // reports @@ -3672,6 +3702,7 @@ export class Parser extends DiagnosticEmitter { null, tn.range(startPos, tn.pos) ); + expr = this.maybeParseCallExpression(tn, expr); break; } // InstanceOfExpression @@ -3687,7 +3718,7 @@ export class Parser extends DiagnosticEmitter { } // ElementAccessExpression case Token.OPENBRACKET: { - next = this.parseExpression(tn); // reports + let next = this.parseExpression(tn); // reports if (!next) return null; if (!tn.skip(Token.CLOSEBRACKET)) { this.error( @@ -3701,6 +3732,7 @@ export class Parser extends DiagnosticEmitter { next, tn.range(startPos, tn.pos) ); + expr = this.maybeParseCallExpression(tn, expr); break; } // UnaryPostfixExpression @@ -3758,27 +3790,19 @@ export class Parser extends DiagnosticEmitter { expr = Node.createCommaExpression(commaExprs, tn.range(startPos, tn.pos)); break; } - default: { - - // PropertyAccessExpression - if (token == Token.DOT) { - if (tn.skipIdentifier()) { - next = Node.createIdentifierExpression(tn.readIdentifier(), tn.range()); - } else { - next = this.parseExpression(tn, - isRightAssociative(token) - ? nextPrecedence - : nextPrecedence + 1 - ); - if (!next) return null; - } - if (next.kind == NodeKind.IDENTIFIER) { // expr '.' Identifier - expr = Node.createPropertyAccessExpression( - expr, - next, - tn.range(startPos, tn.pos) - ); - } else if (next.kind == NodeKind.CALL) { // expr '.' CallExpression + // PropertyAccessExpression + case Token.DOT: { + if (tn.skipIdentifier()) { // expr '.' Identifier + let next = Node.createIdentifierExpression(tn.readIdentifier(), tn.range()); + expr = Node.createPropertyAccessExpression( + expr, + next, + tn.range(startPos, tn.pos) + ); + } else { + let next = this.parseExpression(tn, nextPrecedence + 1); + if (!next) return null; + if (next.kind == NodeKind.CALL) { // expr '.' CallExpression expr = this.joinPropertyCall(tn, startPos, expr, next); if (!expr) return null; } else { @@ -3788,21 +3812,59 @@ export class Parser extends DiagnosticEmitter { ); return null; } - - // BinaryExpression - } else { - next = this.parseExpression(tn, - isRightAssociative(token) - ? nextPrecedence - : nextPrecedence + 1 - ); - if (!next) return null; - expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); } + expr = this.maybeParseCallExpression(tn, expr, true); break; } + // BinaryExpression (right associative) + case Token.EQUALS: + case Token.PLUS_EQUALS: + case Token.MINUS_EQUALS: + case Token.ASTERISK_ASTERISK_EQUALS: + case Token.ASTERISK_EQUALS: + case Token.SLASH_EQUALS: + case Token.PERCENT_EQUALS: + case Token.LESSTHAN_LESSTHAN_EQUALS: + case Token.GREATERTHAN_GREATERTHAN_EQUALS: + case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: + case Token.AMPERSAND_EQUALS: + case Token.CARET_EQUALS: + case Token.BAR_EQUALS: + case Token.ASTERISK_ASTERISK: { + let next = this.parseExpression(tn, nextPrecedence); + if (!next) return null; + expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); + break; + } + // BinaryExpression + case Token.LESSTHAN: + case Token.GREATERTHAN: + case Token.LESSTHAN_EQUALS: + case Token.GREATERTHAN_EQUALS: + case Token.EQUALS_EQUALS: + case Token.EQUALS_EQUALS_EQUALS: + case Token.EXCLAMATION_EQUALS_EQUALS: + case Token.EXCLAMATION_EQUALS: + case Token.PLUS: + case Token.MINUS: + case Token.ASTERISK: + case Token.SLASH: + case Token.PERCENT: + case Token.LESSTHAN_LESSTHAN: + case Token.GREATERTHAN_GREATERTHAN: + case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: + case Token.AMPERSAND: + case Token.BAR: + case Token.CARET: + case Token.AMPERSAND_AMPERSAND: + case Token.BAR_BAR: { + let next = this.parseExpression(tn, nextPrecedence + 1); + if (!next) return null; + expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); + break; + } + default: assert(false); // filtered by determinePrecedence } - expr = this.maybeParseCallExpression(tn, expr); // compound call like on an ElementAccess } return expr; } @@ -3843,24 +3905,24 @@ export class Parser extends DiagnosticEmitter { private maybeParseCallExpression( tn: Tokenizer, - expr: Expression + expr: Expression, + potentiallyGeneric: bool = false ): Expression { - if (nodeIsCallable(expr.kind)) { - let typeArguments: TypeNode[] | null = null; - while ( - tn.skip(Token.OPENPAREN) - || - nodeIsGenericCallable(expr.kind) && (typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn)) !== null - ) { - let args = this.parseArguments(tn); - if (!args) break; - expr = Node.createCallExpression( // is again callable - expr, - typeArguments, - args, - tn.range(expr.range.start, tn.pos) - ); - } + var typeArguments: TypeNode[] | null = null; + while ( + tn.skip(Token.OPENPAREN) + || + potentiallyGeneric && (typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn)) !== null + ) { + let args = this.parseArguments(tn); + if (!args) break; + expr = Node.createCallExpression( // is again callable + expr, + typeArguments, + args, + tn.range(expr.range.start, tn.pos) + ); + potentiallyGeneric = false; } return expr; } @@ -3975,25 +4037,6 @@ export const enum Precedence { GROUPING } -/** Determines the precedence of a starting token. */ -function determinePrecedenceStart(kind: Token): Precedence { - switch (kind) { - case Token.DOT_DOT_DOT: return Precedence.SPREAD; - case Token.YIELD: return Precedence.YIELD; - case Token.EXCLAMATION: - case Token.TILDE: - case Token.PLUS: - case Token.MINUS: - case Token.PLUS_PLUS: - case Token.MINUS_MINUS: - case Token.TYPEOF: - case Token.VOID: - case Token.DELETE: return Precedence.UNARY_PREFIX; - case Token.NEW: return Precedence.MEMBERACCESS; - } - return Precedence.NONE; -} - /** Determines the precende of a non-starting token. */ function determinePrecedence(kind: Token): Precedence { switch (kind) { @@ -4046,25 +4089,3 @@ function determinePrecedence(kind: Token): Precedence { } return Precedence.NONE; } - -/** Determines whether a non-starting token is right associative. */ -function isRightAssociative(kind: Token): bool { - switch (kind) { - case Token.EQUALS: - case Token.PLUS_EQUALS: - case Token.MINUS_EQUALS: - case Token.ASTERISK_ASTERISK_EQUALS: - case Token.ASTERISK_EQUALS: - case Token.SLASH_EQUALS: - case Token.PERCENT_EQUALS: - case Token.LESSTHAN_LESSTHAN_EQUALS: - case Token.GREATERTHAN_GREATERTHAN_EQUALS: - case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: - case Token.AMPERSAND_EQUALS: - case Token.CARET_EQUALS: - case Token.BAR_EQUALS: - case Token.QUESTION: - case Token.ASTERISK_ASTERISK: return true; - default: return false; - } -} diff --git a/src/resolver.ts b/src/resolver.ts index 3ef1d2228a..cc1fe23c9c 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -2414,7 +2414,7 @@ export class Resolver extends DiagnosticEmitter { /** How to proceed with eventual diagnostics. */ reportMode: ReportMode = ReportMode.REPORT ): Element | null { - var element = this.lookupExpression(node.expression, ctxFlow, ctxType, reportMode); + var element = this.resolveTypeName(node.typeName, ctxFlow.actualFunction, reportMode); if (!element) return null; if (element.kind == ElementKind.CLASS_PROTOTYPE) { return this.resolveClassInclTypeArguments( diff --git a/tests/compiler/new.json b/tests/compiler/new.json new file mode 100644 index 0000000000..b1da366ff4 --- /dev/null +++ b/tests/compiler/new.json @@ -0,0 +1,5 @@ +{ + "asc_flags": [ + "--runtime none" + ] +} \ No newline at end of file diff --git a/tests/compiler/new.optimized.wat b/tests/compiler/new.optimized.wat new file mode 100644 index 0000000000..4b11d9201e --- /dev/null +++ b/tests/compiler/new.optimized.wat @@ -0,0 +1,118 @@ +(module + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$v (func)) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (memory $0 0) + (global $new/ref (mut i32) (i32.const 0)) + (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) + (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) + (global $new/gen (mut i32) (i32.const 0)) + (global $new/ref2 (mut i32) (i32.const 0)) + (export "memory" (memory $0)) + (start $start) + (func $~lib/rt/stub/maybeGrowMemory (; 0 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + (local $2 i32) + local.get $0 + memory.size + local.tee $2 + i32.const 16 + i32.shl + local.tee $1 + i32.gt_u + if + local.get $2 + local.get $0 + local.get $1 + i32.sub + i32.const 65535 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.tee $1 + local.get $2 + local.get $1 + i32.gt_s + select + memory.grow + i32.const 0 + i32.lt_s + if + local.get $1 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + end + local.get $0 + global.set $~lib/rt/stub/offset + ) + (func $~lib/rt/stub/__alloc (; 1 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + global.get $~lib/rt/stub/offset + i32.const 16 + i32.add + local.tee $2 + i32.const 16 + i32.add + call $~lib/rt/stub/maybeGrowMemory + local.get $2 + i32.const 16 + i32.sub + local.tee $1 + i32.const 16 + i32.store + local.get $1 + i32.const -1 + i32.store offset=4 + local.get $1 + local.get $0 + i32.store offset=8 + local.get $1 + i32.const 0 + i32.store offset=12 + local.get $2 + ) + (func $start:new (; 2 ;) (type $FUNCSIG$v) + i32.const 16 + global.set $~lib/rt/stub/startOffset + i32.const 16 + global.set $~lib/rt/stub/offset + i32.const 3 + call $~lib/rt/stub/__alloc + global.set $new/ref + i32.const 3 + call $~lib/rt/stub/__alloc + global.set $new/ref + i32.const 3 + call $~lib/rt/stub/__alloc + global.set $new/ref + i32.const 4 + call $~lib/rt/stub/__alloc + global.set $new/gen + i32.const 4 + call $~lib/rt/stub/__alloc + global.set $new/gen + i32.const 5 + call $~lib/rt/stub/__alloc + global.set $new/ref2 + i32.const 5 + call $~lib/rt/stub/__alloc + global.set $new/ref2 + i32.const 5 + call $~lib/rt/stub/__alloc + global.set $new/ref2 + ) + (func $start (; 3 ;) (type $FUNCSIG$v) + call $start:new + ) + (func $null (; 4 ;) (type $FUNCSIG$v) + nop + ) +) diff --git a/tests/compiler/new.ts b/tests/compiler/new.ts new file mode 100644 index 0000000000..87827c0da5 --- /dev/null +++ b/tests/compiler/new.ts @@ -0,0 +1,27 @@ +class Ref { + get ref(): Ref { return this; } +} + +var ref: Ref; +ref = new Ref(); +ref = new Ref; +ref = new Ref().ref; + +class Gen { + get gen(): Gen { return this; } +} + +var gen: Gen; +gen = new Gen(); +gen = new Gen().gen; + +namespace ns { + export class Ref { + get ref(): Ref { return this; } + } +} + +var ref2: ns.Ref; +ref2 = new ns.Ref(); +ref2 = new ns.Ref; +ref2 = new ns.Ref().ref; diff --git a/tests/compiler/new.untouched.wat b/tests/compiler/new.untouched.wat new file mode 100644 index 0000000000..96deeb822d --- /dev/null +++ b/tests/compiler/new.untouched.wat @@ -0,0 +1,304 @@ +(module + (type $FUNCSIG$iii (func (param i32 i32) (result i32))) + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (type $FUNCSIG$v (func)) + (memory $0 0) + (table $0 1 funcref) + (elem (i32.const 0) $null) + (global $new/ref (mut i32) (i32.const 0)) + (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) + (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) + (global $new/gen (mut i32) (i32.const 0)) + (global $new/ref2 (mut i32) (i32.const 0)) + (global $~lib/heap/__heap_base i32 (i32.const 8)) + (export "memory" (memory $0)) + (start $start) + (func $~lib/rt/stub/maybeGrowMemory (; 0 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + memory.size + local.set $1 + local.get $1 + i32.const 16 + i32.shl + local.set $2 + local.get $0 + local.get $2 + i32.gt_u + if + local.get $0 + local.get $2 + i32.sub + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $3 + local.get $1 + local.tee $4 + local.get $3 + local.tee $5 + local.get $4 + local.get $5 + i32.gt_s + select + local.set $4 + local.get $4 + memory.grow + i32.const 0 + i32.lt_s + if + local.get $3 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + end + local.get $0 + global.set $~lib/rt/stub/offset + ) + (func $~lib/rt/stub/__alloc (; 1 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + local.get $0 + i32.const 1073741808 + i32.gt_u + if + unreachable + end + global.get $~lib/rt/stub/offset + i32.const 16 + i32.add + local.set $2 + local.get $0 + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.tee $3 + i32.const 16 + local.tee $4 + local.get $3 + local.get $4 + i32.gt_u + select + local.set $5 + local.get $2 + local.get $5 + i32.add + call $~lib/rt/stub/maybeGrowMemory + local.get $2 + i32.const 16 + i32.sub + local.set $6 + local.get $6 + local.get $5 + i32.store + local.get $6 + i32.const -1 + i32.store offset=4 + local.get $6 + local.get $1 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $2 + ) + (func $~lib/rt/stub/__retain (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + ) + (func $new/Ref#constructor (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 3 + call $~lib/rt/stub/__alloc + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $~lib/rt/stub/__release (; 4 ;) (type $FUNCSIG$vi) (param $0 i32) + nop + ) + (func $new/Ref#get:ref (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $~lib/rt/stub/__retain + ) + (func $new/Gen#constructor (; 6 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 4 + call $~lib/rt/stub/__alloc + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $new/Gen#get:gen (; 7 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $~lib/rt/stub/__retain + ) + (func $new/ns.Ref#constructor (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 5 + call $~lib/rt/stub/__alloc + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $new/ns.Ref#get:ref (; 9 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $~lib/rt/stub/__retain + ) + (func $start:new (; 10 ;) (type $FUNCSIG$v) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + global.get $~lib/heap/__heap_base + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + global.set $~lib/rt/stub/startOffset + global.get $~lib/rt/stub/startOffset + global.set $~lib/rt/stub/offset + i32.const 0 + call $new/Ref#constructor + local.set $0 + global.get $new/ref + call $~lib/rt/stub/__release + local.get $0 + global.set $new/ref + i32.const 0 + call $new/Ref#constructor + local.set $0 + global.get $new/ref + call $~lib/rt/stub/__release + local.get $0 + global.set $new/ref + i32.const 0 + call $new/Ref#constructor + local.tee $0 + call $new/Ref#get:ref + local.tee $1 + local.tee $2 + global.get $new/ref + local.tee $3 + i32.ne + if + local.get $2 + call $~lib/rt/stub/__retain + local.set $2 + local.get $3 + call $~lib/rt/stub/__release + end + local.get $2 + global.set $new/ref + i32.const 0 + call $new/Gen#constructor + local.set $3 + global.get $new/gen + call $~lib/rt/stub/__release + local.get $3 + global.set $new/gen + i32.const 0 + call $new/Gen#constructor + local.tee $3 + call $new/Gen#get:gen + local.tee $2 + local.tee $4 + global.get $new/gen + local.tee $5 + i32.ne + if + local.get $4 + call $~lib/rt/stub/__retain + local.set $4 + local.get $5 + call $~lib/rt/stub/__release + end + local.get $4 + global.set $new/gen + i32.const 0 + call $new/ns.Ref#constructor + local.set $5 + global.get $new/ref2 + call $~lib/rt/stub/__release + local.get $5 + global.set $new/ref2 + i32.const 0 + call $new/ns.Ref#constructor + local.set $4 + global.get $new/ref2 + call $~lib/rt/stub/__release + local.get $4 + global.set $new/ref2 + i32.const 0 + call $new/ns.Ref#constructor + local.tee $4 + call $new/ns.Ref#get:ref + local.tee $5 + local.tee $6 + global.get $new/ref2 + local.tee $7 + i32.ne + if + local.get $6 + call $~lib/rt/stub/__retain + local.set $6 + local.get $7 + call $~lib/rt/stub/__release + end + local.get $6 + global.set $new/ref2 + local.get $0 + call $~lib/rt/stub/__release + local.get $1 + call $~lib/rt/stub/__release + local.get $2 + call $~lib/rt/stub/__release + local.get $3 + call $~lib/rt/stub/__release + local.get $4 + call $~lib/rt/stub/__release + local.get $5 + call $~lib/rt/stub/__release + ) + (func $start (; 11 ;) (type $FUNCSIG$v) + call $start:new + ) + (func $null (; 12 ;) (type $FUNCSIG$v) + ) +)