Simplify resolve infrastructure; Fix handling of nested element and property accesses

This commit is contained in:
dcodeIO 2018-04-05 02:23:03 +02:00
parent e790eb757f
commit 7e90ab161d
12 changed files with 1486 additions and 339 deletions

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

296
src/binary.ts Normal file
View File

@ -0,0 +1,296 @@
// TBD: Moving lib/parse here might make sense where the compiler has to evaluate WASM binaries on
// the fly, like when importing other WASM files through `import`. Module integration hasn't been
// specified, yet, though, and it somewhat conflicts with our dependency on Binaryen.
/** WebAssembly section ids. */
export enum SectionId {
/** A custom section with an explicit name. */
Custom = 0,
/** Function types section. */
Type = 1,
/** Imports section. */
Import = 2,
/** Functions section. */
Function = 3,
/** Tables section. */
Table = 4,
/** Memories section. */
Memory = 5,
/** Globals section. */
Global = 6,
/** Exports section. */
Export = 7,
/** Start function section. */
Start = 8,
/** Table element section. */
Element = 9,
/** Function code section. */
Code = 10,
/** Memory segments section. */
Data = 11
}
/** WebAssembly external kinds. */
export enum ExternalKind {
/** External function. */
Function = 0,
/** External table. */
Table = 1,
/** External memory. */
Memory = 2,
/** External global. */
Global = 3
}
/** WebAssembly name section kinds. */
export enum NameKind {
/** Module name. */
Module = 0,
/** Function name. */
Function = 1,
/** Local name. */
Local = 2,
// see: https://github.com/WebAssembly/design/pull/1064
/** Label name. */
Label = 3,
/** Function type name. */
Type = 4,
/** Table name. */
Table = 5,
/** Memory name. */
Memory = 6,
/** Global variable name. */
Global = 7
}
/** WebAssembly types. */
export enum Type {
i32 = 0x7f,
i64 = 0x7e,
f32 = 0x7d,
f64 = 0x7c,
anyfunc = 0x70,
func = 0x60,
none = 0x40
}
/** WebAssembly opcodes. */
export enum Op {
unreachable = 0x00,
nop = 0x01,
block = 0x02,
loop = 0x03,
if_ = 0x04,
else_ = 0x05,
end = 0x0b,
br = 0x0c,
br_if = 0x0d,
br_table = 0x0e,
return_ = 0x0f,
call = 0x10,
call_indirect = 0x11,
drop = 0x1a,
select = 0x1b,
get_local = 0x20,
set_local = 0x21,
tee_local = 0x22,
get_global = 0x23,
set_global = 0x24,
i32_load = 0x28,
i64_load = 0x29,
f32_load = 0x2a,
f64_load = 0x2b,
i32_load8_s = 0x2c,
i32_load8_u = 0x2d,
i32_load16_s = 0x2e,
i32_load16_u = 0x2f,
i64_load8_s = 0x30,
i64_load8_u = 0x31,
i64_load16_s = 0x32,
i64_load16_u = 0x33,
i64_load32_s = 0x34,
i64_load32_u = 0x35,
i32_store = 0x36,
i64_store = 0x37,
f32_store = 0x38,
f64_store = 0x39,
i32_store8 = 0x3a,
i32_store16 = 0x3b,
i64_store8 = 0x3c,
i64_store16 = 0x3d,
i64_store32 = 0x3e,
current_memory = 0x3f,
grow_memory = 0x40,
i32_const = 0x41,
i64_const = 0x42,
f32_const = 0x43,
f64_const = 0x44,
i32_eqz = 0x45,
i32_eq = 0x46,
i32_ne = 0x47,
i32_lt_s = 0x48,
i32_lt_u = 0x49,
i32_gt_s = 0x4a,
i32_gt_u = 0x4b,
i32_le_s = 0x4c,
i32_le_u = 0x4d,
i32_ge_s = 0x4e,
i32_ge_u = 0x4f,
i64_eqz = 0x50,
i64_eq = 0x51,
i64_ne = 0x52,
i64_lt_s = 0x53,
i64_lt_u = 0x54,
i64_gt_s = 0x55,
i64_gt_u = 0x56,
i64_le_s = 0x57,
i64_le_u = 0x58,
i64_ge_s = 0x59,
i64_ge_u = 0x5a,
f32_eq = 0x5b,
f32_ne = 0x5c,
f32_lt = 0x5d,
f32_gt = 0x5e,
f32_le = 0x5f,
f32_ge = 0x60,
f64_eq = 0x61,
f64_ne = 0x62,
f64_lt = 0x63,
f64_gt = 0x64,
f64_le = 0x65,
f64_ge = 0x66,
i32_clz = 0x67,
i32_ctz = 0x68,
i32_popcnt = 0x69,
i32_add = 0x6a,
i32_sub = 0x6b,
i32_mul = 0x6c,
i32_div_s = 0x6d,
i32_div_u = 0x6e,
i32_rem_s = 0x6f,
i32_rem_u = 0x70,
i32_and = 0x71,
i32_or = 0x72,
i32_xor = 0x73,
i32_shl = 0x74,
i32_shr_s = 0x75,
i32_shr_u = 0x76,
i32_rotl = 0x77,
i32_rotr = 0x78,
i64_clz = 0x79,
i64_ctz = 0x7a,
i64_popcnt = 0x7b,
i64_add = 0x7c,
i64_sub = 0x7d,
i64_mul = 0x7e,
i64_div_s = 0x7f,
i64_div_u = 0x80,
i64_rem_s = 0x81,
i64_rem_u = 0x82,
i64_and = 0x83,
i64_or = 0x84,
i64_xor = 0x85,
i64_shl = 0x86,
i64_shr_s = 0x87,
i64_shr_u = 0x88,
i64_rotl = 0x89,
i64_rotr = 0x8a,
f32_abs = 0x8b,
f32_neg = 0x8c,
f32_ceil = 0x8d,
f32_floor = 0x8e,
f32_trunc = 0x8f,
f32_nearest = 0x90,
f32_sqrt = 0x91,
f32_add = 0x92,
f32_sub = 0x93,
f32_mul = 0x94,
f32_div = 0x95,
f32_min = 0x96,
f32_max = 0x97,
f32_copysign = 0x98,
f64_abs = 0x99,
f64_neg = 0x9a,
f64_ceil = 0x9b,
f64_floor = 0x9c,
f64_trunc = 0x9d,
f64_nearest = 0x9e,
f64_sqrt = 0x9f,
f64_add = 0xa0,
f64_sub = 0xa1,
f64_mul = 0xa2,
f64_div = 0xa3,
f64_min = 0xa4,
f64_max = 0xa5,
f64_copysign = 0xa6,
i32_wrap_i64 = 0xa7,
i32_trunc_s_f32 = 0xa8,
i32_trunc_u_f32 = 0xa9,
i32_trunc_s_f64 = 0xaa,
i32_trunc_u_f64 = 0xab,
i64_extend_s_i32 = 0xac,
i64_extend_u_i32 = 0xad,
i64_trunc_s_f32 = 0xae,
i64_trunc_u_f32 = 0xaf,
i64_trunc_s_f64 = 0xb0,
i64_trunc_u_f64 = 0xb1,
f32_convert_s_i32 = 0xb2,
f32_convert_u_i32 = 0xb3,
f32_convert_s_i64 = 0xb4,
f32_convert_u_i64 = 0xb5,
f32_demote_f64 = 0xb6,
f64_convert_s_i32 = 0xb7,
f64_convert_u_i32 = 0xb8,
f64_convert_s_i64 = 0xb9,
f64_convert_u_i64 = 0xba,
f64_promote_f32 = 0xbb,
i32_reinterpret_f32 = 0xbc,
i64_reinterpret_f64 = 0xbd,
f32_reinterpret_i32 = 0xbe,
f64_reinterpret_i64 = 0xbf
}
enum ReaderState {
HEADER
}
/** WebAssembly binary reader. */
export class Reader { // TODO
/** Buffer being read. */
private buffer: Uint8Array;
/** Current read offset. */
private offset: u32;
/** Total length. */
private length: u32;
/** Current state. */
private state: ReaderState;
/** Constructs a new binary reader. */
constructor(totalLength: u32, initialChunk: Uint8Array) {
this.buffer = initialChunk;
this.offset = 0;
this.length = totalLength;
this.state = ReaderState.HEADER;
}
/** Provides a chunk of data. */
next(chunk: Uint8Array): void {
if (!chunk.length) return;
// var current = this.buffer;
// var offset = this.offset;
// var buffer = new Uint8Array((current.length - offset) + chunk.length);
// buffer.set(current.subarray(offset), 0);
// buffer.set(chunk, (current.length - offset));
// this.buffer = buffer;
// this.length -= this.offset;
// this.offset = 0;
unreachable();
}
finish(): void {
unreachable();
}
}

View File

@ -31,7 +31,6 @@ import {
Program, Program,
ClassPrototype, ClassPrototype,
Class, Class,
Element,
ElementKind, ElementKind,
Enum, Enum,
Field, Field,
@ -4045,59 +4044,56 @@ export class Compiler extends DiagnosticEmitter {
} }
compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): ExpressionRef { compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): ExpressionRef {
var program = this.program;
var currentFunction = this.currentFunction; var currentFunction = this.currentFunction;
var resolved = this.program.resolveExpression(expression, currentFunction); // reports var target = program.resolveExpression(expression, currentFunction); // reports
if (!resolved) return this.module.createUnreachable(); if (!target) return this.module.createUnreachable();
// to compile just the value, we need to know the target's type // to compile just the value, we need to know the target's type
var element = resolved.element;
var elementType: Type; var elementType: Type;
switch (element.kind) { switch (target.kind) {
case ElementKind.GLOBAL: { case ElementKind.GLOBAL: {
if (!this.compileGlobal(<Global>element)) { // reports; not yet compiled if a static field compiled as a global if (!this.compileGlobal(<Global>target)) { // reports; not yet compiled if a static field compiled as a global
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
assert((<Global>element).type != Type.void, "concrete type expected"); assert((<Global>target).type != Type.void); // compileGlobal must guarantee this
// fall-through // fall-through
} }
case ElementKind.LOCAL: case ElementKind.LOCAL:
case ElementKind.FIELD: { case ElementKind.FIELD: {
elementType = (<VariableLikeElement>element).type; elementType = (<VariableLikeElement>target).type;
break; break;
} }
case ElementKind.PROPERTY: { case ElementKind.PROPERTY: {
let prototype = (<Property>element).setterPrototype; let prototype = (<Property>target).setterPrototype;
if (prototype) { if (prototype) {
let instance = prototype.resolve(); // reports let instance = prototype.resolve(); // reports
if (!instance) return this.module.createUnreachable(); if (!instance) return this.module.createUnreachable();
assert(instance.signature.parameterTypes.length == 1); assert(instance.signature.parameterTypes.length == 1); // parser must guarantee this
elementType = instance.signature.parameterTypes[0]; elementType = instance.signature.parameterTypes[0];
break; break;
} }
this.error( this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
expression.range, (<Property>element).internalName expression.range, (<Property>target).internalName
); );
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
case ElementKind.FUNCTION_PROTOTYPE: { case ElementKind.CLASS: {
if (expression.kind == NodeKind.ELEMENTACCESS) { // @operator("[]") if (program.resolvedElementExpression) { // indexed access
if (resolved.target && resolved.target.kind == ElementKind.CLASS) { let indexedGetPrototype = (<Class>target).getIndexedGet();
if (element.simpleName == (<Class>resolved.target).prototype.fnIndexedGet) { if (indexedGetPrototype) {
let resolvedIndexedSet = (<FunctionPrototype>element).resolve(null); // reports let indexedGetInstance = indexedGetPrototype.resolve(); // reports
if (resolvedIndexedSet) { if (!indexedGetInstance) return this.module.createUnreachable();
elementType = resolvedIndexedSet.signature.returnType; elementType = indexedGetInstance.signature.returnType;
break; break;
} }
} else {
this.error( this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0, DiagnosticCode.Index_signature_is_missing_in_type_0,
expression.range, (<Class>resolved.target).toString() expression.range, (<Class>target).toString()
); );
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
}
}
// fall-through // fall-through
} }
default: { default: {
@ -4124,62 +4120,61 @@ export class Compiler extends DiagnosticEmitter {
tee: bool = false tee: bool = false
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
var resolved = this.program.resolveExpression(expression, this.currentFunction); // reports var target = this.program.resolveExpression(expression, this.currentFunction); // reports
if (!resolved) return module.createUnreachable(); if (!target) return module.createUnreachable();
var element = resolved.element; switch (target.kind) {
switch (element.kind) {
case ElementKind.LOCAL: { case ElementKind.LOCAL: {
this.currentType = tee ? (<Local>element).type : Type.void; this.currentType = tee ? (<Local>target).type : Type.void;
if ((<Local>element).is(CommonFlags.CONST)) { if ((<Local>target).is(CommonFlags.CONST)) {
this.error( this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
expression.range, (<Local>element).internalName expression.range, target.internalName
); );
return module.createUnreachable(); return module.createUnreachable();
} }
return tee return tee
? module.createTeeLocal((<Local>element).index, valueWithCorrectType) ? module.createTeeLocal((<Local>target).index, valueWithCorrectType)
: module.createSetLocal((<Local>element).index, valueWithCorrectType); : module.createSetLocal((<Local>target).index, valueWithCorrectType);
} }
case ElementKind.GLOBAL: { case ElementKind.GLOBAL: {
if (!this.compileGlobal(<Global>element)) return module.createUnreachable(); if (!this.compileGlobal(<Global>target)) return module.createUnreachable();
let type = (<Global>element).type; let type = (<Global>target).type;
assert(type != Type.void); assert(type != Type.void);
this.currentType = tee ? type : Type.void; this.currentType = tee ? type : Type.void;
if ((<Local>element).is(CommonFlags.CONST)) { if ((<Local>target).is(CommonFlags.CONST)) {
this.error( this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
expression.range, expression.range,
(<Local>element).internalName target.internalName
); );
return module.createUnreachable(); return module.createUnreachable();
} }
if (tee) { if (tee) {
let nativeType = type.toNativeType(); let nativeType = type.toNativeType();
let internalName = (<Global>element).internalName; let internalName = target.internalName;
return module.createBlock(null, [ // emulated teeGlobal return module.createBlock(null, [ // emulated teeGlobal
module.createSetGlobal(internalName, valueWithCorrectType), module.createSetGlobal(internalName, valueWithCorrectType),
module.createGetGlobal(internalName, nativeType) module.createGetGlobal(internalName, nativeType)
], nativeType); ], nativeType);
} else { } else {
return module.createSetGlobal((<Global>element).internalName, valueWithCorrectType); return module.createSetGlobal(target.internalName, valueWithCorrectType);
} }
} }
case ElementKind.FIELD: { case ElementKind.FIELD: {
if ((<Field>element).is(CommonFlags.READONLY)) { if ((<Field>target).is(CommonFlags.READONLY)) {
this.error( this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
expression.range, (<Field>element).internalName expression.range, (<Field>target).internalName
); );
return module.createUnreachable(); return module.createUnreachable();
} }
assert(resolved.isInstanceTarget); let thisExpression = assert(this.program.resolvedThisExpression);
let targetExpr = this.compileExpression( let thisExpr = this.compileExpressionRetainType(
<Expression>resolved.targetExpression, thisExpression,
(<Class>resolved.target).type this.options.usizeType
); );
let type = (<Field>element).type; let type = (<Field>target).type;
this.currentType = tee ? type : Type.void; this.currentType = tee ? type : Type.void;
let nativeType = type.toNativeType(); let nativeType = type.toNativeType();
if (tee) { if (tee) {
@ -4190,25 +4185,25 @@ export class Compiler extends DiagnosticEmitter {
module.createSetLocal(tempLocalIndex, valueWithCorrectType), module.createSetLocal(tempLocalIndex, valueWithCorrectType),
module.createStore( module.createStore(
type.size >> 3, type.size >> 3,
targetExpr, thisExpr,
module.createGetLocal(tempLocalIndex, nativeType), module.createGetLocal(tempLocalIndex, nativeType),
nativeType, nativeType,
(<Field>element).memoryOffset (<Field>target).memoryOffset
), ),
module.createGetLocal(tempLocalIndex, nativeType) module.createGetLocal(tempLocalIndex, nativeType)
], nativeType); ], nativeType);
} else { } else {
return module.createStore( return module.createStore(
type.size >> 3, type.size >> 3,
targetExpr, thisExpr,
valueWithCorrectType, valueWithCorrectType,
nativeType, nativeType,
(<Field>element).memoryOffset (<Field>target).memoryOffset
); );
} }
} }
case ElementKind.PROPERTY: { case ElementKind.PROPERTY: {
let setterPrototype = (<Property>element).setterPrototype; let setterPrototype = (<Property>target).setterPrototype;
if (setterPrototype) { if (setterPrototype) {
let setterInstance = setterPrototype.resolve(); // reports let setterInstance = setterPrototype.resolve(); // reports
if (!setterInstance) return module.createUnreachable(); if (!setterInstance) return module.createUnreachable();
@ -4216,35 +4211,35 @@ export class Compiler extends DiagnosticEmitter {
// call just the setter if the return value isn't of interest // call just the setter if the return value isn't of interest
if (!tee) { if (!tee) {
if (setterInstance.is(CommonFlags.INSTANCE)) { if (setterInstance.is(CommonFlags.INSTANCE)) {
assert(resolved.isInstanceTarget); let thisExpression = assert(this.program.resolvedThisExpression);
let thisArg = this.compileExpression( let thisExpr = this.compileExpressionRetainType(
<Expression>resolved.targetExpression, thisExpression,
(<Class>resolved.target).type this.options.usizeType
); );
return this.makeCallDirect(setterInstance, [ thisArg, valueWithCorrectType ]); return this.makeCallDirect(setterInstance, [ thisExpr, valueWithCorrectType ]);
} else { } else {
return this.makeCallDirect(setterInstance, [ valueWithCorrectType ]); return this.makeCallDirect(setterInstance, [ valueWithCorrectType ]);
} }
} }
// otherwise call the setter first, then the getter // otherwise call the setter first, then the getter
let getterPrototype = (<Property>element).getterPrototype; let getterPrototype = (<Property>target).getterPrototype;
assert(getterPrototype != null); // must have one if there is a setter assert(getterPrototype != null); // must have one if there is a setter
let getterInstance = (<FunctionPrototype>getterPrototype).resolve(); // reports let getterInstance = (<FunctionPrototype>getterPrototype).resolve(); // reports
if (!getterInstance) return module.createUnreachable(); if (!getterInstance) return module.createUnreachable();
let returnType = getterInstance.signature.returnType; let returnType = getterInstance.signature.returnType;
let nativeReturnType = returnType.toNativeType(); let nativeReturnType = returnType.toNativeType();
if (setterInstance.is(CommonFlags.INSTANCE)) { if (setterInstance.is(CommonFlags.INSTANCE)) {
assert(resolved.isInstanceTarget); let thisExpression = assert(this.program.resolvedThisExpression);
let thisArg = this.compileExpression( let thisExpr = this.compileExpressionRetainType(
<Expression>resolved.targetExpression, thisExpression,
(<Class>resolved.target).type this.options.usizeType
); );
let tempLocal = this.currentFunction.getAndFreeTempLocal(returnType); let tempLocal = this.currentFunction.getAndFreeTempLocal(returnType);
let tempLocalIndex = tempLocal.index; let tempLocalIndex = tempLocal.index;
return module.createBlock(null, [ return module.createBlock(null, [
this.makeCallDirect(setterInstance, [ // set and remember the target this.makeCallDirect(setterInstance, [ // set and remember the target
module.createTeeLocal(tempLocalIndex, thisArg), module.createTeeLocal(tempLocalIndex, thisExpr),
valueWithCorrectType valueWithCorrectType
]), ]),
this.makeCallDirect(getterInstance, [ // get from remembered target this.makeCallDirect(getterInstance, [ // get from remembered target
@ -4261,63 +4256,66 @@ export class Compiler extends DiagnosticEmitter {
} else { } else {
this.error( this.error(
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
expression.range, (<Property>element).internalName expression.range, target.internalName
); );
} }
return module.createUnreachable(); return module.createUnreachable();
} }
case ElementKind.FUNCTION_PROTOTYPE: { // @operator("[]") ? case ElementKind.CLASS: {
if (expression.kind == NodeKind.ELEMENTACCESS) { let elementExpression = this.program.resolvedElementExpression;
assert(resolved.isInstanceTarget); if (elementExpression) {
let getterInstance = (<FunctionPrototype>element).resolve(); // reports let indexedGetPrototype = (<Class>target).getIndexedGet();
if (!getterInstance) return module.createUnreachable(); if (indexedGetPrototype) {
// obtain @operator("[]=") let indexedGetInstance = indexedGetPrototype.resolve(); // reports
let setElementName = (<Class>resolved.target).prototype.fnIndexedSet; if (!indexedGetInstance) return module.createUnreachable();
let setElement: Element | null; let indexedSetPrototype = (<Class>target).getIndexedSet();
if ( if (!indexedSetPrototype) {
setElementName != null && this.error(
(<Class>resolved.target).members && DiagnosticCode.Index_signature_in_type_0_only_permits_reading,
(setElement = (<Map<string,Element>>(<Class>resolved.target).members).get(setElementName)) && expression.range, target.internalName
setElement.kind == ElementKind.FUNCTION_PROTOTYPE );
) { this.currentType = tee ? indexedGetInstance.signature.returnType : Type.void;
let setterInstance = (<FunctionPrototype>setElement).resolve(); // reports return module.createUnreachable();
if (!setterInstance) return module.createUnreachable(); }
let targetType = (<Class>resolved.target).type; let indexedSetInstance = indexedSetPrototype.resolve(); // reports
let targetExpr = this.compileExpression( if (!indexedSetInstance) return module.createUnreachable();
<Expression>resolved.targetExpression, let targetType = (<Class>target).type;
targetType let thisExpression = assert(this.program.resolvedThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType
); );
let elementExpr = this.compileExpression( let elementExpr = this.compileExpression(
(<ElementAccessExpression>expression).elementExpression, elementExpression,
Type.i32 Type.i32
); );
if (tee) { if (tee) {
let tempLocalTarget = this.currentFunction.getTempLocal(targetType); let tempLocalTarget = this.currentFunction.getTempLocal(targetType);
let tempLocalElement = this.currentFunction.getAndFreeTempLocal(this.currentType); let tempLocalElement = this.currentFunction.getAndFreeTempLocal(this.currentType);
let returnType = getterInstance.signature.returnType; let returnType = indexedGetInstance.signature.returnType;
this.currentFunction.freeTempLocal(tempLocalTarget); this.currentFunction.freeTempLocal(tempLocalTarget);
return module.createBlock(null, [ return module.createBlock(null, [
this.makeCallDirect(setterInstance, [ this.makeCallDirect(indexedSetInstance, [
module.createTeeLocal(tempLocalTarget.index, targetExpr), module.createTeeLocal(tempLocalTarget.index, thisExpr),
module.createTeeLocal(tempLocalElement.index, elementExpr), module.createTeeLocal(tempLocalElement.index, elementExpr),
valueWithCorrectType valueWithCorrectType
]), ]),
this.makeCallDirect(getterInstance, [ this.makeCallDirect(indexedGetInstance, [
module.createGetLocal(tempLocalTarget.index, tempLocalTarget.type.toNativeType()), module.createGetLocal(tempLocalTarget.index, tempLocalTarget.type.toNativeType()),
module.createGetLocal(tempLocalElement.index, tempLocalElement.type.toNativeType()) module.createGetLocal(tempLocalElement.index, tempLocalElement.type.toNativeType())
]) ])
], returnType.toNativeType()); ], returnType.toNativeType());
} else { } else {
return this.makeCallDirect(setterInstance, [ return this.makeCallDirect(indexedSetInstance, [
targetExpr, thisExpr,
elementExpr, elementExpr,
valueWithCorrectType valueWithCorrectType
]); ]);
} }
} else { } else {
this.error( this.error(
DiagnosticCode.Index_signature_in_type_0_only_permits_reading, DiagnosticCode.Index_signature_is_missing_in_type_0,
expression.range, (<Class>resolved.target).internalName expression.range, target.internalName
); );
return module.createUnreachable(); return module.createUnreachable();
} }
@ -4335,17 +4333,16 @@ export class Compiler extends DiagnosticEmitter {
compileCallExpression(expression: CallExpression, contextualType: Type): ExpressionRef { compileCallExpression(expression: CallExpression, contextualType: Type): ExpressionRef {
var module = this.module; var module = this.module;
var currentFunction = this.currentFunction; var currentFunction = this.currentFunction;
var resolved = this.program.resolveExpression(expression.expression, currentFunction); // reports var target = this.program.resolveExpression(expression.expression, currentFunction); // reports
if (!resolved) return module.createUnreachable(); if (!target) return module.createUnreachable();
var element = resolved.element;
var signature: Signature | null; var signature: Signature | null;
var indexArg: ExpressionRef; var indexArg: ExpressionRef;
switch (element.kind) { switch (target.kind) {
// direct call: concrete function // direct call: concrete function
case ElementKind.FUNCTION_PROTOTYPE: { case ElementKind.FUNCTION_PROTOTYPE: {
let prototype = <FunctionPrototype>element; let prototype = <FunctionPrototype>target;
// builtins are compiled on the fly // builtins are compiled on the fly
if (prototype.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) { if (prototype.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
@ -4377,69 +4374,72 @@ export class Compiler extends DiagnosticEmitter {
expression expression
); );
if (!instance) return module.createUnreachable(); if (!instance) return module.createUnreachable();
let thisArg: ExpressionRef = 0;
if (instance.is(CommonFlags.INSTANCE)) { if (instance.is(CommonFlags.INSTANCE)) {
assert(resolved.isInstanceTarget); let thisExpression = assert(this.program.resolvedThisExpression);
thisArg = this.compileExpression( let thisExpr = this.compileExpressionRetainType(
<Expression>resolved.targetExpression, thisExpression,
(<Class>resolved.target).type this.options.usizeType
); );
if (!thisArg) return module.createUnreachable(); return this.compileCallDirect(instance, expression.arguments, expression, thisExpr);
} else { } else {
assert(!resolved.isInstanceTarget); // if static, resolvedThisExpression is the ClassPrototype
return this.compileCallDirect(instance, expression.arguments, expression);
} }
return this.compileCallDirect(instance, expression.arguments, expression, thisArg);
} }
} }
// indirect call: index argument with signature // indirect call: index argument with signature
case ElementKind.LOCAL: { case ElementKind.LOCAL: {
if (signature = (<Local>element).type.signatureReference) { if (signature = (<Local>target).type.signatureReference) {
indexArg = module.createGetLocal((<Local>element).index, NativeType.I32); indexArg = module.createGetLocal((<Local>target).index, NativeType.I32);
break; break;
} else { } else {
this.error( this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, (<Local>element).type.toString() expression.range, (<Local>target).type.toString()
); );
return module.createUnreachable(); return module.createUnreachable();
} }
} }
case ElementKind.GLOBAL: { case ElementKind.GLOBAL: {
if (signature = (<Global>element).type.signatureReference) { if (signature = (<Global>target).type.signatureReference) {
indexArg = module.createGetGlobal((<Global>element).internalName, (<Global>element).type.toNativeType()); indexArg = module.createGetGlobal((<Global>target).internalName, (<Global>target).type.toNativeType());
break; break;
} else { } else {
this.error( this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, (<Global>element).type.toString() expression.range, (<Global>target).type.toString()
); );
return module.createUnreachable(); return module.createUnreachable();
} }
} }
case ElementKind.FIELD: { case ElementKind.FIELD: {
let type = (<Field>element).type; let type = (<Field>target).type;
if (signature = type.signatureReference) { if (signature = type.signatureReference) {
let targetExpr = this.compileExpression(assert(resolved.targetExpression), type); let thisExpression = assert(this.program.resolvedThisExpression);
let thisExpr = this.compileExpressionRetainType(
thisExpression,
this.options.usizeType
);
indexArg = module.createLoad( indexArg = module.createLoad(
4, 4,
false, false,
targetExpr, thisExpr,
NativeType.I32, NativeType.I32,
(<Field>element).memoryOffset (<Field>target).memoryOffset
); );
break; break;
} else { } else {
this.error( this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, (<Field>element).type.toString() expression.range, (<Field>target).type.toString()
); );
return module.createUnreachable(); return module.createUnreachable();
} }
} }
case ElementKind.FUNCTION_TARGET: { case ElementKind.FUNCTION_TARGET: {
signature = (<FunctionTarget>element).signature; signature = (<FunctionTarget>target).signature;
indexArg = this.compileExpression(expression.expression, (<FunctionTarget>element).type); indexArg = this.compileExpression(expression.expression, (<FunctionTarget>target).type);
break; break;
} }
case ElementKind.PROPERTY: // TODO case ElementKind.PROPERTY: // TODO
@ -4827,25 +4827,32 @@ export class Compiler extends DiagnosticEmitter {
} }
compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): ExpressionRef { compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): ExpressionRef {
var resolved = this.program.resolveElementAccess(expression, this.currentFunction); // reports var target = this.program.resolveElementAccess(expression, this.currentFunction); // reports
if (!resolved) return this.module.createUnreachable(); if (!target) return this.module.createUnreachable();
switch (target.kind) {
assert( // should be guaranteed by resolveElementAccess case ElementKind.CLASS: {
resolved.element.kind == ElementKind.FUNCTION_PROTOTYPE && let indexedGetPrototype = (<Class>target).getIndexedGet();
resolved.target && if (!indexedGetPrototype) {
resolved.target.kind == ElementKind.CLASS this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
expression.expression.range, (<Class>target).internalName
); );
var target = <Class>resolved.target; return this.module.createUnreachable();
var instance = (<FunctionPrototype>resolved.element).resolve( // reports }
null, let indexedGetInstance = indexedGetPrototype.resolve(); // reports
target.contextualTypeArguments if (!indexedGetInstance) return this.module.createUnreachable();
); let thisArg = this.compileExpression(expression.expression, (<Class>target).type);
if (!instance) return this.module.createUnreachable(); return this.compileCallDirect(indexedGetInstance, [
var thisArg = this.compileExpression(expression.expression, target.type);
return this.compileCallDirect(instance, [
expression.elementExpression expression.elementExpression
], expression, thisArg); ], expression, thisArg);
} }
}
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
);
return this.module.createUnreachable();
}
compileFunctionExpression(expression: FunctionExpression, contextualType: Type): ExpressionRef { compileFunctionExpression(expression: FunctionExpression, contextualType: Type): ExpressionRef {
var declaration = expression.declaration; var declaration = expression.declaration;
@ -4956,42 +4963,41 @@ export class Compiler extends DiagnosticEmitter {
} }
// otherwise resolve // otherwise resolve
var resolved = this.program.resolveIdentifier( // reports var target = this.program.resolveIdentifier( // reports
expression, expression,
this.currentFunction, this.currentFunction,
this.currentEnum this.currentEnum
); );
if (!resolved) return module.createUnreachable(); if (!target) return module.createUnreachable();
var element = resolved.element; switch (target.kind) {
switch (element.kind) {
case ElementKind.LOCAL: { case ElementKind.LOCAL: {
if ((<Local>element).is(CommonFlags.INLINED)) { if ((<Local>target).is(CommonFlags.INLINED)) {
return this.compileInlineConstant(<Local>element, contextualType, retainConstantType); return this.compileInlineConstant(<Local>target, contextualType, retainConstantType);
} }
let localType = (<Local>element).type; let localType = (<Local>target).type;
let localIndex = (<Local>element).index; let localIndex = (<Local>target).index;
assert(localIndex >= 0); assert(localIndex >= 0);
this.currentType = localType; this.currentType = localType;
return this.module.createGetLocal(localIndex, localType.toNativeType()); return this.module.createGetLocal(localIndex, localType.toNativeType());
} }
case ElementKind.GLOBAL: { case ElementKind.GLOBAL: {
if (element.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) { if (target.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
return compileBuiltinGetConstant(this, <Global>element, expression); return compileBuiltinGetConstant(this, <Global>target, expression);
} }
if (!this.compileGlobal(<Global>element)) { // reports; not yet compiled if a static field if (!this.compileGlobal(<Global>target)) { // reports; not yet compiled if a static field
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
let globalType = (<Global>element).type; let globalType = (<Global>target).type;
assert(globalType != Type.void); assert(globalType != Type.void);
if ((<Global>element).is(CommonFlags.INLINED)) { if ((<Global>target).is(CommonFlags.INLINED)) {
return this.compileInlineConstant(<Global>element, contextualType, retainConstantType); return this.compileInlineConstant(<Global>target, contextualType, retainConstantType);
} }
this.currentType = globalType; this.currentType = globalType;
return this.module.createGetGlobal((<Global>element).internalName, globalType.toNativeType()); return this.module.createGetGlobal((<Global>target).internalName, globalType.toNativeType());
} }
case ElementKind.ENUMVALUE: { // here: if referenced from within the same enum case ElementKind.ENUMVALUE: { // here: if referenced from within the same enum
if (!element.is(CommonFlags.COMPILED)) { if (!target.is(CommonFlags.COMPILED)) {
this.error( this.error(
DiagnosticCode.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums, DiagnosticCode.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums,
expression.range expression.range
@ -5000,13 +5006,13 @@ export class Compiler extends DiagnosticEmitter {
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
this.currentType = Type.i32; this.currentType = Type.i32;
if ((<EnumValue>element).is(CommonFlags.INLINED)) { if ((<EnumValue>target).is(CommonFlags.INLINED)) {
return this.module.createI32((<EnumValue>element).constantValue); return this.module.createI32((<EnumValue>target).constantValue);
} }
return this.module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32); return this.module.createGetGlobal((<EnumValue>target).internalName, NativeType.I32);
} }
case ElementKind.FUNCTION_PROTOTYPE: { case ElementKind.FUNCTION_PROTOTYPE: {
let instance = (<FunctionPrototype>element).resolve( let instance = (<FunctionPrototype>target).resolve(
null, null,
this.currentFunction.contextualTypeArguments this.currentFunction.contextualTypeArguments
); );
@ -5359,19 +5365,19 @@ export class Compiler extends DiagnosticEmitter {
var currentFunction = this.currentFunction; var currentFunction = this.currentFunction;
// obtain the class being instantiated // obtain the class being instantiated
var resolved = this.program.resolveExpression( // reports var target = this.program.resolveExpression( // reports
expression.expression, expression.expression,
currentFunction currentFunction
); );
if (!resolved) return module.createUnreachable(); if (!target) return module.createUnreachable();
if (resolved.element.kind != ElementKind.CLASS_PROTOTYPE) { if (target.kind != ElementKind.CLASS_PROTOTYPE) {
this.error( this.error(
DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature, DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature,
expression.expression.range expression.expression.range
); );
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
var classPrototype = <ClassPrototype>resolved.element; var classPrototype = <ClassPrototype>target;
var classInstance = classPrototype.resolveUsingTypeArguments( // reports var classInstance = classPrototype.resolveUsingTypeArguments( // reports
expression.typeArguments, expression.typeArguments,
null, null,
@ -5424,56 +5430,53 @@ export class Compiler extends DiagnosticEmitter {
var program = this.program; var program = this.program;
var module = this.module; var module = this.module;
var resolved = program.resolvePropertyAccess(propertyAccess, this.currentFunction); // reports var target = program.resolvePropertyAccess(propertyAccess, this.currentFunction); // reports
if (!resolved) return module.createUnreachable(); if (!target) return module.createUnreachable();
var element = resolved.element; switch (target.kind) {
var targetExpr: ExpressionRef;
switch (element.kind) {
case ElementKind.GLOBAL: { // static property case ElementKind.GLOBAL: { // static property
if (element.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) { if (target.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
return compileBuiltinGetConstant(this, <Global>element, propertyAccess); return compileBuiltinGetConstant(this, <Global>target, propertyAccess);
} }
if (!this.compileGlobal(<Global>element)) { // reports; not yet compiled if a static field if (!this.compileGlobal(<Global>target)) { // reports; not yet compiled if a static field
return module.createUnreachable(); return module.createUnreachable();
} }
let globalType = (<Global>element).type; let globalType = (<Global>target).type;
assert(globalType != Type.void); assert(globalType != Type.void);
if ((<Global>element).is(CommonFlags.INLINED)) { if ((<Global>target).is(CommonFlags.INLINED)) {
return this.compileInlineConstant(<Global>element, contextualType, retainConstantType); return this.compileInlineConstant(<Global>target, contextualType, retainConstantType);
} }
this.currentType = globalType; this.currentType = globalType;
return module.createGetGlobal((<Global>element).internalName, globalType.toNativeType()); return module.createGetGlobal((<Global>target).internalName, globalType.toNativeType());
} }
case ElementKind.ENUMVALUE: { // enum value case ElementKind.ENUMVALUE: { // enum value
if (!this.compileEnum((<EnumValue>element).enum)) { if (!this.compileEnum((<EnumValue>target).enum)) {
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
this.currentType = Type.i32; this.currentType = Type.i32;
if ((<EnumValue>element).is(CommonFlags.INLINED)) { if ((<EnumValue>target).is(CommonFlags.INLINED)) {
return module.createI32((<EnumValue>element).constantValue); return module.createI32((<EnumValue>target).constantValue);
} }
return module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32); return module.createGetGlobal((<EnumValue>target).internalName, NativeType.I32);
} }
case ElementKind.FIELD: { // instance field case ElementKind.FIELD: { // instance field
assert(resolved.isInstanceTarget); let thisExpression = assert(program.resolvedThisExpression);
assert((<Field>element).memoryOffset >= 0); assert((<Field>target).memoryOffset >= 0);
targetExpr = this.compileExpression( let thisExpr = this.compileExpressionRetainType(
<Expression>resolved.targetExpression, thisExpression,
this.options.usizeType, this.options.usizeType
ConversionKind.NONE
); );
this.currentType = (<Field>element).type; this.currentType = (<Field>target).type;
return module.createLoad( return module.createLoad(
(<Field>element).type.size >> 3, (<Field>target).type.size >> 3,
(<Field>element).type.is(TypeFlags.SIGNED | TypeFlags.INTEGER), (<Field>target).type.is(TypeFlags.SIGNED | TypeFlags.INTEGER),
targetExpr, thisExpr,
(<Field>element).type.toNativeType(), (<Field>target).type.toNativeType(),
(<Field>element).memoryOffset (<Field>target).memoryOffset
); );
} }
case ElementKind.PROPERTY: { // instance property (here: getter) case ElementKind.PROPERTY: { // instance property (here: getter)
let prototype = (<Property>element).getterPrototype; let prototype = (<Property>target).getterPrototype;
if (prototype) { if (prototype) {
let instance = prototype.resolve(null); // reports let instance = prototype.resolve(null); // reports
if (!instance) return module.createUnreachable(); if (!instance) return module.createUnreachable();
@ -5489,12 +5492,13 @@ export class Compiler extends DiagnosticEmitter {
if (instance.is(CommonFlags.INSTANCE)) { if (instance.is(CommonFlags.INSTANCE)) {
let parent = assert(instance.memberOf); let parent = assert(instance.memberOf);
assert(parent.kind == ElementKind.CLASS); assert(parent.kind == ElementKind.CLASS);
targetExpr = this.compileExpression( let thisExpression = assert(program.resolvedThisExpression);
<Expression>resolved.targetExpression, let thisExpr = this.compileExpressionRetainType(
(<Class>parent).type thisExpression,
this.options.usizeType
); );
this.currentType = signature.returnType; this.currentType = signature.returnType;
return this.compileCallDirect(instance, [], propertyAccess, targetExpr); return this.compileCallDirect(instance, [], propertyAccess, thisExpr);
} else { } else {
this.currentType = signature.returnType; this.currentType = signature.returnType;
return this.compileCallDirect(instance, [], propertyAccess); return this.compileCallDirect(instance, [], propertyAccess);
@ -5502,7 +5506,7 @@ export class Compiler extends DiagnosticEmitter {
} else { } else {
this.error( this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1, DiagnosticCode.Property_0_does_not_exist_on_type_1,
propertyAccess.range, (<Property>element).simpleName, (<Property>element).parent.toString() propertyAccess.range, (<Property>target).simpleName, (<Property>target).parent.toString()
); );
return module.createUnreachable(); return module.createUnreachable();
} }

View File

@ -137,6 +137,11 @@ export class Program extends DiagnosticEmitter {
/** String instance reference. */ /** String instance reference. */
stringInstance: Class | null = null; stringInstance: Class | null = null;
/** Target expression of the previously resolved property or element access. */
resolvedThisExpression: Expression | null = null;
/** Element expression of the previously resolved element access. */
resolvedElementExpression : Expression | null = null;
/** Constructs a new program, optionally inheriting parser diagnostics. */ /** Constructs a new program, optionally inheriting parser diagnostics. */
constructor(diagnostics: DiagnosticMessage[] | null = null) { constructor(diagnostics: DiagnosticMessage[] | null = null) {
super(diagnostics); super(diagnostics);
@ -289,18 +294,15 @@ export class Program extends DiagnosticEmitter {
for (let i = 0, k = queuedDerivedClasses.length; i < k; ++i) { for (let i = 0, k = queuedDerivedClasses.length; i < k; ++i) {
let derivedDeclaration = queuedDerivedClasses[i].declaration; let derivedDeclaration = queuedDerivedClasses[i].declaration;
let derivedType = assert(derivedDeclaration.extendsType); let derivedType = assert(derivedDeclaration.extendsType);
let resolved = this.resolveIdentifier(derivedType.name, null); let derived = this.resolveIdentifier(derivedType.name, null); // reports
if (resolved) { if (!derived) continue;
if (resolved.element.kind != ElementKind.CLASS_PROTOTYPE) { if (derived.kind == ElementKind.CLASS_PROTOTYPE) {
queuedDerivedClasses[i].basePrototype = <ClassPrototype>derived;
} else {
this.error( this.error(
DiagnosticCode.A_class_may_only_extend_another_class, DiagnosticCode.A_class_may_only_extend_another_class,
derivedType.range derivedType.range
); );
continue;
}
queuedDerivedClasses[i].basePrototype = (
<ClassPrototype>resolved.element
);
} }
} }
@ -1709,7 +1711,7 @@ export class Program extends DiagnosticEmitter {
identifier: IdentifierExpression, identifier: IdentifierExpression,
contextualFunction: Function | null, contextualFunction: Function | null,
contextualEnum: Enum | null = null contextualEnum: Enum | null = null
): ResolvedElement | null { ): Element | null {
var name = identifier.text; var name = identifier.text;
var element: Element | null; var element: Element | null;
@ -1723,16 +1725,18 @@ export class Program extends DiagnosticEmitter {
(element = contextualEnum.members.get(name)) && (element = contextualEnum.members.get(name)) &&
element.kind == ElementKind.ENUMVALUE element.kind == ElementKind.ENUMVALUE
) { ) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); this.resolvedThisExpression = null;
return resolvedElement.set(element); this.resolvedElementExpression = null;
return element; // ENUMVALUE
} }
} else if (contextualFunction) { } else if (contextualFunction) {
// check locals // check locals
if (element = contextualFunction.flow.getScopedLocal(name)) { if (element = contextualFunction.flow.getScopedLocal(name)) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); this.resolvedThisExpression = null;
return resolvedElement.set(element); this.resolvedElementExpression = null;
return element; // LOCAL
} }
// check outer scope locals // check outer scope locals
@ -1752,8 +1756,9 @@ export class Program extends DiagnosticEmitter {
if (namespace = contextualFunction.prototype.namespace) { if (namespace = contextualFunction.prototype.namespace) {
do { do {
if (element = this.elementsLookup.get(namespace.internalName + STATIC_DELIMITER + name)) { if (element = this.elementsLookup.get(namespace.internalName + STATIC_DELIMITER + name)) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); this.resolvedThisExpression = null;
return resolvedElement.set(element); this.resolvedElementExpression = null;
return element; // LOCAL
} }
} while (namespace = namespace.namespace); } while (namespace = namespace.namespace);
} }
@ -1761,14 +1766,16 @@ export class Program extends DiagnosticEmitter {
// search current file // search current file
if (element = this.elementsLookup.get(identifier.range.source.internalPath + PATH_DELIMITER + name)) { if (element = this.elementsLookup.get(identifier.range.source.internalPath + PATH_DELIMITER + name)) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); this.resolvedThisExpression = null;
return resolvedElement.set(element); this.resolvedElementExpression = null;
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
} }
// search global scope // search global scope
if (element = this.elementsLookup.get(name)) { if (element = this.elementsLookup.get(name)) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); this.resolvedThisExpression = null;
return resolvedElement.set(element); this.resolvedElementExpression = null;
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
} }
this.error( this.error(
@ -1782,47 +1789,63 @@ export class Program extends DiagnosticEmitter {
resolvePropertyAccess( resolvePropertyAccess(
propertyAccess: PropertyAccessExpression, propertyAccess: PropertyAccessExpression,
contextualFunction: Function contextualFunction: Function
): ResolvedElement | null { ): Element | null {
// start by resolving the lhs target (expression before the last dot) // start by resolving the lhs target (expression before the last dot)
var targetExpression = propertyAccess.expression; var targetExpression = propertyAccess.expression;
resolvedElement = this.resolveExpression( // reports var target = this.resolveExpression(targetExpression, contextualFunction); // reports
targetExpression, if (!target) return null;
contextualFunction
);
if (!resolvedElement) return null;
var target = resolvedElement.element;
// at this point we know exactly what the target is, so look up the element within // at this point we know exactly what the target is, so look up the element within
var propertyName = propertyAccess.property.text; var propertyName = propertyAccess.property.text;
var targetType: Type;
var member: Element | null;
// Resolve variable-likes to their class type first // Resolve variable-likes to the class type they reference first
switch (target.kind) { switch (target.kind) {
case ElementKind.GLOBAL: case ElementKind.GLOBAL:
case ElementKind.LOCAL: case ElementKind.LOCAL:
case ElementKind.FIELD: { case ElementKind.FIELD: {
if (!(targetType = (<VariableLikeElement>target).type).classReference) { let classReference = (<VariableLikeElement>target).type.classReference;
if (!classReference) {
this.error( this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1, DiagnosticCode.Property_0_does_not_exist_on_type_1,
propertyAccess.property.range, propertyName, targetType.toString() propertyAccess.property.range, propertyName, (<VariableLikeElement>target).type.toString()
); );
return null; return null;
} }
target = <Class>targetType.classReference; target = classReference;
break; break;
} }
case ElementKind.PROPERTY: { case ElementKind.PROPERTY: {
let getter = assert((<Property>target).getterPrototype).resolve(); // reports let getter = assert((<Property>target).getterPrototype).resolve(); // reports
if (!getter) return null; if (!getter) return null;
if (!(targetType = getter.signature.returnType).classReference) { let classReference = getter.signature.returnType.classReference;
if (!classReference) {
this.error( this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1, DiagnosticCode.Property_0_does_not_exist_on_type_1,
propertyAccess.property.range, propertyName, targetType.toString() propertyAccess.property.range, propertyName, getter.signature.returnType.toString()
); );
return null; return null;
} }
target = <Class>targetType.classReference; target = classReference;
break;
}
case ElementKind.CLASS: {
let elementExpression = this.resolvedElementExpression;
if (elementExpression) {
let indexedGetPrototype = (<Class>target).getIndexedGet();
if (indexedGetPrototype) {
let indexedGetInstance = indexedGetPrototype.resolve(); // reports
if (!indexedGetInstance) return null;
let classReference = indexedGetInstance.signature.returnType.classReference;
if (!classReference) {
this.error(
DiagnosticCode.Property_0_does_not_exist_on_type_1,
propertyAccess.property.range, propertyName, (<VariableLikeElement>target).type.toString()
);
return null;
}
target = classReference;
}
}
break; break;
} }
} }
@ -1832,17 +1855,21 @@ export class Program extends DiagnosticEmitter {
case ElementKind.CLASS_PROTOTYPE: case ElementKind.CLASS_PROTOTYPE:
case ElementKind.CLASS: { case ElementKind.CLASS: {
do { do {
if (target.members && (member = target.members.get(propertyName))) { let members = target.members;
return resolvedElement.set(member).withTarget(target, targetExpression); let member: Element | null;
if (members && (member = members.get(propertyName))) {
this.resolvedThisExpression = targetExpression;
this.resolvedElementExpression = null;
return member; // instance FIELD, static GLOBAL, FUNCTION_PROTOTYPE...
} }
// check inherited static members on the base prototype while target is a class prototype // traverse inherited static members on the base prototype if target is a class prototype
if (target.kind == ElementKind.CLASS_PROTOTYPE) { if (target.kind == ElementKind.CLASS_PROTOTYPE) {
if ((<ClassPrototype>target).basePrototype) { if ((<ClassPrototype>target).basePrototype) {
target = <ClassPrototype>(<ClassPrototype>target).basePrototype; target = <ClassPrototype>(<ClassPrototype>target).basePrototype;
} else { } else {
break; break;
} }
// or inherited instance members on the base class while target is a class instance // traverse inherited instance members on the base class if target is a class instance
} else if (target.kind == ElementKind.CLASS) { } else if (target.kind == ElementKind.CLASS) {
if ((<Class>target).base) { if ((<Class>target).base) {
target = <Class>(<Class>target).base; target = <Class>(<Class>target).base;
@ -1856,8 +1883,12 @@ export class Program extends DiagnosticEmitter {
break; break;
} }
default: { // enums or other namespace-like elements default: { // enums or other namespace-like elements
if (target.members && (member = target.members.get(propertyName))) { let members = target.members;
return resolvedElement.set(member).withTarget(target, targetExpression); let member: Element | null;
if (members && (member = members.get(propertyName))) {
this.resolvedThisExpression = targetExpression;
this.resolvedElementExpression = null;
return member; // static ENUMVALUE, static GLOBAL, static FUNCTION_PROTOTYPE...
} }
break; break;
} }
@ -1872,39 +1903,41 @@ export class Program extends DiagnosticEmitter {
resolveElementAccess( resolveElementAccess(
elementAccess: ElementAccessExpression, elementAccess: ElementAccessExpression,
contextualFunction: Function contextualFunction: Function
): ResolvedElement | null { ): Element | null {
// start by resolving the lhs target
var targetExpression = elementAccess.expression; var targetExpression = elementAccess.expression;
resolvedElement = this.resolveExpression( var target = this.resolveExpression(targetExpression, contextualFunction);
targetExpression, if (!target) return null;
contextualFunction
);
if (!resolvedElement) return null;
var target = resolvedElement.element;
switch (target.kind) { switch (target.kind) {
case ElementKind.GLOBAL: case ElementKind.GLOBAL:
case ElementKind.LOCAL: case ElementKind.LOCAL:
case ElementKind.FIELD: { case ElementKind.FIELD: {
assert(!this.resolvedThisExpression && !this.resolvedElementExpression);
let type = (<VariableLikeElement>target).type; let type = (<VariableLikeElement>target).type;
if (type.classReference) { if (target = type.classReference) {
let indexedGetName = (target = type.classReference).prototype.fnIndexedGet; this.resolvedThisExpression = targetExpression;
let indexedGet: Element | null; this.resolvedElementExpression = elementAccess.elementExpression;
if ( return target;
indexedGetName != null && }
target.members && break;
(indexedGet = target.members.get(indexedGetName)) && }
indexedGet.kind == ElementKind.FUNCTION_PROTOTYPE case ElementKind.CLASS: { // element access on element access
) { let indexedGetPrototype = (<Class>target).getIndexedGet();
return resolvedElement.set(indexedGet).withTarget(type.classReference, targetExpression); if (indexedGetPrototype) {
let indexedGetInstance = indexedGetPrototype.resolve(); // reports
if (!indexedGetInstance) return null;
let returnType = indexedGetInstance.signature.returnType;
if (target = returnType.classReference) {
this.resolvedThisExpression = targetExpression;
this.resolvedElementExpression = elementAccess.elementExpression;
return target;
} }
} }
break; break;
} }
// FIXME: indexed access on indexed access
} }
this.error( this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0, DiagnosticCode.Operation_not_supported,
targetExpression.range, target.internalName targetExpression.range
); );
return null; return null;
} }
@ -1912,7 +1945,7 @@ export class Program extends DiagnosticEmitter {
resolveExpression( resolveExpression(
expression: Expression, expression: Expression,
contextualFunction: Function contextualFunction: Function
): ResolvedElement | null { ): Element | null {
while (expression.kind == NodeKind.PARENTHESIZED) { while (expression.kind == NodeKind.PARENTHESIZED) {
expression = (<ParenthesizedExpression>expression).expression; expression = (<ParenthesizedExpression>expression).expression;
} }
@ -1922,8 +1955,9 @@ export class Program extends DiagnosticEmitter {
if (type) { if (type) {
let classType = type.classReference; let classType = type.classReference;
if (classType) { if (classType) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); this.resolvedThisExpression = null;
return resolvedElement.set(classType); this.resolvedElementExpression = null;
return classType;
} }
} }
return null; return null;
@ -1934,8 +1968,9 @@ export class Program extends DiagnosticEmitter {
case NodeKind.THIS: { // -> Class / ClassPrototype case NodeKind.THIS: { // -> Class / ClassPrototype
let parent = contextualFunction.memberOf; let parent = contextualFunction.memberOf;
if (parent) { if (parent) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); this.resolvedThisExpression = null;
return resolvedElement.set(parent); this.resolvedElementExpression = null;
return parent;
} }
this.error( this.error(
DiagnosticCode._this_cannot_be_referenced_in_current_location, DiagnosticCode._this_cannot_be_referenced_in_current_location,
@ -1946,8 +1981,9 @@ export class Program extends DiagnosticEmitter {
case NodeKind.SUPER: { // -> Class case NodeKind.SUPER: { // -> Class
let parent = contextualFunction.memberOf; let parent = contextualFunction.memberOf;
if (parent && parent.kind == ElementKind.CLASS && (parent = (<Class>parent).base)) { if (parent && parent.kind == ElementKind.CLASS && (parent = (<Class>parent).base)) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); this.resolvedThisExpression = null;
return resolvedElement.set(parent); this.resolvedElementExpression = null;
return parent;
} }
this.error( this.error(
DiagnosticCode._super_can_only_be_referenced_in_a_derived_class, DiagnosticCode._super_can_only_be_referenced_in_a_derived_class,
@ -1971,24 +2007,22 @@ export class Program extends DiagnosticEmitter {
); );
} }
case NodeKind.CALL: { case NodeKind.CALL: {
let resolved = this.resolveExpression( let targetExpression = (<CallExpression>expression).expression;
(<CallExpression>expression).expression, let target = this.resolveExpression(targetExpression, contextualFunction); // reports
contextualFunction if (!target) return null;
); if (target.kind == ElementKind.FUNCTION_PROTOTYPE) {
if (resolved) { let instance = (<FunctionPrototype>target).resolveUsingTypeArguments( // reports
let element = resolved.element;
if (element && element.kind == ElementKind.FUNCTION_PROTOTYPE) {
let instance = (<FunctionPrototype>element).resolveUsingTypeArguments(
(<CallExpression>expression).typeArguments, (<CallExpression>expression).typeArguments,
contextualFunction.contextualTypeArguments, contextualFunction.contextualTypeArguments,
expression expression
); );
if (instance) { if (!instance) return null;
let returnType = instance.signature.returnType; let returnType = instance.signature.returnType;
let classType = returnType.classReference; let classType = returnType.classReference;
if (classType) { if (classType) {
if (!resolvedElement) resolvedElement = new ResolvedElement(); // reuse resolvedThisExpression (might be property access)
return resolvedElement.set(classType); // reuse resolvedElementExpression (might be element access)
return classType;
} else { } else {
let signature = returnType.signatureReference; let signature = returnType.signatureReference;
if (signature) { if (signature) {
@ -1997,12 +2031,16 @@ export class Program extends DiagnosticEmitter {
functionTarget = new FunctionTarget(this, signature); functionTarget = new FunctionTarget(this, signature);
signature.cachedFunctionTarget = functionTarget; signature.cachedFunctionTarget = functionTarget;
} }
if (!resolvedElement) resolvedElement = new ResolvedElement(); // reuse resolvedThisExpression (might be property access)
return resolvedElement.set(functionTarget); // reuse resolvedElementExpression (might be element access)
} return functionTarget;
}
} }
} }
this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
targetExpression.range, target.internalName
);
return null;
} }
break; break;
} }
@ -2015,44 +2053,6 @@ export class Program extends DiagnosticEmitter {
} }
} }
/** Common result structure returned when calling any of the resolve functions on a {@link Program}. */
export class ResolvedElement {
/** The target element, if a property or element access */
target: Element | null;
/** The target element's expression, if a property or element access. */
targetExpression: Expression | null;
/** The element being accessed. */
element: Element;
/** Clears the target and sets the resolved element. */
set(element: Element): this {
this.target = null;
this.targetExpression = null;
this.element = element;
return this;
}
/** Sets the resolved target in addition to the previously set element. */
withTarget(target: Element, targetExpression: Expression): this {
this.target = target;
this.targetExpression = targetExpression;
return this;
}
/** Tests if the target is a valid instance target. */
get isInstanceTarget(): bool {
return (
this.target != null &&
this.target.kind == ElementKind.CLASS &&
this.targetExpression != null
);
}
}
// Cached result structure instance
var resolvedElement: ResolvedElement | null;
/** Indicates the specific kind of an {@link Element}. */ /** Indicates the specific kind of an {@link Element}. */
export enum ElementKind { export enum ElementKind {
/** A {@link Global}. */ /** A {@link Global}. */
@ -3264,6 +3264,24 @@ export class Class extends Element {
return false; return false;
} }
getIndexedGet(): FunctionPrototype | null {
var members = this.members;
var name = this.prototype.fnIndexedGet;
if (!members || name == null) return null;
var element = members.get(name);
if (!element || element.kind != ElementKind.FUNCTION_PROTOTYPE) return null;
return <FunctionPrototype>element;
}
getIndexedSet(): FunctionPrototype | null {
var members = this.members;
var name = this.prototype.fnIndexedSet;
if (!members || name == null) return null;
var element = members.get(name);
if (!element || element.kind != ElementKind.FUNCTION_PROTOTYPE) return null;
return <FunctionPrototype>element;
}
toString(): string { toString(): string {
return this.simpleName; return this.simpleName;
} }

View File

@ -8,6 +8,7 @@
"./**/*.ts" "./**/*.ts"
], ],
"exclude": [ "exclude": [
"./binary.ts",
"./extra/**", "./extra/**",
"./glue/wasm/**" "./glue/wasm/**"
] ]

View File

@ -1,5 +1,14 @@
/** @module util *//***/ /** @module util *//***/
/** Reads a 32-bit integer from the specified buffer. */
export function readI32(buffer: Uint8Array, offset: i32): i32 {
return buffer[offset ]
| buffer[offset + 1] << 8
| buffer[offset + 2] << 16
| buffer[offset + 3] << 24;
}
/** Writes a 32-bit integer to the specified buffer. */
export function writeI32(value: i32, buffer: Uint8Array, offset: i32): void { export function writeI32(value: i32, buffer: Uint8Array, offset: i32): void {
buffer[offset ] = value; buffer[offset ] = value;
buffer[offset + 1] = value >>> 8; buffer[offset + 1] = value >>> 8;
@ -7,15 +16,35 @@ export function writeI32(value: i32, buffer: Uint8Array, offset: i32): void {
buffer[offset + 3] = value >>> 24; buffer[offset + 3] = value >>> 24;
} }
/** Reads a 64-bit integer from the specified buffer. */
export function readI64(buffer: Uint8Array, offset: i32): I64 {
var lo = readI32(buffer, offset);
var hi = readI32(buffer, offset + 4);
return i64_new(lo, hi);
}
/** Writes a 64-bit integer to the specified buffer. */
export function writeI64(value: I64, buffer: Uint8Array, offset: i32): void { export function writeI64(value: I64, buffer: Uint8Array, offset: i32): void {
writeI32(i64_low(value), buffer, offset); writeI32(i64_low(value), buffer, offset);
writeI32(i64_high(value), buffer, offset + 4); writeI32(i64_high(value), buffer, offset + 4);
} }
/** Reads a 32-bit float from the specified buffer. */
export function readF32(buffer: Uint8Array, offset: i32): f32 {
return i32_as_f32(readI32(buffer, offset));
}
/** Writes a 32-bit float to the specified buffer. */
export function writeF32(value: f32, buffer: Uint8Array, offset: i32): void { export function writeF32(value: f32, buffer: Uint8Array, offset: i32): void {
writeI32(f32_as_i32(value), buffer, offset); writeI32(f32_as_i32(value), buffer, offset);
} }
/** Reads a 64-bit float from the specified buffer. */
export function readF64(buffer: Uint8Array, offset: i32): f64 {
return i64_as_f64(readI64(buffer, offset));
}
/** Writes a 64-bit float to the specified buffer. */
export function writeF64(value: f64, buffer: Uint8Array, offset: i32): void { export function writeF64(value: f64, buffer: Uint8Array, offset: i32): void {
var valueI64 = f64_as_i64(value); var valueI64 = f64_as_i64(value);
writeI32(i64_low(valueI64), buffer, offset); writeI32(i64_low(valueI64), buffer, offset);

View File

@ -0,0 +1,330 @@
(module
(type $ii (func (param i32) (result i32)))
(type $iii (func (param i32 i32) (result i32)))
(type $iiii (func (param i32 i32 i32) (result i32)))
(type $iiiiv (func (param i32 i32 i32 i32)))
(import "env" "abort" (func $abort (param i32 i32 i32 i32)))
(global $argumentCount (mut i32) (i32.const 0))
(memory $0 1)
(data (i32.const 8) "\0e\00\00\00~\00l\00i\00b\00/\00s\00t\00r\00i\00n\00g\00.\00t\00s")
(data (i32.const 40) "\04\00\00\00n\00u\00l\00l")
(export "i32ArrayArrayElementAccess" (func $std/array-access/i32ArrayArrayElementAccess))
(export "stringArrayPropertyAccess" (func $std/array-access/stringArrayPropertyAccess))
(export "stringArrayMethodCall" (func $std/array-access/stringArrayMethodCall))
(export "stringArrayArrayPropertyAccess" (func $std/array-access/stringArrayArrayPropertyAccess))
(export "stringArrayArrayMethodCall" (func $std/array-access/stringArrayArrayMethodCall))
(export "memory" (memory $0))
(func $~lib/array/Array<Array<i32>>#__get (; 1 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if
(i32.ge_u
(get_local $1)
(i32.load offset=4
(get_local $0)
)
)
(unreachable)
)
(i32.load
(i32.add
(i32.load
(get_local $0)
)
(i32.shl
(get_local $1)
(i32.const 2)
)
)
)
)
(func $std/array-access/i32ArrayArrayElementAccess (; 2 ;) (type $ii) (param $0 i32) (result i32)
(call $~lib/array/Array<Array<i32>>#__get
(call $~lib/array/Array<Array<i32>>#__get
(get_local $0)
(i32.const 0)
)
(i32.const 1)
)
)
(func $std/array-access/stringArrayPropertyAccess (; 3 ;) (type $ii) (param $0 i32) (result i32)
(i32.load
(call $~lib/array/Array<Array<i32>>#__get
(get_local $0)
(i32.const 0)
)
)
)
(func $~lib/memory/compare_memory (; 4 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(if
(i32.eq
(get_local $0)
(get_local $1)
)
(return
(i32.const 0)
)
)
(loop $continue|0
(if
(if (result i32)
(get_local $2)
(i32.eq
(i32.load8_u
(get_local $0)
)
(i32.load8_u
(get_local $1)
)
)
(get_local $2)
)
(block
(set_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
(set_local $0
(i32.add
(get_local $0)
(i32.const 1)
)
)
(set_local $1
(i32.add
(get_local $1)
(i32.const 1)
)
)
(br $continue|0)
)
)
)
(if (result i32)
(get_local $2)
(i32.sub
(i32.load8_u
(get_local $0)
)
(i32.load8_u
(get_local $1)
)
)
(i32.const 0)
)
)
(func $~lib/string/String.__eq (; 5 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(if
(get_local $0)
(if
(i32.eqz
(get_local $1)
)
(return
(i32.const 0)
)
)
(return
(i32.eqz
(get_local $1)
)
)
)
(if
(i32.ne
(tee_local $2
(i32.load
(get_local $0)
)
)
(i32.load
(get_local $1)
)
)
(return
(i32.const 0)
)
)
(i32.eqz
(call $~lib/memory/compare_memory
(i32.add
(get_local $0)
(i32.const 4)
)
(i32.add
(get_local $1)
(i32.const 4)
)
(i32.shl
(get_local $2)
(i32.const 1)
)
)
)
)
(func $~lib/string/String.__ne (; 6 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.eqz
(call $~lib/string/String.__eq
(get_local $0)
(get_local $1)
)
)
)
(func $~lib/string/String#startsWith (; 7 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(if
(i32.eqz
(call $~lib/string/String.__ne
(get_local $0)
(i32.const 0)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 8)
(i32.const 243)
(i32.const 4)
)
(unreachable)
)
)
(if
(call $~lib/string/String.__eq
(get_local $1)
(i32.const 0)
)
(set_local $1
(i32.const 40)
)
)
(if
(i32.gt_s
(i32.add
(tee_local $4
(i32.load
(get_local $1)
)
)
(tee_local $2
(select
(tee_local $2
(select
(get_local $2)
(i32.const 0)
(i32.gt_s
(get_local $2)
(get_local $3)
)
)
)
(tee_local $3
(tee_local $5
(i32.load
(get_local $0)
)
)
)
(i32.lt_s
(get_local $2)
(get_local $3)
)
)
)
)
(get_local $5)
)
(return
(i32.const 0)
)
)
(i32.eqz
(call $~lib/memory/compare_memory
(i32.add
(i32.add
(get_local $0)
(i32.const 4)
)
(i32.shl
(get_local $2)
(i32.const 1)
)
)
(i32.add
(get_local $1)
(i32.const 4)
)
(i32.shl
(get_local $4)
(i32.const 1)
)
)
)
)
(func $~lib/string/String#startsWith|trampoline (; 8 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(block $1of1
(block $0of1
(block $oob
(br_table $0of1 $1of1 $oob
(i32.sub
(get_global $argumentCount)
(i32.const 1)
)
)
)
(unreachable)
)
(set_local $2
(i32.const 0)
)
)
(call $~lib/string/String#startsWith
(get_local $0)
(get_local $1)
(get_local $2)
)
)
(func $std/array-access/stringArrayMethodCall (; 9 ;) (type $ii) (param $0 i32) (result i32)
(set_global $argumentCount
(i32.const 1)
)
(call $~lib/string/String#startsWith|trampoline
(call $~lib/array/Array<Array<i32>>#__get
(get_local $0)
(i32.const 0)
)
(i32.const 4)
(i32.const 0)
)
)
(func $std/array-access/stringArrayArrayPropertyAccess (; 10 ;) (type $ii) (param $0 i32) (result i32)
(i32.load
(call $~lib/array/Array<Array<i32>>#__get
(call $~lib/array/Array<Array<i32>>#__get
(get_local $0)
(i32.const 0)
)
(i32.const 1)
)
)
)
(func $std/array-access/stringArrayArrayMethodCall (; 11 ;) (type $ii) (param $0 i32) (result i32)
(set_global $argumentCount
(i32.const 1)
)
(call $~lib/string/String#startsWith|trampoline
(call $~lib/array/Array<Array<i32>>#__get
(call $~lib/array/Array<Array<i32>>#__get
(get_local $0)
(i32.const 0)
)
(i32.const 1)
)
(i32.const 4)
(i32.const 0)
)
)
)

View File

@ -0,0 +1,19 @@
export function i32ArrayArrayElementAccess(a: i32[][]): i32 {
return a[0][1];
}
export function stringArrayPropertyAccess(a: string[]): i32 {
return a[0].length;
}
export function stringArrayMethodCall(a: string[]): i32 {
return a[0].startsWith("");
}
export function stringArrayArrayPropertyAccess(a: string[][]): i32 {
return a[0][1].length;
}
export function stringArrayArrayMethodCall(a: string[][]): i32 {
return a[0][1].startsWith("");
}

View File

@ -0,0 +1,450 @@
(module
(type $ii (func (param i32) (result i32)))
(type $iii (func (param i32 i32) (result i32)))
(type $i (func (result i32)))
(type $iiii (func (param i32 i32 i32) (result i32)))
(type $iiiiv (func (param i32 i32 i32 i32)))
(import "env" "abort" (func $abort (param i32 i32 i32 i32)))
(global $~lib/string/HEADER_SIZE i32 (i32.const 4))
(global $argumentCount (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 52))
(memory $0 1)
(data (i32.const 4) "\00\00\00\00")
(data (i32.const 8) "\0e\00\00\00~\00l\00i\00b\00/\00s\00t\00r\00i\00n\00g\00.\00t\00s\00")
(data (i32.const 40) "\04\00\00\00n\00u\00l\00l\00")
(export "i32ArrayArrayElementAccess" (func $std/array-access/i32ArrayArrayElementAccess))
(export "stringArrayPropertyAccess" (func $std/array-access/stringArrayPropertyAccess))
(export "stringArrayMethodCall" (func $std/array-access/stringArrayMethodCall))
(export "stringArrayArrayPropertyAccess" (func $std/array-access/stringArrayArrayPropertyAccess))
(export "stringArrayArrayMethodCall" (func $std/array-access/stringArrayArrayMethodCall))
(export "memory" (memory $0))
(func $~lib/array/Array<Array<i32>>#__get (; 1 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if
(i32.ge_u
(get_local $1)
(i32.load offset=4
(get_local $0)
)
)
(unreachable)
)
(return
(i32.load
(i32.add
(i32.load
(get_local $0)
)
(i32.mul
(get_local $1)
(i32.const 4)
)
)
)
)
)
(func $~lib/array/Array<i32>#__get (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if
(i32.ge_u
(get_local $1)
(i32.load offset=4
(get_local $0)
)
)
(unreachable)
)
(return
(i32.load
(i32.add
(i32.load
(get_local $0)
)
(i32.mul
(get_local $1)
(i32.const 4)
)
)
)
)
)
(func $std/array-access/i32ArrayArrayElementAccess (; 3 ;) (type $ii) (param $0 i32) (result i32)
(return
(call $~lib/array/Array<i32>#__get
(call $~lib/array/Array<Array<i32>>#__get
(get_local $0)
(i32.const 0)
)
(i32.const 1)
)
)
)
(func $~lib/array/Array<String>#__get (; 4 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if
(i32.ge_u
(get_local $1)
(i32.load offset=4
(get_local $0)
)
)
(unreachable)
)
(return
(i32.load
(i32.add
(i32.load
(get_local $0)
)
(i32.mul
(get_local $1)
(i32.const 4)
)
)
)
)
)
(func $std/array-access/stringArrayPropertyAccess (; 5 ;) (type $ii) (param $0 i32) (result i32)
(return
(i32.load
(call $~lib/array/Array<String>#__get
(get_local $0)
(i32.const 0)
)
)
)
)
(func $~lib/memory/compare_memory (; 6 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(if
(i32.eq
(get_local $0)
(get_local $1)
)
(return
(i32.const 0)
)
)
(block $break|0
(loop $continue|0
(if
(if (result i32)
(get_local $2)
(i32.eq
(i32.load8_u
(get_local $0)
)
(i32.load8_u
(get_local $1)
)
)
(get_local $2)
)
(block
(block
(set_local $2
(i32.sub
(get_local $2)
(i32.const 1)
)
)
(set_local $0
(i32.add
(get_local $0)
(i32.const 1)
)
)
(set_local $1
(i32.add
(get_local $1)
(i32.const 1)
)
)
)
(br $continue|0)
)
)
)
)
(return
(if (result i32)
(get_local $2)
(i32.sub
(i32.load8_u
(get_local $0)
)
(i32.load8_u
(get_local $1)
)
)
(i32.const 0)
)
)
)
(func $~lib/string/String.__eq (; 7 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(if
(i32.eqz
(get_local $0)
)
(return
(i32.eqz
(get_local $1)
)
)
(if
(i32.eqz
(get_local $1)
)
(return
(i32.const 0)
)
)
)
(set_local $2
(i32.load
(get_local $0)
)
)
(if
(i32.ne
(get_local $2)
(i32.load
(get_local $1)
)
)
(return
(i32.const 0)
)
)
(return
(i32.eqz
(call $~lib/memory/compare_memory
(i32.add
(get_local $0)
(i32.const 4)
)
(i32.add
(get_local $1)
(i32.const 4)
)
(i32.shl
(get_local $2)
(i32.const 1)
)
)
)
)
)
(func $~lib/string/String.__ne (; 8 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(return
(i32.eqz
(call $~lib/string/String.__eq
(get_local $0)
(get_local $1)
)
)
)
)
(func $~lib/string/String#startsWith (; 9 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i32)
(local $7 i32)
(local $8 i32)
(if
(i32.eqz
(call $~lib/string/String.__ne
(get_local $0)
(i32.const 0)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 8)
(i32.const 243)
(i32.const 4)
)
(unreachable)
)
)
(if
(call $~lib/string/String.__eq
(get_local $1)
(i32.const 0)
)
(set_local $1
(i32.const 40)
)
)
(set_local $3
(get_local $2)
)
(set_local $4
(i32.load
(get_local $0)
)
)
(set_local $7
(select
(tee_local $5
(select
(tee_local $5
(get_local $3)
)
(tee_local $6
(i32.const 0)
)
(i32.gt_s
(get_local $5)
(get_local $6)
)
)
)
(tee_local $6
(get_local $4)
)
(i32.lt_s
(get_local $5)
(get_local $6)
)
)
)
(set_local $8
(i32.load
(get_local $1)
)
)
(if
(i32.gt_s
(i32.add
(get_local $8)
(get_local $7)
)
(get_local $4)
)
(return
(i32.const 0)
)
)
(return
(i32.eqz
(call $~lib/memory/compare_memory
(i32.add
(i32.add
(get_local $0)
(i32.const 4)
)
(i32.shl
(get_local $7)
(i32.const 1)
)
)
(i32.add
(get_local $1)
(i32.const 4)
)
(i32.shl
(get_local $8)
(i32.const 1)
)
)
)
)
)
(func $~lib/string/String#startsWith|trampoline (; 10 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(block $1of1
(block $0of1
(block $oob
(br_table $0of1 $1of1 $oob
(i32.sub
(get_global $argumentCount)
(i32.const 1)
)
)
)
(unreachable)
)
(set_local $2
(i32.const 0)
)
)
(call $~lib/string/String#startsWith
(get_local $0)
(get_local $1)
(get_local $2)
)
)
(func $std/array-access/stringArrayMethodCall (; 11 ;) (type $ii) (param $0 i32) (result i32)
(return
(block (result i32)
(set_global $argumentCount
(i32.const 1)
)
(call $~lib/string/String#startsWith|trampoline
(call $~lib/array/Array<String>#__get
(get_local $0)
(i32.const 0)
)
(i32.const 4)
(i32.const 0)
)
)
)
)
(func $~lib/array/Array<Array<String>>#__get (; 12 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if
(i32.ge_u
(get_local $1)
(i32.load offset=4
(get_local $0)
)
)
(unreachable)
)
(return
(i32.load
(i32.add
(i32.load
(get_local $0)
)
(i32.mul
(get_local $1)
(i32.const 4)
)
)
)
)
)
(func $std/array-access/stringArrayArrayPropertyAccess (; 13 ;) (type $ii) (param $0 i32) (result i32)
(return
(i32.load
(call $~lib/array/Array<String>#__get
(call $~lib/array/Array<Array<String>>#__get
(get_local $0)
(i32.const 0)
)
(i32.const 1)
)
)
)
)
(func $std/array-access/stringArrayArrayMethodCall (; 14 ;) (type $ii) (param $0 i32) (result i32)
(return
(block (result i32)
(set_global $argumentCount
(i32.const 1)
)
(call $~lib/string/String#startsWith|trampoline
(call $~lib/array/Array<String>#__get
(call $~lib/array/Array<Array<String>>#__get
(get_local $0)
(i32.const 0)
)
(i32.const 1)
)
(i32.const 4)
(i32.const 0)
)
)
)
)
)