From 75328f3feb7145305c077f93096808bf96a266c8 Mon Sep 17 00:00:00 2001 From: Daniel Wirtz Date: Thu, 31 Jan 2019 10:35:49 +0100 Subject: [PATCH] Implement calls to 'super()' (#445) --- src/ast.ts | 3 +- src/compiler.ts | 89 +++++- src/diagnosticMessages.generated.ts | 14 +- src/diagnosticMessages.json | 8 +- src/parser.ts | 6 + src/program.ts | 19 +- src/resolver.ts | 10 +- tests/compiler/call-super.optimized.wat | 276 ++++++++++++++++++ tests/compiler/call-super.ts | 66 +++++ tests/compiler/call-super.untouched.wat | 355 ++++++++++++++++++++++++ 10 files changed, 832 insertions(+), 14 deletions(-) create mode 100644 tests/compiler/call-super.optimized.wat create mode 100644 tests/compiler/call-super.ts create mode 100644 tests/compiler/call-super.untouched.wat diff --git a/src/ast.ts b/src/ast.ts index 1b2a839a..a3290e11 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -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; } diff --git a/src/compiler.ts b/src/compiler.ts index c8d6bbd2..0723c31a 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -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(parent, declaration.name) )); } } + // check that super has been called if this is a derived class + if ((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(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 diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index 85c5b678..831dc9a4 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -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 ""; } } diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 3594958b..c1830de6 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -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 } diff --git a/src/parser.ts b/src/parser.ts index e19cf38a..75b24ded 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -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: { diff --git a/src/program.ts b/src/program.ts index c916acf2..f0219008 100644 --- a/src/program.ts +++ b/src/program.ts @@ -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 diff --git a/src/resolver.ts b/src/resolver.ts index 33f5f883..e24e54fd 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -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 = (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) { diff --git a/tests/compiler/call-super.optimized.wat b/tests/compiler/call-super.optimized.wat new file mode 100644 index 00000000..2b3f00cd --- /dev/null +++ b/tests/compiler/call-super.optimized.wat @@ -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 + ) +) diff --git a/tests/compiler/call-super.ts b/tests/compiler/call-super.ts new file mode 100644 index 00000000..939226ea --- /dev/null +++ b/tests/compiler/call-super.ts @@ -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(); diff --git a/tests/compiler/call-super.untouched.wat b/tests/compiler/call-super.untouched.wat new file mode 100644 index 00000000..b7e3eb9c --- /dev/null +++ b/tests/compiler/call-super.untouched.wat @@ -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) + ) +)