mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-26 15:32:16 +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();
|
||||||
|
}
|
||||||
|
}
|
382
src/compiler.ts
382
src/compiler.ts
@ -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();
|
||||||
}
|
}
|
||||||
|
262
src/program.ts
262
src/program.ts
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"./**/*.ts"
|
"./**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
|
"./binary.ts",
|
||||||
"./extra/**",
|
"./extra/**",
|
||||||
"./glue/wasm/**"
|
"./glue/wasm/**"
|
||||||
]
|
]
|
||||||
|
@ -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);
|
||||||
|
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