mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-05-27 14:41:24 +00:00
Implement calls to 'super()' (#445)
This commit is contained in:
parent
65c4acd2d4
commit
75328f3feb
@ -120,7 +120,8 @@ export function nodeIsCallable(kind: NodeKind): bool {
|
||||
case NodeKind.CALL:
|
||||
case NodeKind.ELEMENTACCESS:
|
||||
case NodeKind.PARENTHESIZED:
|
||||
case NodeKind.PROPERTYACCESS: return true;
|
||||
case NodeKind.PROPERTYACCESS:
|
||||
case NodeKind.SUPER: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1095,6 +1095,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (isConstructor) {
|
||||
let nativeSizeType = this.options.nativeSizeType;
|
||||
assert(instance.is(CommonFlags.INSTANCE));
|
||||
let parent = assert(instance.parent);
|
||||
assert(parent.kind == ElementKind.CLASS);
|
||||
|
||||
// implicitly return `this` if the constructor doesn't always return on its own
|
||||
if (!flow.is(FlowFlags.RETURNS)) {
|
||||
@ -1105,14 +1107,20 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// if not all branches are guaranteed to allocate, also append a conditional allocation
|
||||
} else {
|
||||
let parent = assert(instance.parent);
|
||||
assert(parent.kind == ElementKind.CLASS);
|
||||
stmts.push(module.createTeeLocal(0,
|
||||
this.makeConditionalAllocate(<Class>parent, declaration.name)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// check that super has been called if this is a derived class
|
||||
if ((<Class>parent).base && !flow.is(FlowFlags.CALLS_SUPER)) {
|
||||
this.error(
|
||||
DiagnosticCode.Constructors_for_derived_classes_must_contain_a_super_call,
|
||||
instance.prototype.declaration.range
|
||||
);
|
||||
}
|
||||
|
||||
// make sure all branches return
|
||||
} else if (returnType != Type.void && !flow.is(FlowFlags.RETURNS)) {
|
||||
this.error(
|
||||
@ -5230,6 +5238,43 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
}
|
||||
|
||||
case ElementKind.CLASS: {
|
||||
|
||||
// call to `super()`
|
||||
if (expression.expression.kind == NodeKind.SUPER) {
|
||||
if (!currentFunction.is(CommonFlags.CONSTRUCTOR)) {
|
||||
this.error(
|
||||
DiagnosticCode.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors,
|
||||
expression.range
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
|
||||
let classInstance = assert(currentFunction.parent);
|
||||
assert(classInstance.kind == ElementKind.CLASS);
|
||||
let expr = this.compileSuperInstantiate(<Class>classInstance, expression.arguments, expression);
|
||||
this.currentType = Type.void;
|
||||
|
||||
// check that super() is called before allocation is performed (incl. in super arguments)
|
||||
let flow = currentFunction.flow;
|
||||
if (flow.isAny(
|
||||
FlowFlags.ALLOCATES |
|
||||
FlowFlags.CONDITIONALLY_ALLOCATES
|
||||
)) {
|
||||
this.error(
|
||||
DiagnosticCode._super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class,
|
||||
expression.range
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
flow.set(FlowFlags.ALLOCATES | FlowFlags.CALLS_SUPER);
|
||||
|
||||
let thisLocal = assert(this.currentFunction.flow.getScopedLocal("this"));
|
||||
return module.createSetLocal(thisLocal.index, expr);
|
||||
}
|
||||
// otherwise fall-through
|
||||
}
|
||||
|
||||
// not supported
|
||||
default: {
|
||||
this.error(
|
||||
@ -6027,6 +6072,15 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return module.createUnreachable();
|
||||
}
|
||||
case NodeKind.SUPER: {
|
||||
if (currentFunction.is(CommonFlags.CONSTRUCTOR)) {
|
||||
if (!currentFunction.flow.is(FlowFlags.CALLS_SUPER)) {
|
||||
// TS1034 in the parser effectively limits this to property accesses
|
||||
this.error(
|
||||
DiagnosticCode._super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class,
|
||||
expression.range
|
||||
);
|
||||
}
|
||||
}
|
||||
let flow = currentFunction.flow;
|
||||
if (flow.is(FlowFlags.INLINE_CONTEXT)) {
|
||||
let scopedThis = flow.getScopedLocal("this");
|
||||
@ -6658,6 +6712,37 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return expr;
|
||||
}
|
||||
|
||||
compileSuperInstantiate(classInstance: Class, argumentExpressions: Expression[], reportNode: Node): ExpressionRef {
|
||||
// traverse to the top-most visible constructor (except the current one)
|
||||
var currentClassInstance: Class | null = classInstance.base;
|
||||
var constructorInstance: Function | null = null;
|
||||
while (currentClassInstance) {
|
||||
constructorInstance = currentClassInstance.constructorInstance;
|
||||
if (constructorInstance) break; // TODO: check visibility
|
||||
currentClassInstance = currentClassInstance.base;
|
||||
}
|
||||
|
||||
// if a constructor is present, allocate the necessary memory for `this` and call it
|
||||
var expr: ExpressionRef;
|
||||
if (constructorInstance) {
|
||||
expr = this.compileCallDirect(constructorInstance, argumentExpressions, reportNode,
|
||||
this.makeAllocate(classInstance, reportNode)
|
||||
);
|
||||
|
||||
// otherwise simply allocate a new instance and initialize its fields
|
||||
} else {
|
||||
if (argumentExpressions.length) {
|
||||
this.error(
|
||||
DiagnosticCode.Expected_0_arguments_but_got_1,
|
||||
reportNode.range, "0", argumentExpressions.length.toString(10)
|
||||
);
|
||||
}
|
||||
expr = this.makeAllocate(classInstance, reportNode);
|
||||
}
|
||||
this.currentType = classInstance.type;
|
||||
return expr;
|
||||
}
|
||||
|
||||
compileParenthesizedExpression(
|
||||
expression: ParenthesizedExpression,
|
||||
contextualType: Type
|
||||
|
@ -66,6 +66,7 @@ export enum DiagnosticCode {
|
||||
Unexpected_end_of_text = 1126,
|
||||
Invalid_character = 1127,
|
||||
_case_or_default_expected = 1130,
|
||||
_super_must_be_followed_by_an_argument_list_or_member_access = 1034,
|
||||
A_declare_modifier_cannot_be_used_in_an_already_ambient_context = 1038,
|
||||
Type_argument_expected = 1140,
|
||||
String_literal_expected = 1141,
|
||||
@ -94,6 +95,7 @@ export enum DiagnosticCode {
|
||||
Index_signature_is_missing_in_type_0 = 2329,
|
||||
_this_cannot_be_referenced_in_current_location = 2332,
|
||||
_super_can_only_be_referenced_in_a_derived_class = 2335,
|
||||
Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors = 2337,
|
||||
Property_0_does_not_exist_on_type_1 = 2339,
|
||||
Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures = 2349,
|
||||
Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature = 2351,
|
||||
@ -101,6 +103,8 @@ export enum DiagnosticCode {
|
||||
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
|
||||
The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access = 2364,
|
||||
Operator_0_cannot_be_applied_to_types_1_and_2 = 2365,
|
||||
A_super_call_must_be_the_first_statement_in_the_constructor = 2376,
|
||||
Constructors_for_derived_classes_must_contain_a_super_call = 2377,
|
||||
_get_and_set_accessor_must_have_the_same_type = 2380,
|
||||
Constructor_implementation_is_missing = 2390,
|
||||
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391,
|
||||
@ -124,7 +128,9 @@ export enum DiagnosticCode {
|
||||
Required_type_parameters_may_not_follow_optional_type_parameters = 2706,
|
||||
File_0_not_found = 6054,
|
||||
Numeric_separators_are_not_allowed_here = 6188,
|
||||
Multiple_consecutive_numeric_separators_are_not_permitted = 6189
|
||||
Multiple_consecutive_numeric_separators_are_not_permitted = 6189,
|
||||
_super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class = 17009,
|
||||
_super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class = 17011
|
||||
}
|
||||
|
||||
/** Translates a diagnostic code to its respective string. */
|
||||
@ -189,6 +195,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 1126: return "Unexpected end of text.";
|
||||
case 1127: return "Invalid character.";
|
||||
case 1130: return "'case' or 'default' expected.";
|
||||
case 1034: return "'super' must be followed by an argument list or member access.";
|
||||
case 1038: return "A 'declare' modifier cannot be used in an already ambient context.";
|
||||
case 1140: return "Type argument expected.";
|
||||
case 1141: return "String literal expected.";
|
||||
@ -217,6 +224,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 2329: return "Index signature is missing in type '{0}'.";
|
||||
case 2332: return "'this' cannot be referenced in current location.";
|
||||
case 2335: return "'super' can only be referenced in a derived class.";
|
||||
case 2337: return "Super calls are not permitted outside constructors or in nested functions inside constructors.";
|
||||
case 2339: return "Property '{0}' does not exist on type '{1}'.";
|
||||
case 2349: return "Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.";
|
||||
case 2351: return "Cannot use 'new' with an expression whose type lacks a construct signature.";
|
||||
@ -224,6 +232,8 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 2357: return "The operand of an increment or decrement operator must be a variable or a property access.";
|
||||
case 2364: return "The left-hand side of an assignment expression must be a variable or a property access.";
|
||||
case 2365: return "Operator '{0}' cannot be applied to types '{1}' and '{2}'.";
|
||||
case 2376: return "A 'super' call must be the first statement in the constructor.";
|
||||
case 2377: return "Constructors for derived classes must contain a 'super' call.";
|
||||
case 2380: return "'get' and 'set' accessor must have the same type.";
|
||||
case 2390: return "Constructor implementation is missing.";
|
||||
case 2391: return "Function implementation is missing or not immediately following the declaration.";
|
||||
@ -248,6 +258,8 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 6054: return "File '{0}' not found.";
|
||||
case 6188: return "Numeric separators are not allowed here.";
|
||||
case 6189: return "Multiple consecutive numeric separators are not permitted.";
|
||||
case 17009: return "'super' must be called before accessing 'this' in the constructor of a derived class.";
|
||||
case 17011: return "'super' must be called before accessing a property of 'super' in the constructor of a derived class.";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,7 @@
|
||||
"Unexpected end of text.": 1126,
|
||||
"Invalid character.": 1127,
|
||||
"'case' or 'default' expected.": 1130,
|
||||
"'super' must be followed by an argument list or member access.": 1034,
|
||||
"A 'declare' modifier cannot be used in an already ambient context.": 1038,
|
||||
"Type argument expected.": 1140,
|
||||
"String literal expected.": 1141,
|
||||
@ -88,6 +89,7 @@
|
||||
"Index signature is missing in type '{0}'.": 2329,
|
||||
"'this' cannot be referenced in current location.": 2332,
|
||||
"'super' can only be referenced in a derived class.": 2335,
|
||||
"Super calls are not permitted outside constructors or in nested functions inside constructors.": 2337,
|
||||
"Property '{0}' does not exist on type '{1}'.": 2339,
|
||||
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": 2349,
|
||||
"Cannot use 'new' with an expression whose type lacks a construct signature.": 2351,
|
||||
@ -95,6 +97,8 @@
|
||||
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
|
||||
"The left-hand side of an assignment expression must be a variable or a property access.": 2364,
|
||||
"Operator '{0}' cannot be applied to types '{1}' and '{2}'.": 2365,
|
||||
"A 'super' call must be the first statement in the constructor.": 2376,
|
||||
"Constructors for derived classes must contain a 'super' call.": 2377,
|
||||
"'get' and 'set' accessor must have the same type.": 2380,
|
||||
"Constructor implementation is missing.": 2390,
|
||||
"Function implementation is missing or not immediately following the declaration.": 2391,
|
||||
@ -119,5 +123,7 @@
|
||||
|
||||
"File '{0}' not found.": 6054,
|
||||
"Numeric separators are not allowed here.": 6188,
|
||||
"Multiple consecutive numeric separators are not permitted.": 6189
|
||||
"Multiple consecutive numeric separators are not permitted.": 6189,
|
||||
"'super' must be called before accessing 'this' in the constructor of a derived class.": 17009,
|
||||
"'super' must be called before accessing a property of 'super' in the constructor of a derived class.": 17011
|
||||
}
|
||||
|
@ -3337,6 +3337,12 @@ export class Parser extends DiagnosticEmitter {
|
||||
return Node.createConstructorExpression(tn.range(startPos, tn.pos));
|
||||
}
|
||||
case Token.SUPER: {
|
||||
if (tn.peek() != Token.DOT && tn.nextToken != Token.OPENPAREN) {
|
||||
this.error(
|
||||
DiagnosticCode._super_must_be_followed_by_an_argument_list_or_member_access,
|
||||
tn.range()
|
||||
);
|
||||
}
|
||||
return Node.createSuperExpression(tn.range(startPos, tn.pos));
|
||||
}
|
||||
case Token.STRINGLITERAL: {
|
||||
|
@ -3041,26 +3041,28 @@ export const enum FlowFlags {
|
||||
CONTINUES = 1 << 4,
|
||||
/** This branch always allocates. Constructors only. */
|
||||
ALLOCATES = 1 << 5,
|
||||
/** This branch always calls super. Constructors only. */
|
||||
CALLS_SUPER = 1 << 6,
|
||||
|
||||
// conditional
|
||||
|
||||
/** This branch conditionally returns in a child branch. */
|
||||
CONDITIONALLY_RETURNS = 1 << 6,
|
||||
CONDITIONALLY_RETURNS = 1 << 7,
|
||||
/** This branch conditionally throws in a child branch. */
|
||||
CONDITIONALLY_THROWS = 1 << 7,
|
||||
CONDITIONALLY_THROWS = 1 << 8,
|
||||
/** This branch conditionally breaks in a child branch. */
|
||||
CONDITIONALLY_BREAKS = 1 << 8,
|
||||
CONDITIONALLY_BREAKS = 1 << 9,
|
||||
/** This branch conditionally continues in a child branch. */
|
||||
CONDITIONALLY_CONTINUES = 1 << 9,
|
||||
CONDITIONALLY_CONTINUES = 1 << 10,
|
||||
/** This branch conditionally allocates in a child branch. Constructors only. */
|
||||
CONDITIONALLY_ALLOCATES = 1 << 10,
|
||||
CONDITIONALLY_ALLOCATES = 1 << 11,
|
||||
|
||||
// special
|
||||
|
||||
/** This branch is part of inlining a function. */
|
||||
INLINE_CONTEXT = 1 << 11,
|
||||
INLINE_CONTEXT = 1 << 12,
|
||||
/** This branch explicitly requests no bounds checking. */
|
||||
UNCHECKED_CONTEXT = 1 << 12,
|
||||
UNCHECKED_CONTEXT = 1 << 13,
|
||||
|
||||
// masks
|
||||
|
||||
@ -3076,7 +3078,8 @@ export const enum FlowFlags {
|
||||
| FlowFlags.THROWS
|
||||
| FlowFlags.BREAKS
|
||||
| FlowFlags.CONTINUES
|
||||
| FlowFlags.ALLOCATES,
|
||||
| FlowFlags.ALLOCATES
|
||||
| FlowFlags.CALLS_SUPER,
|
||||
|
||||
/** Any conditional flag. */
|
||||
ANY_CONDITIONAL = FlowFlags.CONDITIONALLY_RETURNS
|
||||
|
@ -1242,8 +1242,16 @@ export class Resolver extends DiagnosticEmitter {
|
||||
|
||||
// Lay out fields in advance
|
||||
case ElementKind.FIELD_PROTOTYPE: {
|
||||
if (!instance.members) instance.members = new Map();
|
||||
let fieldDeclaration = (<FieldPrototype>member).declaration;
|
||||
if (!instance.members) instance.members = new Map();
|
||||
else if (instance.members.has(member.simpleName)) {
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_identifier_0,
|
||||
fieldDeclaration.name.range,
|
||||
member.simpleName
|
||||
);
|
||||
break;
|
||||
}
|
||||
let fieldType: Type | null = null;
|
||||
// TODO: handle duplicate non-private fields
|
||||
if (!fieldDeclaration.type) {
|
||||
|
276
tests/compiler/call-super.optimized.wat
Normal file
276
tests/compiler/call-super.optimized.wat
Normal file
@ -0,0 +1,276 @@
|
||||
(module
|
||||
(type $v (func))
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $iiiiv (func (param i32 i32 i32 i32)))
|
||||
(type $FUNCSIG$i (func (result i32)))
|
||||
(import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32)))
|
||||
(memory $0 1)
|
||||
(data (i32.const 8) "\0d\00\00\00c\00a\00l\00l\00-\00s\00u\00p\00e\00r\00.\00t\00s")
|
||||
(table $0 1 anyfunc)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0))
|
||||
(global $~lib/allocator/arena/offset (mut i32) (i32.const 0))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(start $start)
|
||||
(func $~lib/allocator/arena/__memory_allocate (; 1 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
get_local $0
|
||||
i32.const 1073741824
|
||||
i32.gt_u
|
||||
if
|
||||
unreachable
|
||||
end
|
||||
get_global $~lib/allocator/arena/offset
|
||||
tee_local $1
|
||||
get_local $0
|
||||
i32.const 1
|
||||
get_local $0
|
||||
i32.const 1
|
||||
i32.gt_u
|
||||
select
|
||||
i32.add
|
||||
i32.const 7
|
||||
i32.add
|
||||
i32.const -8
|
||||
i32.and
|
||||
tee_local $2
|
||||
current_memory
|
||||
tee_local $3
|
||||
i32.const 16
|
||||
i32.shl
|
||||
i32.gt_u
|
||||
if
|
||||
get_local $3
|
||||
get_local $2
|
||||
get_local $1
|
||||
i32.sub
|
||||
i32.const 65535
|
||||
i32.add
|
||||
i32.const -65536
|
||||
i32.and
|
||||
i32.const 16
|
||||
i32.shr_u
|
||||
tee_local $0
|
||||
get_local $3
|
||||
get_local $0
|
||||
i32.gt_s
|
||||
select
|
||||
grow_memory
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
get_local $0
|
||||
grow_memory
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
unreachable
|
||||
end
|
||||
end
|
||||
end
|
||||
get_local $2
|
||||
set_global $~lib/allocator/arena/offset
|
||||
get_local $1
|
||||
)
|
||||
(func $call-super/A#constructor (; 2 ;) (type $ii) (param $0 i32) (result i32)
|
||||
get_local $0
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 4
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
tee_local $0
|
||||
i32.const 1
|
||||
i32.store
|
||||
end
|
||||
get_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 6
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
)
|
||||
(func $call-super/B#constructor (; 3 ;) (type $FUNCSIG$i) (result i32)
|
||||
(local $0 i32)
|
||||
i32.const 8
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
tee_local $0
|
||||
i32.const 1
|
||||
i32.store
|
||||
get_local $0
|
||||
i32.const 2
|
||||
i32.store offset=4
|
||||
get_local $0
|
||||
call $call-super/A#constructor
|
||||
tee_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 15
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
i32.load offset=4
|
||||
i32.const 2
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 16
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
)
|
||||
(func $call-super/test1 (; 4 ;) (type $v)
|
||||
(local $0 i32)
|
||||
call $call-super/B#constructor
|
||||
tee_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 22
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
i32.load offset=4
|
||||
i32.const 2
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 23
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $call-super/D#constructor (; 5 ;) (type $FUNCSIG$i) (result i32)
|
||||
(local $0 i32)
|
||||
i32.const 8
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
tee_local $0
|
||||
i32.const 1
|
||||
i32.store
|
||||
get_local $0
|
||||
i32.const 2
|
||||
i32.store offset=4
|
||||
get_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 36
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
i32.load offset=4
|
||||
i32.const 2
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 37
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
)
|
||||
(func $call-super/test2 (; 6 ;) (type $v)
|
||||
(local $0 i32)
|
||||
call $call-super/D#constructor
|
||||
tee_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 43
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
i32.load offset=4
|
||||
i32.const 2
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 44
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $call-super/E#constructor (; 7 ;) (type $FUNCSIG$i) (result i32)
|
||||
(local $0 i32)
|
||||
i32.const 4
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
tee_local $0
|
||||
i32.const 1
|
||||
i32.store
|
||||
get_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 52
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
)
|
||||
(func $start (; 8 ;) (type $v)
|
||||
i32.const 40
|
||||
set_global $~lib/allocator/arena/startOffset
|
||||
get_global $~lib/allocator/arena/startOffset
|
||||
set_global $~lib/allocator/arena/offset
|
||||
call $call-super/test1
|
||||
call $call-super/test2
|
||||
call $call-super/E#constructor
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.ne
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 62
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $null (; 9 ;) (type $v)
|
||||
nop
|
||||
)
|
||||
)
|
66
tests/compiler/call-super.ts
Normal file
66
tests/compiler/call-super.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import "allocator/arena";
|
||||
|
||||
class A {
|
||||
a: i32 = 1;
|
||||
constructor() {
|
||||
assert(this.a == 1);
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
// a: i32 = 3; // FIXME: currently duplicate identifier
|
||||
b: i32 = 2;
|
||||
constructor() {
|
||||
super();
|
||||
assert(this.a == 1);
|
||||
assert(this.b == 2);
|
||||
}
|
||||
}
|
||||
|
||||
function test1(): void {
|
||||
var b = new B();
|
||||
assert(b.a == 1);
|
||||
assert(b.b == 2);
|
||||
}
|
||||
|
||||
test1();
|
||||
|
||||
class C {
|
||||
a: i32 = 1;
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
b: i32 = 2;
|
||||
constructor() {
|
||||
super();
|
||||
assert(this.a == 1);
|
||||
assert(this.b == 2);
|
||||
}
|
||||
}
|
||||
|
||||
function test2(): void {
|
||||
var d = new D();
|
||||
assert(d.a == 1);
|
||||
assert(d.b == 2);
|
||||
}
|
||||
|
||||
test2();
|
||||
|
||||
class E {
|
||||
a: i32 = 1;
|
||||
constructor() {
|
||||
assert(this.a == 1);
|
||||
}
|
||||
}
|
||||
|
||||
class F extends E {
|
||||
b: i32 = 2;
|
||||
}
|
||||
|
||||
function test3(): void {
|
||||
var f = new F();
|
||||
assert(f.a == 1);
|
||||
// assert(f.b == 2); // FIXME: uses E#constructor, not initializing fields
|
||||
}
|
||||
|
||||
test3();
|
355
tests/compiler/call-super.untouched.wat
Normal file
355
tests/compiler/call-super.untouched.wat
Normal file
@ -0,0 +1,355 @@
|
||||
(module
|
||||
(type $v (func))
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $iiiiv (func (param i32 i32 i32 i32)))
|
||||
(import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32)))
|
||||
(memory $0 1)
|
||||
(data (i32.const 8) "\0d\00\00\00c\00a\00l\00l\00-\00s\00u\00p\00e\00r\00.\00t\00s\00")
|
||||
(table $0 1 anyfunc)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $~lib/internal/allocator/AL_BITS i32 (i32.const 3))
|
||||
(global $~lib/internal/allocator/AL_SIZE i32 (i32.const 8))
|
||||
(global $~lib/internal/allocator/AL_MASK i32 (i32.const 7))
|
||||
(global $~lib/internal/allocator/MAX_SIZE_32 i32 (i32.const 1073741824))
|
||||
(global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0))
|
||||
(global $~lib/allocator/arena/offset (mut i32) (i32.const 0))
|
||||
(global $HEAP_BASE i32 (i32.const 40))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(start $start)
|
||||
(func $~lib/allocator/arena/__memory_allocate (; 1 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
(local $6 i32)
|
||||
get_local $0
|
||||
get_global $~lib/internal/allocator/MAX_SIZE_32
|
||||
i32.gt_u
|
||||
if
|
||||
unreachable
|
||||
end
|
||||
get_global $~lib/allocator/arena/offset
|
||||
set_local $1
|
||||
get_local $1
|
||||
get_local $0
|
||||
tee_local $2
|
||||
i32.const 1
|
||||
tee_local $3
|
||||
get_local $2
|
||||
get_local $3
|
||||
i32.gt_u
|
||||
select
|
||||
i32.add
|
||||
get_global $~lib/internal/allocator/AL_MASK
|
||||
i32.add
|
||||
get_global $~lib/internal/allocator/AL_MASK
|
||||
i32.const -1
|
||||
i32.xor
|
||||
i32.and
|
||||
set_local $4
|
||||
current_memory
|
||||
set_local $5
|
||||
get_local $4
|
||||
get_local $5
|
||||
i32.const 16
|
||||
i32.shl
|
||||
i32.gt_u
|
||||
if
|
||||
get_local $4
|
||||
get_local $1
|
||||
i32.sub
|
||||
i32.const 65535
|
||||
i32.add
|
||||
i32.const 65535
|
||||
i32.const -1
|
||||
i32.xor
|
||||
i32.and
|
||||
i32.const 16
|
||||
i32.shr_u
|
||||
set_local $2
|
||||
get_local $5
|
||||
tee_local $3
|
||||
get_local $2
|
||||
tee_local $6
|
||||
get_local $3
|
||||
get_local $6
|
||||
i32.gt_s
|
||||
select
|
||||
set_local $3
|
||||
get_local $3
|
||||
grow_memory
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
get_local $2
|
||||
grow_memory
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
unreachable
|
||||
end
|
||||
end
|
||||
end
|
||||
get_local $4
|
||||
set_global $~lib/allocator/arena/offset
|
||||
get_local $1
|
||||
)
|
||||
(func $~lib/memory/memory.allocate (; 2 ;) (type $ii) (param $0 i32) (result i32)
|
||||
get_local $0
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
return
|
||||
)
|
||||
(func $call-super/A#constructor (; 3 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
get_local $0
|
||||
if (result i32)
|
||||
get_local $0
|
||||
else
|
||||
block (result i32)
|
||||
i32.const 4
|
||||
call $~lib/memory/memory.allocate
|
||||
set_local $1
|
||||
get_local $1
|
||||
i32.const 1
|
||||
i32.store
|
||||
get_local $1
|
||||
end
|
||||
tee_local $0
|
||||
end
|
||||
tee_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 6
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
)
|
||||
(func $call-super/B#constructor (; 4 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
block (result i32)
|
||||
i32.const 8
|
||||
call $~lib/memory/memory.allocate
|
||||
set_local $1
|
||||
get_local $1
|
||||
i32.const 1
|
||||
i32.store
|
||||
get_local $1
|
||||
i32.const 2
|
||||
i32.store offset=4
|
||||
get_local $1
|
||||
end
|
||||
call $call-super/A#constructor
|
||||
set_local $0
|
||||
get_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 15
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
i32.load offset=4
|
||||
i32.const 2
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 16
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
)
|
||||
(func $call-super/test1 (; 5 ;) (type $v)
|
||||
(local $0 i32)
|
||||
i32.const 0
|
||||
call $call-super/B#constructor
|
||||
set_local $0
|
||||
get_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 22
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
i32.load offset=4
|
||||
i32.const 2
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 23
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $call-super/D#constructor (; 6 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
block (result i32)
|
||||
i32.const 8
|
||||
call $~lib/memory/memory.allocate
|
||||
set_local $1
|
||||
get_local $1
|
||||
i32.const 1
|
||||
i32.store
|
||||
get_local $1
|
||||
i32.const 2
|
||||
i32.store offset=4
|
||||
get_local $1
|
||||
end
|
||||
set_local $0
|
||||
get_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 36
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
i32.load offset=4
|
||||
i32.const 2
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 37
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
)
|
||||
(func $call-super/test2 (; 7 ;) (type $v)
|
||||
(local $0 i32)
|
||||
i32.const 0
|
||||
call $call-super/D#constructor
|
||||
set_local $0
|
||||
get_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 43
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
i32.load offset=4
|
||||
i32.const 2
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 44
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $call-super/E#constructor (; 8 ;) (type $ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
get_local $0
|
||||
if (result i32)
|
||||
get_local $0
|
||||
else
|
||||
block (result i32)
|
||||
i32.const 4
|
||||
call $~lib/memory/memory.allocate
|
||||
set_local $1
|
||||
get_local $1
|
||||
i32.const 1
|
||||
i32.store
|
||||
get_local $1
|
||||
end
|
||||
tee_local $0
|
||||
end
|
||||
tee_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 52
|
||||
i32.const 4
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
get_local $0
|
||||
)
|
||||
(func $call-super/test3 (; 9 ;) (type $v)
|
||||
(local $0 i32)
|
||||
i32.const 0
|
||||
call $call-super/E#constructor
|
||||
set_local $0
|
||||
get_local $0
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 8
|
||||
i32.const 62
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $start (; 10 ;) (type $v)
|
||||
get_global $HEAP_BASE
|
||||
get_global $~lib/internal/allocator/AL_MASK
|
||||
i32.add
|
||||
get_global $~lib/internal/allocator/AL_MASK
|
||||
i32.const -1
|
||||
i32.xor
|
||||
i32.and
|
||||
set_global $~lib/allocator/arena/startOffset
|
||||
get_global $~lib/allocator/arena/startOffset
|
||||
set_global $~lib/allocator/arena/offset
|
||||
call $call-super/test1
|
||||
call $call-super/test2
|
||||
call $call-super/test3
|
||||
)
|
||||
(func $null (; 11 ;) (type $v)
|
||||
)
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user