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

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

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

296
src/binary.ts Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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