mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Simplify resolve infrastructure; Fix handling of nested element and property accesses
This commit is contained in:
parent
e790eb757f
commit
7e90ab161d
2
dist/asc.js
vendored
2
dist/asc.js
vendored
File diff suppressed because one or more lines are too long
2
dist/asc.js.map
vendored
2
dist/asc.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js
vendored
2
dist/assemblyscript.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js.map
vendored
2
dist/assemblyscript.js.map
vendored
File diff suppressed because one or more lines are too long
296
src/binary.ts
Normal file
296
src/binary.ts
Normal 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();
|
||||
}
|
||||
}
|
396
src/compiler.ts
396
src/compiler.ts
@ -31,7 +31,6 @@ import {
|
||||
Program,
|
||||
ClassPrototype,
|
||||
Class,
|
||||
Element,
|
||||
ElementKind,
|
||||
Enum,
|
||||
Field,
|
||||
@ -4045,58 +4044,55 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): ExpressionRef {
|
||||
var program = this.program;
|
||||
var currentFunction = this.currentFunction;
|
||||
var resolved = this.program.resolveExpression(expression, currentFunction); // reports
|
||||
if (!resolved) return this.module.createUnreachable();
|
||||
var target = program.resolveExpression(expression, currentFunction); // reports
|
||||
if (!target) return this.module.createUnreachable();
|
||||
|
||||
// to compile just the value, we need to know the target's type
|
||||
var element = resolved.element;
|
||||
var elementType: Type;
|
||||
switch (element.kind) {
|
||||
switch (target.kind) {
|
||||
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();
|
||||
}
|
||||
assert((<Global>element).type != Type.void, "concrete type expected");
|
||||
assert((<Global>target).type != Type.void); // compileGlobal must guarantee this
|
||||
// fall-through
|
||||
}
|
||||
case ElementKind.LOCAL:
|
||||
case ElementKind.FIELD: {
|
||||
elementType = (<VariableLikeElement>element).type;
|
||||
elementType = (<VariableLikeElement>target).type;
|
||||
break;
|
||||
}
|
||||
case ElementKind.PROPERTY: {
|
||||
let prototype = (<Property>element).setterPrototype;
|
||||
let prototype = (<Property>target).setterPrototype;
|
||||
if (prototype) {
|
||||
let instance = prototype.resolve(); // reports
|
||||
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];
|
||||
break;
|
||||
}
|
||||
this.error(
|
||||
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();
|
||||
}
|
||||
case ElementKind.FUNCTION_PROTOTYPE: {
|
||||
if (expression.kind == NodeKind.ELEMENTACCESS) { // @operator("[]")
|
||||
if (resolved.target && resolved.target.kind == ElementKind.CLASS) {
|
||||
if (element.simpleName == (<Class>resolved.target).prototype.fnIndexedGet) {
|
||||
let resolvedIndexedSet = (<FunctionPrototype>element).resolve(null); // reports
|
||||
if (resolvedIndexedSet) {
|
||||
elementType = resolvedIndexedSet.signature.returnType;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
expression.range, (<Class>resolved.target).toString()
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
case ElementKind.CLASS: {
|
||||
if (program.resolvedElementExpression) { // indexed access
|
||||
let indexedGetPrototype = (<Class>target).getIndexedGet();
|
||||
if (indexedGetPrototype) {
|
||||
let indexedGetInstance = indexedGetPrototype.resolve(); // reports
|
||||
if (!indexedGetInstance) return this.module.createUnreachable();
|
||||
elementType = indexedGetInstance.signature.returnType;
|
||||
break;
|
||||
}
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
expression.range, (<Class>target).toString()
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
// fall-through
|
||||
}
|
||||
@ -4124,62 +4120,61 @@ export class Compiler extends DiagnosticEmitter {
|
||||
tee: bool = false
|
||||
): ExpressionRef {
|
||||
var module = this.module;
|
||||
var resolved = this.program.resolveExpression(expression, this.currentFunction); // reports
|
||||
if (!resolved) return module.createUnreachable();
|
||||
var target = this.program.resolveExpression(expression, this.currentFunction); // reports
|
||||
if (!target) return module.createUnreachable();
|
||||
|
||||
var element = resolved.element;
|
||||
switch (element.kind) {
|
||||
switch (target.kind) {
|
||||
case ElementKind.LOCAL: {
|
||||
this.currentType = tee ? (<Local>element).type : Type.void;
|
||||
if ((<Local>element).is(CommonFlags.CONST)) {
|
||||
this.currentType = tee ? (<Local>target).type : Type.void;
|
||||
if ((<Local>target).is(CommonFlags.CONST)) {
|
||||
this.error(
|
||||
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 tee
|
||||
? module.createTeeLocal((<Local>element).index, valueWithCorrectType)
|
||||
: module.createSetLocal((<Local>element).index, valueWithCorrectType);
|
||||
? module.createTeeLocal((<Local>target).index, valueWithCorrectType)
|
||||
: module.createSetLocal((<Local>target).index, valueWithCorrectType);
|
||||
}
|
||||
case ElementKind.GLOBAL: {
|
||||
if (!this.compileGlobal(<Global>element)) return module.createUnreachable();
|
||||
let type = (<Global>element).type;
|
||||
if (!this.compileGlobal(<Global>target)) return module.createUnreachable();
|
||||
let type = (<Global>target).type;
|
||||
assert(type != Type.void);
|
||||
this.currentType = tee ? type : Type.void;
|
||||
if ((<Local>element).is(CommonFlags.CONST)) {
|
||||
if ((<Local>target).is(CommonFlags.CONST)) {
|
||||
this.error(
|
||||
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
|
||||
expression.range,
|
||||
(<Local>element).internalName
|
||||
target.internalName
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
if (tee) {
|
||||
let nativeType = type.toNativeType();
|
||||
let internalName = (<Global>element).internalName;
|
||||
let internalName = target.internalName;
|
||||
return module.createBlock(null, [ // emulated teeGlobal
|
||||
module.createSetGlobal(internalName, valueWithCorrectType),
|
||||
module.createGetGlobal(internalName, nativeType)
|
||||
], nativeType);
|
||||
} else {
|
||||
return module.createSetGlobal((<Global>element).internalName, valueWithCorrectType);
|
||||
return module.createSetGlobal(target.internalName, valueWithCorrectType);
|
||||
}
|
||||
}
|
||||
case ElementKind.FIELD: {
|
||||
if ((<Field>element).is(CommonFlags.READONLY)) {
|
||||
if ((<Field>target).is(CommonFlags.READONLY)) {
|
||||
this.error(
|
||||
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();
|
||||
}
|
||||
assert(resolved.isInstanceTarget);
|
||||
let targetExpr = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
(<Class>resolved.target).type
|
||||
let thisExpression = assert(this.program.resolvedThisExpression);
|
||||
let thisExpr = this.compileExpressionRetainType(
|
||||
thisExpression,
|
||||
this.options.usizeType
|
||||
);
|
||||
let type = (<Field>element).type;
|
||||
let type = (<Field>target).type;
|
||||
this.currentType = tee ? type : Type.void;
|
||||
let nativeType = type.toNativeType();
|
||||
if (tee) {
|
||||
@ -4190,25 +4185,25 @@ export class Compiler extends DiagnosticEmitter {
|
||||
module.createSetLocal(tempLocalIndex, valueWithCorrectType),
|
||||
module.createStore(
|
||||
type.size >> 3,
|
||||
targetExpr,
|
||||
thisExpr,
|
||||
module.createGetLocal(tempLocalIndex, nativeType),
|
||||
nativeType,
|
||||
(<Field>element).memoryOffset
|
||||
(<Field>target).memoryOffset
|
||||
),
|
||||
module.createGetLocal(tempLocalIndex, nativeType)
|
||||
], nativeType);
|
||||
} else {
|
||||
return module.createStore(
|
||||
type.size >> 3,
|
||||
targetExpr,
|
||||
thisExpr,
|
||||
valueWithCorrectType,
|
||||
nativeType,
|
||||
(<Field>element).memoryOffset
|
||||
(<Field>target).memoryOffset
|
||||
);
|
||||
}
|
||||
}
|
||||
case ElementKind.PROPERTY: {
|
||||
let setterPrototype = (<Property>element).setterPrototype;
|
||||
let setterPrototype = (<Property>target).setterPrototype;
|
||||
if (setterPrototype) {
|
||||
let setterInstance = setterPrototype.resolve(); // reports
|
||||
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
|
||||
if (!tee) {
|
||||
if (setterInstance.is(CommonFlags.INSTANCE)) {
|
||||
assert(resolved.isInstanceTarget);
|
||||
let thisArg = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
(<Class>resolved.target).type
|
||||
let thisExpression = assert(this.program.resolvedThisExpression);
|
||||
let thisExpr = this.compileExpressionRetainType(
|
||||
thisExpression,
|
||||
this.options.usizeType
|
||||
);
|
||||
return this.makeCallDirect(setterInstance, [ thisArg, valueWithCorrectType ]);
|
||||
return this.makeCallDirect(setterInstance, [ thisExpr, valueWithCorrectType ]);
|
||||
} else {
|
||||
return this.makeCallDirect(setterInstance, [ valueWithCorrectType ]);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
let getterInstance = (<FunctionPrototype>getterPrototype).resolve(); // reports
|
||||
if (!getterInstance) return module.createUnreachable();
|
||||
let returnType = getterInstance.signature.returnType;
|
||||
let nativeReturnType = returnType.toNativeType();
|
||||
if (setterInstance.is(CommonFlags.INSTANCE)) {
|
||||
assert(resolved.isInstanceTarget);
|
||||
let thisArg = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
(<Class>resolved.target).type
|
||||
let thisExpression = assert(this.program.resolvedThisExpression);
|
||||
let thisExpr = this.compileExpressionRetainType(
|
||||
thisExpression,
|
||||
this.options.usizeType
|
||||
);
|
||||
let tempLocal = this.currentFunction.getAndFreeTempLocal(returnType);
|
||||
let tempLocalIndex = tempLocal.index;
|
||||
return module.createBlock(null, [
|
||||
this.makeCallDirect(setterInstance, [ // set and remember the target
|
||||
module.createTeeLocal(tempLocalIndex, thisArg),
|
||||
module.createTeeLocal(tempLocalIndex, thisExpr),
|
||||
valueWithCorrectType
|
||||
]),
|
||||
this.makeCallDirect(getterInstance, [ // get from remembered target
|
||||
@ -4261,63 +4256,66 @@ export class Compiler extends DiagnosticEmitter {
|
||||
} else {
|
||||
this.error(
|
||||
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();
|
||||
}
|
||||
case ElementKind.FUNCTION_PROTOTYPE: { // @operator("[]") ?
|
||||
if (expression.kind == NodeKind.ELEMENTACCESS) {
|
||||
assert(resolved.isInstanceTarget);
|
||||
let getterInstance = (<FunctionPrototype>element).resolve(); // reports
|
||||
if (!getterInstance) return module.createUnreachable();
|
||||
// obtain @operator("[]=")
|
||||
let setElementName = (<Class>resolved.target).prototype.fnIndexedSet;
|
||||
let setElement: Element | null;
|
||||
if (
|
||||
setElementName != null &&
|
||||
(<Class>resolved.target).members &&
|
||||
(setElement = (<Map<string,Element>>(<Class>resolved.target).members).get(setElementName)) &&
|
||||
setElement.kind == ElementKind.FUNCTION_PROTOTYPE
|
||||
) {
|
||||
let setterInstance = (<FunctionPrototype>setElement).resolve(); // reports
|
||||
if (!setterInstance) return module.createUnreachable();
|
||||
let targetType = (<Class>resolved.target).type;
|
||||
let targetExpr = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
targetType
|
||||
case ElementKind.CLASS: {
|
||||
let elementExpression = this.program.resolvedElementExpression;
|
||||
if (elementExpression) {
|
||||
let indexedGetPrototype = (<Class>target).getIndexedGet();
|
||||
if (indexedGetPrototype) {
|
||||
let indexedGetInstance = indexedGetPrototype.resolve(); // reports
|
||||
if (!indexedGetInstance) return module.createUnreachable();
|
||||
let indexedSetPrototype = (<Class>target).getIndexedSet();
|
||||
if (!indexedSetPrototype) {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_in_type_0_only_permits_reading,
|
||||
expression.range, target.internalName
|
||||
);
|
||||
this.currentType = tee ? indexedGetInstance.signature.returnType : Type.void;
|
||||
return module.createUnreachable();
|
||||
}
|
||||
let indexedSetInstance = indexedSetPrototype.resolve(); // reports
|
||||
if (!indexedSetInstance) return module.createUnreachable();
|
||||
let targetType = (<Class>target).type;
|
||||
let thisExpression = assert(this.program.resolvedThisExpression);
|
||||
let thisExpr = this.compileExpressionRetainType(
|
||||
thisExpression,
|
||||
this.options.usizeType
|
||||
);
|
||||
let elementExpr = this.compileExpression(
|
||||
(<ElementAccessExpression>expression).elementExpression,
|
||||
elementExpression,
|
||||
Type.i32
|
||||
);
|
||||
if (tee) {
|
||||
let tempLocalTarget = this.currentFunction.getTempLocal(targetType);
|
||||
let tempLocalElement = this.currentFunction.getAndFreeTempLocal(this.currentType);
|
||||
let returnType = getterInstance.signature.returnType;
|
||||
let returnType = indexedGetInstance.signature.returnType;
|
||||
this.currentFunction.freeTempLocal(tempLocalTarget);
|
||||
return module.createBlock(null, [
|
||||
this.makeCallDirect(setterInstance, [
|
||||
module.createTeeLocal(tempLocalTarget.index, targetExpr),
|
||||
this.makeCallDirect(indexedSetInstance, [
|
||||
module.createTeeLocal(tempLocalTarget.index, thisExpr),
|
||||
module.createTeeLocal(tempLocalElement.index, elementExpr),
|
||||
valueWithCorrectType
|
||||
]),
|
||||
this.makeCallDirect(getterInstance, [
|
||||
this.makeCallDirect(indexedGetInstance, [
|
||||
module.createGetLocal(tempLocalTarget.index, tempLocalTarget.type.toNativeType()),
|
||||
module.createGetLocal(tempLocalElement.index, tempLocalElement.type.toNativeType())
|
||||
])
|
||||
], returnType.toNativeType());
|
||||
} else {
|
||||
return this.makeCallDirect(setterInstance, [
|
||||
targetExpr,
|
||||
return this.makeCallDirect(indexedSetInstance, [
|
||||
thisExpr,
|
||||
elementExpr,
|
||||
valueWithCorrectType
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_in_type_0_only_permits_reading,
|
||||
expression.range, (<Class>resolved.target).internalName
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
expression.range, target.internalName
|
||||
);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
@ -4335,17 +4333,16 @@ export class Compiler extends DiagnosticEmitter {
|
||||
compileCallExpression(expression: CallExpression, contextualType: Type): ExpressionRef {
|
||||
var module = this.module;
|
||||
var currentFunction = this.currentFunction;
|
||||
var resolved = this.program.resolveExpression(expression.expression, currentFunction); // reports
|
||||
if (!resolved) return module.createUnreachable();
|
||||
var target = this.program.resolveExpression(expression.expression, currentFunction); // reports
|
||||
if (!target) return module.createUnreachable();
|
||||
|
||||
var element = resolved.element;
|
||||
var signature: Signature | null;
|
||||
var indexArg: ExpressionRef;
|
||||
switch (element.kind) {
|
||||
switch (target.kind) {
|
||||
|
||||
// direct call: concrete function
|
||||
case ElementKind.FUNCTION_PROTOTYPE: {
|
||||
let prototype = <FunctionPrototype>element;
|
||||
let prototype = <FunctionPrototype>target;
|
||||
|
||||
// builtins are compiled on the fly
|
||||
if (prototype.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
|
||||
@ -4377,69 +4374,72 @@ export class Compiler extends DiagnosticEmitter {
|
||||
expression
|
||||
);
|
||||
if (!instance) return module.createUnreachable();
|
||||
let thisArg: ExpressionRef = 0;
|
||||
if (instance.is(CommonFlags.INSTANCE)) {
|
||||
assert(resolved.isInstanceTarget);
|
||||
thisArg = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
(<Class>resolved.target).type
|
||||
let thisExpression = assert(this.program.resolvedThisExpression);
|
||||
let thisExpr = this.compileExpressionRetainType(
|
||||
thisExpression,
|
||||
this.options.usizeType
|
||||
);
|
||||
if (!thisArg) return module.createUnreachable();
|
||||
return this.compileCallDirect(instance, expression.arguments, expression, thisExpr);
|
||||
} 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
|
||||
case ElementKind.LOCAL: {
|
||||
if (signature = (<Local>element).type.signatureReference) {
|
||||
indexArg = module.createGetLocal((<Local>element).index, NativeType.I32);
|
||||
if (signature = (<Local>target).type.signatureReference) {
|
||||
indexArg = module.createGetLocal((<Local>target).index, NativeType.I32);
|
||||
break;
|
||||
} else {
|
||||
this.error(
|
||||
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();
|
||||
}
|
||||
}
|
||||
case ElementKind.GLOBAL: {
|
||||
if (signature = (<Global>element).type.signatureReference) {
|
||||
indexArg = module.createGetGlobal((<Global>element).internalName, (<Global>element).type.toNativeType());
|
||||
if (signature = (<Global>target).type.signatureReference) {
|
||||
indexArg = module.createGetGlobal((<Global>target).internalName, (<Global>target).type.toNativeType());
|
||||
break;
|
||||
} else {
|
||||
this.error(
|
||||
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();
|
||||
}
|
||||
}
|
||||
case ElementKind.FIELD: {
|
||||
let type = (<Field>element).type;
|
||||
let type = (<Field>target).type;
|
||||
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(
|
||||
4,
|
||||
false,
|
||||
targetExpr,
|
||||
thisExpr,
|
||||
NativeType.I32,
|
||||
(<Field>element).memoryOffset
|
||||
(<Field>target).memoryOffset
|
||||
);
|
||||
break;
|
||||
} else {
|
||||
this.error(
|
||||
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();
|
||||
}
|
||||
}
|
||||
case ElementKind.FUNCTION_TARGET: {
|
||||
signature = (<FunctionTarget>element).signature;
|
||||
indexArg = this.compileExpression(expression.expression, (<FunctionTarget>element).type);
|
||||
signature = (<FunctionTarget>target).signature;
|
||||
indexArg = this.compileExpression(expression.expression, (<FunctionTarget>target).type);
|
||||
break;
|
||||
}
|
||||
case ElementKind.PROPERTY: // TODO
|
||||
@ -4827,24 +4827,31 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): ExpressionRef {
|
||||
var resolved = this.program.resolveElementAccess(expression, this.currentFunction); // reports
|
||||
if (!resolved) return this.module.createUnreachable();
|
||||
|
||||
assert( // should be guaranteed by resolveElementAccess
|
||||
resolved.element.kind == ElementKind.FUNCTION_PROTOTYPE &&
|
||||
resolved.target &&
|
||||
resolved.target.kind == ElementKind.CLASS
|
||||
var target = this.program.resolveElementAccess(expression, this.currentFunction); // reports
|
||||
if (!target) return this.module.createUnreachable();
|
||||
switch (target.kind) {
|
||||
case ElementKind.CLASS: {
|
||||
let indexedGetPrototype = (<Class>target).getIndexedGet();
|
||||
if (!indexedGetPrototype) {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
expression.expression.range, (<Class>target).internalName
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
let indexedGetInstance = indexedGetPrototype.resolve(); // reports
|
||||
if (!indexedGetInstance) return this.module.createUnreachable();
|
||||
let thisArg = this.compileExpression(expression.expression, (<Class>target).type);
|
||||
return this.compileCallDirect(indexedGetInstance, [
|
||||
expression.elementExpression
|
||||
], expression, thisArg);
|
||||
}
|
||||
}
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
expression.range
|
||||
);
|
||||
var target = <Class>resolved.target;
|
||||
var instance = (<FunctionPrototype>resolved.element).resolve( // reports
|
||||
null,
|
||||
target.contextualTypeArguments
|
||||
);
|
||||
if (!instance) return this.module.createUnreachable();
|
||||
var thisArg = this.compileExpression(expression.expression, target.type);
|
||||
return this.compileCallDirect(instance, [
|
||||
expression.elementExpression
|
||||
], expression, thisArg);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
compileFunctionExpression(expression: FunctionExpression, contextualType: Type): ExpressionRef {
|
||||
@ -4956,42 +4963,41 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
// otherwise resolve
|
||||
var resolved = this.program.resolveIdentifier( // reports
|
||||
var target = this.program.resolveIdentifier( // reports
|
||||
expression,
|
||||
this.currentFunction,
|
||||
this.currentEnum
|
||||
);
|
||||
if (!resolved) return module.createUnreachable();
|
||||
if (!target) return module.createUnreachable();
|
||||
|
||||
var element = resolved.element;
|
||||
switch (element.kind) {
|
||||
switch (target.kind) {
|
||||
case ElementKind.LOCAL: {
|
||||
if ((<Local>element).is(CommonFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Local>element, contextualType, retainConstantType);
|
||||
if ((<Local>target).is(CommonFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Local>target, contextualType, retainConstantType);
|
||||
}
|
||||
let localType = (<Local>element).type;
|
||||
let localIndex = (<Local>element).index;
|
||||
let localType = (<Local>target).type;
|
||||
let localIndex = (<Local>target).index;
|
||||
assert(localIndex >= 0);
|
||||
this.currentType = localType;
|
||||
return this.module.createGetLocal(localIndex, localType.toNativeType());
|
||||
}
|
||||
case ElementKind.GLOBAL: {
|
||||
if (element.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
|
||||
return compileBuiltinGetConstant(this, <Global>element, expression);
|
||||
if (target.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
|
||||
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();
|
||||
}
|
||||
let globalType = (<Global>element).type;
|
||||
let globalType = (<Global>target).type;
|
||||
assert(globalType != Type.void);
|
||||
if ((<Global>element).is(CommonFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Global>element, contextualType, retainConstantType);
|
||||
if ((<Global>target).is(CommonFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Global>target, contextualType, retainConstantType);
|
||||
}
|
||||
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
|
||||
if (!element.is(CommonFlags.COMPILED)) {
|
||||
if (!target.is(CommonFlags.COMPILED)) {
|
||||
this.error(
|
||||
DiagnosticCode.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums,
|
||||
expression.range
|
||||
@ -5000,13 +5006,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
this.currentType = Type.i32;
|
||||
if ((<EnumValue>element).is(CommonFlags.INLINED)) {
|
||||
return this.module.createI32((<EnumValue>element).constantValue);
|
||||
if ((<EnumValue>target).is(CommonFlags.INLINED)) {
|
||||
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: {
|
||||
let instance = (<FunctionPrototype>element).resolve(
|
||||
let instance = (<FunctionPrototype>target).resolve(
|
||||
null,
|
||||
this.currentFunction.contextualTypeArguments
|
||||
);
|
||||
@ -5359,19 +5365,19 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var currentFunction = this.currentFunction;
|
||||
|
||||
// obtain the class being instantiated
|
||||
var resolved = this.program.resolveExpression( // reports
|
||||
var target = this.program.resolveExpression( // reports
|
||||
expression.expression,
|
||||
currentFunction
|
||||
);
|
||||
if (!resolved) return module.createUnreachable();
|
||||
if (resolved.element.kind != ElementKind.CLASS_PROTOTYPE) {
|
||||
if (!target) return module.createUnreachable();
|
||||
if (target.kind != ElementKind.CLASS_PROTOTYPE) {
|
||||
this.error(
|
||||
DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature,
|
||||
expression.expression.range
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
var classPrototype = <ClassPrototype>resolved.element;
|
||||
var classPrototype = <ClassPrototype>target;
|
||||
var classInstance = classPrototype.resolveUsingTypeArguments( // reports
|
||||
expression.typeArguments,
|
||||
null,
|
||||
@ -5424,56 +5430,53 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var program = this.program;
|
||||
var module = this.module;
|
||||
|
||||
var resolved = program.resolvePropertyAccess(propertyAccess, this.currentFunction); // reports
|
||||
if (!resolved) return module.createUnreachable();
|
||||
var target = program.resolvePropertyAccess(propertyAccess, this.currentFunction); // reports
|
||||
if (!target) return module.createUnreachable();
|
||||
|
||||
var element = resolved.element;
|
||||
var targetExpr: ExpressionRef;
|
||||
switch (element.kind) {
|
||||
switch (target.kind) {
|
||||
case ElementKind.GLOBAL: { // static property
|
||||
if (element.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
|
||||
return compileBuiltinGetConstant(this, <Global>element, propertyAccess);
|
||||
if (target.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
|
||||
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();
|
||||
}
|
||||
let globalType = (<Global>element).type;
|
||||
let globalType = (<Global>target).type;
|
||||
assert(globalType != Type.void);
|
||||
if ((<Global>element).is(CommonFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Global>element, contextualType, retainConstantType);
|
||||
if ((<Global>target).is(CommonFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Global>target, contextualType, retainConstantType);
|
||||
}
|
||||
this.currentType = globalType;
|
||||
return module.createGetGlobal((<Global>element).internalName, globalType.toNativeType());
|
||||
return module.createGetGlobal((<Global>target).internalName, globalType.toNativeType());
|
||||
}
|
||||
case ElementKind.ENUMVALUE: { // enum value
|
||||
if (!this.compileEnum((<EnumValue>element).enum)) {
|
||||
if (!this.compileEnum((<EnumValue>target).enum)) {
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
this.currentType = Type.i32;
|
||||
if ((<EnumValue>element).is(CommonFlags.INLINED)) {
|
||||
return module.createI32((<EnumValue>element).constantValue);
|
||||
if ((<EnumValue>target).is(CommonFlags.INLINED)) {
|
||||
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
|
||||
assert(resolved.isInstanceTarget);
|
||||
assert((<Field>element).memoryOffset >= 0);
|
||||
targetExpr = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
this.options.usizeType,
|
||||
ConversionKind.NONE
|
||||
let thisExpression = assert(program.resolvedThisExpression);
|
||||
assert((<Field>target).memoryOffset >= 0);
|
||||
let thisExpr = this.compileExpressionRetainType(
|
||||
thisExpression,
|
||||
this.options.usizeType
|
||||
);
|
||||
this.currentType = (<Field>element).type;
|
||||
this.currentType = (<Field>target).type;
|
||||
return module.createLoad(
|
||||
(<Field>element).type.size >> 3,
|
||||
(<Field>element).type.is(TypeFlags.SIGNED | TypeFlags.INTEGER),
|
||||
targetExpr,
|
||||
(<Field>element).type.toNativeType(),
|
||||
(<Field>element).memoryOffset
|
||||
(<Field>target).type.size >> 3,
|
||||
(<Field>target).type.is(TypeFlags.SIGNED | TypeFlags.INTEGER),
|
||||
thisExpr,
|
||||
(<Field>target).type.toNativeType(),
|
||||
(<Field>target).memoryOffset
|
||||
);
|
||||
}
|
||||
case ElementKind.PROPERTY: { // instance property (here: getter)
|
||||
let prototype = (<Property>element).getterPrototype;
|
||||
let prototype = (<Property>target).getterPrototype;
|
||||
if (prototype) {
|
||||
let instance = prototype.resolve(null); // reports
|
||||
if (!instance) return module.createUnreachable();
|
||||
@ -5489,12 +5492,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (instance.is(CommonFlags.INSTANCE)) {
|
||||
let parent = assert(instance.memberOf);
|
||||
assert(parent.kind == ElementKind.CLASS);
|
||||
targetExpr = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
(<Class>parent).type
|
||||
let thisExpression = assert(program.resolvedThisExpression);
|
||||
let thisExpr = this.compileExpressionRetainType(
|
||||
thisExpression,
|
||||
this.options.usizeType
|
||||
);
|
||||
this.currentType = signature.returnType;
|
||||
return this.compileCallDirect(instance, [], propertyAccess, targetExpr);
|
||||
return this.compileCallDirect(instance, [], propertyAccess, thisExpr);
|
||||
} else {
|
||||
this.currentType = signature.returnType;
|
||||
return this.compileCallDirect(instance, [], propertyAccess);
|
||||
@ -5502,7 +5506,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
} else {
|
||||
this.error(
|
||||
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();
|
||||
}
|
||||
|
296
src/program.ts
296
src/program.ts
@ -137,6 +137,11 @@ export class Program extends DiagnosticEmitter {
|
||||
/** String instance reference. */
|
||||
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. */
|
||||
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
||||
super(diagnostics);
|
||||
@ -289,17 +294,14 @@ export class Program extends DiagnosticEmitter {
|
||||
for (let i = 0, k = queuedDerivedClasses.length; i < k; ++i) {
|
||||
let derivedDeclaration = queuedDerivedClasses[i].declaration;
|
||||
let derivedType = assert(derivedDeclaration.extendsType);
|
||||
let resolved = this.resolveIdentifier(derivedType.name, null);
|
||||
if (resolved) {
|
||||
if (resolved.element.kind != ElementKind.CLASS_PROTOTYPE) {
|
||||
this.error(
|
||||
DiagnosticCode.A_class_may_only_extend_another_class,
|
||||
derivedType.range
|
||||
);
|
||||
continue;
|
||||
}
|
||||
queuedDerivedClasses[i].basePrototype = (
|
||||
<ClassPrototype>resolved.element
|
||||
let derived = this.resolveIdentifier(derivedType.name, null); // reports
|
||||
if (!derived) continue;
|
||||
if (derived.kind == ElementKind.CLASS_PROTOTYPE) {
|
||||
queuedDerivedClasses[i].basePrototype = <ClassPrototype>derived;
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.A_class_may_only_extend_another_class,
|
||||
derivedType.range
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1709,7 +1711,7 @@ export class Program extends DiagnosticEmitter {
|
||||
identifier: IdentifierExpression,
|
||||
contextualFunction: Function | null,
|
||||
contextualEnum: Enum | null = null
|
||||
): ResolvedElement | null {
|
||||
): Element | null {
|
||||
var name = identifier.text;
|
||||
|
||||
var element: Element | null;
|
||||
@ -1723,16 +1725,18 @@ export class Program extends DiagnosticEmitter {
|
||||
(element = contextualEnum.members.get(name)) &&
|
||||
element.kind == ElementKind.ENUMVALUE
|
||||
) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(element);
|
||||
this.resolvedThisExpression = null;
|
||||
this.resolvedElementExpression = null;
|
||||
return element; // ENUMVALUE
|
||||
}
|
||||
|
||||
} else if (contextualFunction) {
|
||||
|
||||
// check locals
|
||||
if (element = contextualFunction.flow.getScopedLocal(name)) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(element);
|
||||
this.resolvedThisExpression = null;
|
||||
this.resolvedElementExpression = null;
|
||||
return element; // LOCAL
|
||||
}
|
||||
|
||||
// check outer scope locals
|
||||
@ -1752,8 +1756,9 @@ export class Program extends DiagnosticEmitter {
|
||||
if (namespace = contextualFunction.prototype.namespace) {
|
||||
do {
|
||||
if (element = this.elementsLookup.get(namespace.internalName + STATIC_DELIMITER + name)) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(element);
|
||||
this.resolvedThisExpression = null;
|
||||
this.resolvedElementExpression = null;
|
||||
return element; // LOCAL
|
||||
}
|
||||
} while (namespace = namespace.namespace);
|
||||
}
|
||||
@ -1761,14 +1766,16 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
// search current file
|
||||
if (element = this.elementsLookup.get(identifier.range.source.internalPath + PATH_DELIMITER + name)) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(element);
|
||||
this.resolvedThisExpression = null;
|
||||
this.resolvedElementExpression = null;
|
||||
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
|
||||
}
|
||||
|
||||
// search global scope
|
||||
if (element = this.elementsLookup.get(name)) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(element);
|
||||
this.resolvedThisExpression = null;
|
||||
this.resolvedElementExpression = null;
|
||||
return element; // GLOBAL, FUNCTION_PROTOTYPE, CLASS_PROTOTYPE
|
||||
}
|
||||
|
||||
this.error(
|
||||
@ -1782,47 +1789,63 @@ export class Program extends DiagnosticEmitter {
|
||||
resolvePropertyAccess(
|
||||
propertyAccess: PropertyAccessExpression,
|
||||
contextualFunction: Function
|
||||
): ResolvedElement | null {
|
||||
): Element | null {
|
||||
// start by resolving the lhs target (expression before the last dot)
|
||||
var targetExpression = propertyAccess.expression;
|
||||
resolvedElement = this.resolveExpression( // reports
|
||||
targetExpression,
|
||||
contextualFunction
|
||||
);
|
||||
if (!resolvedElement) return null;
|
||||
var target = resolvedElement.element;
|
||||
var target = this.resolveExpression(targetExpression, contextualFunction); // reports
|
||||
if (!target) return null;
|
||||
|
||||
// at this point we know exactly what the target is, so look up the element within
|
||||
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) {
|
||||
case ElementKind.GLOBAL:
|
||||
case ElementKind.LOCAL:
|
||||
case ElementKind.FIELD: {
|
||||
if (!(targetType = (<VariableLikeElement>target).type).classReference) {
|
||||
let classReference = (<VariableLikeElement>target).type.classReference;
|
||||
if (!classReference) {
|
||||
this.error(
|
||||
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;
|
||||
}
|
||||
target = <Class>targetType.classReference;
|
||||
target = classReference;
|
||||
break;
|
||||
}
|
||||
case ElementKind.PROPERTY: {
|
||||
let getter = assert((<Property>target).getterPrototype).resolve(); // reports
|
||||
if (!getter) return null;
|
||||
if (!(targetType = getter.signature.returnType).classReference) {
|
||||
let classReference = getter.signature.returnType.classReference;
|
||||
if (!classReference) {
|
||||
this.error(
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1832,17 +1855,21 @@ export class Program extends DiagnosticEmitter {
|
||||
case ElementKind.CLASS_PROTOTYPE:
|
||||
case ElementKind.CLASS: {
|
||||
do {
|
||||
if (target.members && (member = target.members.get(propertyName))) {
|
||||
return resolvedElement.set(member).withTarget(target, targetExpression);
|
||||
let members = target.members;
|
||||
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 ((<ClassPrototype>target).basePrototype) {
|
||||
target = <ClassPrototype>(<ClassPrototype>target).basePrototype;
|
||||
} else {
|
||||
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) {
|
||||
if ((<Class>target).base) {
|
||||
target = <Class>(<Class>target).base;
|
||||
@ -1856,8 +1883,12 @@ export class Program extends DiagnosticEmitter {
|
||||
break;
|
||||
}
|
||||
default: { // enums or other namespace-like elements
|
||||
if (target.members && (member = target.members.get(propertyName))) {
|
||||
return resolvedElement.set(member).withTarget(target, targetExpression);
|
||||
let members = target.members;
|
||||
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;
|
||||
}
|
||||
@ -1872,39 +1903,41 @@ export class Program extends DiagnosticEmitter {
|
||||
resolveElementAccess(
|
||||
elementAccess: ElementAccessExpression,
|
||||
contextualFunction: Function
|
||||
): ResolvedElement | null {
|
||||
// start by resolving the lhs target
|
||||
): Element | null {
|
||||
var targetExpression = elementAccess.expression;
|
||||
resolvedElement = this.resolveExpression(
|
||||
targetExpression,
|
||||
contextualFunction
|
||||
);
|
||||
if (!resolvedElement) return null;
|
||||
var target = resolvedElement.element;
|
||||
var target = this.resolveExpression(targetExpression, contextualFunction);
|
||||
if (!target) return null;
|
||||
switch (target.kind) {
|
||||
case ElementKind.GLOBAL:
|
||||
case ElementKind.LOCAL:
|
||||
case ElementKind.FIELD: {
|
||||
assert(!this.resolvedThisExpression && !this.resolvedElementExpression);
|
||||
let type = (<VariableLikeElement>target).type;
|
||||
if (type.classReference) {
|
||||
let indexedGetName = (target = type.classReference).prototype.fnIndexedGet;
|
||||
let indexedGet: Element | null;
|
||||
if (
|
||||
indexedGetName != null &&
|
||||
target.members &&
|
||||
(indexedGet = target.members.get(indexedGetName)) &&
|
||||
indexedGet.kind == ElementKind.FUNCTION_PROTOTYPE
|
||||
) {
|
||||
return resolvedElement.set(indexedGet).withTarget(type.classReference, targetExpression);
|
||||
if (target = type.classReference) {
|
||||
this.resolvedThisExpression = targetExpression;
|
||||
this.resolvedElementExpression = elementAccess.elementExpression;
|
||||
return target;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementKind.CLASS: { // element access on element access
|
||||
let indexedGetPrototype = (<Class>target).getIndexedGet();
|
||||
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;
|
||||
}
|
||||
// FIXME: indexed access on indexed access
|
||||
}
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
targetExpression.range, target.internalName
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
targetExpression.range
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@ -1912,7 +1945,7 @@ export class Program extends DiagnosticEmitter {
|
||||
resolveExpression(
|
||||
expression: Expression,
|
||||
contextualFunction: Function
|
||||
): ResolvedElement | null {
|
||||
): Element | null {
|
||||
while (expression.kind == NodeKind.PARENTHESIZED) {
|
||||
expression = (<ParenthesizedExpression>expression).expression;
|
||||
}
|
||||
@ -1922,8 +1955,9 @@ export class Program extends DiagnosticEmitter {
|
||||
if (type) {
|
||||
let classType = type.classReference;
|
||||
if (classType) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(classType);
|
||||
this.resolvedThisExpression = null;
|
||||
this.resolvedElementExpression = null;
|
||||
return classType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -1934,8 +1968,9 @@ export class Program extends DiagnosticEmitter {
|
||||
case NodeKind.THIS: { // -> Class / ClassPrototype
|
||||
let parent = contextualFunction.memberOf;
|
||||
if (parent) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(parent);
|
||||
this.resolvedThisExpression = null;
|
||||
this.resolvedElementExpression = null;
|
||||
return parent;
|
||||
}
|
||||
this.error(
|
||||
DiagnosticCode._this_cannot_be_referenced_in_current_location,
|
||||
@ -1946,8 +1981,9 @@ export class Program extends DiagnosticEmitter {
|
||||
case NodeKind.SUPER: { // -> Class
|
||||
let parent = contextualFunction.memberOf;
|
||||
if (parent && parent.kind == ElementKind.CLASS && (parent = (<Class>parent).base)) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(parent);
|
||||
this.resolvedThisExpression = null;
|
||||
this.resolvedElementExpression = null;
|
||||
return parent;
|
||||
}
|
||||
this.error(
|
||||
DiagnosticCode._super_can_only_be_referenced_in_a_derived_class,
|
||||
@ -1971,38 +2007,40 @@ export class Program extends DiagnosticEmitter {
|
||||
);
|
||||
}
|
||||
case NodeKind.CALL: {
|
||||
let resolved = this.resolveExpression(
|
||||
(<CallExpression>expression).expression,
|
||||
contextualFunction
|
||||
);
|
||||
if (resolved) {
|
||||
let element = resolved.element;
|
||||
if (element && element.kind == ElementKind.FUNCTION_PROTOTYPE) {
|
||||
let instance = (<FunctionPrototype>element).resolveUsingTypeArguments(
|
||||
(<CallExpression>expression).typeArguments,
|
||||
contextualFunction.contextualTypeArguments,
|
||||
expression
|
||||
);
|
||||
if (instance) {
|
||||
let returnType = instance.signature.returnType;
|
||||
let classType = returnType.classReference;
|
||||
if (classType) {
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(classType);
|
||||
} else {
|
||||
let signature = returnType.signatureReference;
|
||||
if (signature) {
|
||||
let functionTarget = signature.cachedFunctionTarget;
|
||||
if (!functionTarget) {
|
||||
functionTarget = new FunctionTarget(this, signature);
|
||||
signature.cachedFunctionTarget = functionTarget;
|
||||
}
|
||||
if (!resolvedElement) resolvedElement = new ResolvedElement();
|
||||
return resolvedElement.set(functionTarget);
|
||||
}
|
||||
let targetExpression = (<CallExpression>expression).expression;
|
||||
let target = this.resolveExpression(targetExpression, contextualFunction); // reports
|
||||
if (!target) return null;
|
||||
if (target.kind == ElementKind.FUNCTION_PROTOTYPE) {
|
||||
let instance = (<FunctionPrototype>target).resolveUsingTypeArguments( // reports
|
||||
(<CallExpression>expression).typeArguments,
|
||||
contextualFunction.contextualTypeArguments,
|
||||
expression
|
||||
);
|
||||
if (!instance) return null;
|
||||
let returnType = instance.signature.returnType;
|
||||
let classType = returnType.classReference;
|
||||
if (classType) {
|
||||
// reuse resolvedThisExpression (might be property access)
|
||||
// reuse resolvedElementExpression (might be element access)
|
||||
return classType;
|
||||
} else {
|
||||
let signature = returnType.signatureReference;
|
||||
if (signature) {
|
||||
let functionTarget = signature.cachedFunctionTarget;
|
||||
if (!functionTarget) {
|
||||
functionTarget = new FunctionTarget(this, signature);
|
||||
signature.cachedFunctionTarget = functionTarget;
|
||||
}
|
||||
// reuse resolvedThisExpression (might be property access)
|
||||
// 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;
|
||||
}
|
||||
@ -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}. */
|
||||
export enum ElementKind {
|
||||
/** A {@link Global}. */
|
||||
@ -3264,6 +3264,24 @@ export class Class extends Element {
|
||||
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 {
|
||||
return this.simpleName;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
"./**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"./binary.ts",
|
||||
"./extra/**",
|
||||
"./glue/wasm/**"
|
||||
]
|
||||
|
@ -1,5 +1,14 @@
|
||||
/** @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 {
|
||||
buffer[offset ] = value;
|
||||
buffer[offset + 1] = value >>> 8;
|
||||
@ -7,15 +16,35 @@ export function writeI32(value: i32, buffer: Uint8Array, offset: i32): void {
|
||||
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 {
|
||||
writeI32(i64_low(value), buffer, offset);
|
||||
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 {
|
||||
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 {
|
||||
var valueI64 = f64_as_i64(value);
|
||||
writeI32(i64_low(valueI64), buffer, offset);
|
||||
|
330
tests/compiler/std/array-access.optimized.wat
Normal file
330
tests/compiler/std/array-access.optimized.wat
Normal 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)
|
||||
)
|
||||
)
|
||||
)
|
19
tests/compiler/std/array-access.ts
Normal file
19
tests/compiler/std/array-access.ts
Normal 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("");
|
||||
}
|
450
tests/compiler/std/array-access.untouched.wat
Normal file
450
tests/compiler/std/array-access.untouched.wat
Normal 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)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user