Skip to content

Implement virtual overloading / interfaces #1208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
"prepublishOnly": "node scripts/prepublish",
"postpublish": "node scripts/postpublish",
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
"asbuild:untouched": "node bin/asc src/glue/wasm/index.ts src/index.ts -t out/assemblyscript.untouched.wat -b out/assemblyscript.untouched.wasm -d out/assemblyscript.d.ts --validate --debug --measure",
"asbuild:optimized": "node bin/asc src/glue/wasm/index.ts src/index.ts -t out/assemblyscript.optimized.wat -b out/assemblyscript.optimized.wasm -O3 --validate --measure",
"asbuild:untouched": "node bin/asc src/glue/wasm/index.ts src/index.ts -t out/assemblyscript.untouched.wat -b out/assemblyscript.untouched.wasm -d out/assemblyscript.d.ts --validate --debug --measure --runtime stub",
"asbuild:optimized": "node bin/asc src/glue/wasm/index.ts src/index.ts -t out/assemblyscript.optimized.wat -b out/assemblyscript.optimized.wasm -O3 --validate --measure --runtime stub",
"astest": "ts-node tests/bootstrap"
},
"releaseFiles": [
Expand Down
10 changes: 10 additions & 0 deletions src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,16 @@ export abstract class Node {
}
return false;
}

/** Checks if this is a call calling a method on super. */
get isCallOnSuper(): bool {
if (this.kind != NodeKind.CALL) return false;
var expression = changetype<CallExpression>(this).expression;
if (expression.kind != NodeKind.PROPERTYACCESS) return false;
var target = (<PropertyAccessExpression>expression).expression;
if (target.kind == NodeKind.SUPER) return true;
return false;
}
}

// types
Expand Down
400 changes: 297 additions & 103 deletions src/compiler.ts

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions src/diagnosticMessages.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export enum DiagnosticCode {
_0_is_not_a_valid_operator = 224,
Expression_cannot_be_represented_by_a_type = 225,
Expression_resolves_to_unusual_type_0 = 226,
Function_0_is_virtual_and_will_not_be_inlined = 227,
Property_0_only_has_a_setter_and_is_missing_a_getter = 228,
_0_keyword_cannot_be_used_here = 229,
Type_0_is_cyclic_Module_will_include_deferred_garbage_collection = 900,
Importing_the_table_disables_some_indirect_call_optimizations = 901,
Exporting_the_table_disables_some_indirect_call_optimizations = 902,
Expand Down Expand Up @@ -107,6 +110,7 @@ export enum DiagnosticCode {
Duplicate_identifier_0 = 2300,
Cannot_find_name_0 = 2304,
Module_0_has_no_exported_member_1 = 2305,
An_interface_can_only_extend_an_interface = 2312,
Generic_type_0_requires_1_type_argument_s = 2314,
Type_0_is_not_generic = 2315,
Type_0_is_not_assignable_to_type_1 = 2322,
Expand All @@ -123,19 +127,25 @@ export enum DiagnosticCode {
Operator_0_cannot_be_applied_to_types_1_and_2 = 2365,
A_super_call_must_be_the_first_statement_in_the_constructor = 2376,
Constructors_for_derived_classes_must_contain_a_super_call = 2377,
Getter_and_setter_accessors_do_not_agree_in_visibility = 2379,
_get_and_set_accessor_must_have_the_same_type = 2380,
Overload_signatures_must_all_be_public_private_or_protected = 2385,
Constructor_implementation_is_missing = 2390,
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391,
Multiple_constructor_implementations_are_not_allowed = 2392,
Duplicate_function_implementation = 2393,
This_overload_signature_is_not_compatible_with_its_implementation_signature = 2394,
Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local = 2395,
A_class_can_only_implement_an_interface = 2422,
A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged = 2434,
The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly = 2453,
Type_0_has_no_property_1 = 2460,
The_0_operator_cannot_be_applied_to_type_1 = 2469,
In_const_enum_declarations_member_initializer_must_be_constant_expression = 2474,
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
_0_is_referenced_directly_or_indirectly_in_its_own_base_expression = 2506,
Cannot_create_an_instance_of_an_abstract_class = 2511,
Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_2 = 2515,
Object_is_possibly_null = 2531,
Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540,
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
Expand Down Expand Up @@ -194,6 +204,9 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 224: return "'{0}' is not a valid operator.";
case 225: return "Expression cannot be represented by a type.";
case 226: return "Expression resolves to unusual type '{0}'.";
case 227: return "Function '{0}' is virtual and will not be inlined.";
case 228: return "Property '{0}' only has a setter and is missing a getter.";
case 229: return "'{0}' keyword cannot be used here.";
case 900: return "Type '{0}' is cyclic. Module will include deferred garbage collection.";
case 901: return "Importing the table disables some indirect call optimizations.";
case 902: return "Exporting the table disables some indirect call optimizations.";
Expand Down Expand Up @@ -261,6 +274,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2300: return "Duplicate identifier '{0}'.";
case 2304: return "Cannot find name '{0}'.";
case 2305: return "Module '{0}' has no exported member '{1}'.";
case 2312: return "An interface can only extend an interface.";
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
case 2315: return "Type '{0}' is not generic.";
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
Expand All @@ -277,19 +291,25 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2365: return "Operator '{0}' cannot be applied to types '{1}' and '{2}'.";
case 2376: return "A 'super' call must be the first statement in the constructor.";
case 2377: return "Constructors for derived classes must contain a 'super' call.";
case 2379: return "Getter and setter accessors do not agree in visibility.";
case 2380: return "'get' and 'set' accessor must have the same type.";
case 2385: return "Overload signatures must all be public, private or protected.";
case 2390: return "Constructor implementation is missing.";
case 2391: return "Function implementation is missing or not immediately following the declaration.";
case 2392: return "Multiple constructor implementations are not allowed.";
case 2393: return "Duplicate function implementation.";
case 2394: return "This overload signature is not compatible with its implementation signature.";
case 2395: return "Individual declarations in merged declaration '{0}' must be all exported or all local.";
case 2422: return "A class can only implement an interface.";
case 2434: return "A namespace declaration cannot be located prior to a class or function with which it is merged.";
case 2453: return "The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly.";
case 2460: return "Type '{0}' has no property '{1}'.";
case 2469: return "The '{0}' operator cannot be applied to type '{1}'.";
case 2474: return "In 'const' enum declarations member initializer must be constant expression.";
case 2484: return "Export declaration conflicts with exported declaration of '{0}'.";
case 2506: return "'{0}' is referenced directly or indirectly in its own base expression.";
case 2511: return "Cannot create an instance of an abstract class.";
case 2515: return "Non-abstract class '{0}' does not implement inherited abstract member '{1}' from '{2}'.";
case 2531: return "Object is possibly 'null'.";
case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property.";
case 2541: return "The target of an assignment must be a variable or a property access.";
Expand Down
10 changes: 10 additions & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"'{0}' is not a valid operator.": 224,
"Expression cannot be represented by a type.": 225,
"Expression resolves to unusual type '{0}'.": 226,
"Function '{0}' is virtual and will not be inlined.": 227,
"Property '{0}' only has a setter and is missing a getter.": 228,
"'{0}' keyword cannot be used here.": 229,

"Type '{0}' is cyclic. Module will include deferred garbage collection.": 900,
"Importing the table disables some indirect call optimizations.": 901,
Expand Down Expand Up @@ -103,6 +106,7 @@
"Duplicate identifier '{0}'.": 2300,
"Cannot find name '{0}'.": 2304,
"Module '{0}' has no exported member '{1}'.": 2305,
"An interface can only extend an interface.": 2312,
"Generic type '{0}' requires {1} type argument(s).": 2314,
"Type '{0}' is not generic.": 2315,
"Type '{0}' is not assignable to type '{1}'.": 2322,
Expand All @@ -119,19 +123,25 @@
"Operator '{0}' cannot be applied to types '{1}' and '{2}'.": 2365,
"A 'super' call must be the first statement in the constructor.": 2376,
"Constructors for derived classes must contain a 'super' call.": 2377,
"Getter and setter accessors do not agree in visibility.": 2379,
"'get' and 'set' accessor must have the same type.": 2380,
"Overload signatures must all be public, private or protected.": 2385,
"Constructor implementation is missing.": 2390,
"Function implementation is missing or not immediately following the declaration.": 2391,
"Multiple constructor implementations are not allowed.": 2392,
"Duplicate function implementation.": 2393,
"This overload signature is not compatible with its implementation signature.": 2394,
"Individual declarations in merged declaration '{0}' must be all exported or all local.": 2395,
"A class can only implement an interface.": 2422,
"A namespace declaration cannot be located prior to a class or function with which it is merged.": 2434,
"The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly.": 2453,
"Type '{0}' has no property '{1}'.": 2460,
"The '{0}' operator cannot be applied to type '{1}'.": 2469,
"In 'const' enum declarations member initializer must be constant expression.": 2474,
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
"'{0}' is referenced directly or indirectly in its own base expression.": 2506,
"Cannot create an instance of an abstract class.": 2511,
"Non-abstract class '{0}' does not implement inherited abstract member '{1}' from '{2}'.": 2515,
"Object is possibly 'null'.": 2531,
"Cannot assign to '{0}' because it is a constant or a read-only property.": 2540,
"The target of an assignment must be a variable or a property access.": 2541,
Expand Down
53 changes: 39 additions & 14 deletions src/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class DiagnosticMessage {
/** Respective source range, if any. */
range: Range | null = null;
/** Related range, if any. */
relatedRange: Range | null = null;
relatedRange: Range | null = null; // TODO: Make this a related message for chains?

/** Constructs a new diagnostic message. */
private constructor(code: i32, category: DiagnosticCategory, message: string) {
Expand All @@ -112,6 +112,26 @@ export class DiagnosticMessage {
return new DiagnosticMessage(code, category, message);
}

/** Tests if this message equals the specified. */
equals(other: DiagnosticMessage): bool {
if (this.code != other.code) return false;
var thisRange = this.range;
var otherRange = other.range;
if (thisRange) {
if (!otherRange || !thisRange.equals(otherRange)) return false;
} else if (otherRange) {
return false;
}
var thisRelatedRange = this.relatedRange;
var otherRelatedRange = other.relatedRange;
if (thisRelatedRange) {
if (!otherRelatedRange || !thisRelatedRange.equals(otherRelatedRange)) return false;
} else if (otherRange) {
return false;
}
return this.message == other.message;
}

/** Adds a source range to this message. */
withRange(range: Range): this {
this.range = range;
Expand All @@ -137,10 +157,13 @@ export class DiagnosticMessage {
this.message +
"\" in " +
source.normalizedPath +
":" +
"(" +
source.lineAt(range.start).toString() +
":" +
source.columnAt().toString()
"," +
source.columnAt().toString() +
"+" +
(range.end - range.start).toString() +
")"
);
}
return (
Expand Down Expand Up @@ -248,7 +271,7 @@ export abstract class DiagnosticEmitter {
/** Diagnostic messages emitted so far. */
diagnostics: DiagnosticMessage[];
/** Diagnostic messages already seen, by range. */
private seen: Map<Source,Map<i32,i32[]>> = new Map();
private seen: Map<Source,Map<i32,DiagnosticMessage[]>> = new Map();

/** Initializes this diagnostic emitter. */
protected constructor(diagnostics: DiagnosticMessage[] | null = null) {
Expand All @@ -265,6 +288,9 @@ export abstract class DiagnosticEmitter {
arg1: string | null = null,
arg2: string | null = null
): void {
var message = DiagnosticMessage.create(code, category, arg0, arg1, arg2);
if (range) message = message.withRange(range);
if (relatedRange) message.relatedRange = relatedRange;
// It is possible that the same diagnostic is emitted twice, for example
// when compiling generics with different types or when recompiling a loop
// because our initial assumptions didn't hold. It is even possible to get
Expand All @@ -274,21 +300,20 @@ export abstract class DiagnosticEmitter {
if (seen.has(range.source)) {
let seenInSource = assert(seen.get(range.source));
if (seenInSource.has(range.start)) {
let seenCodesAtPos = assert(seenInSource.get(range.start));
if (seenCodesAtPos.includes(code)) return;
seenCodesAtPos.push(code);
let seenMessagesAtPos = assert(seenInSource.get(range.start));
for (let i = 0, k = seenMessagesAtPos.length; i < k; ++i) {
if (seenMessagesAtPos[i].equals(message)) return;
}
seenMessagesAtPos.push(message);
} else {
seenInSource.set(range.start, [ code ]);
seenInSource.set(range.start, [ message ]);
}
} else {
let seenInSource = new Map<i32,i32[]>();
seenInSource.set(range.start, [ code ]);
let seenInSource = new Map<i32,DiagnosticMessage[]>();
seenInSource.set(range.start, [ message ]);
seen.set(range.source, seenInSource);
}
}
var message = DiagnosticMessage.create(code, category, arg0, arg1, arg2);
if (range) message = message.withRange(range);
if (relatedRange) message.relatedRange = relatedRange;
this.diagnostics.push(message);
// console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
// console.log(<string>new Error("stack").stack);
Expand Down
95 changes: 92 additions & 3 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1870,14 +1870,13 @@ export function getFunctionResults(func: FunctionRef): NativeType {
return binaryen._BinaryenFunctionGetResults(func);
}

export function getFunctionVars(func: FunctionRef): NativeType {
// TODO: unify this on Binaryen's side?
export function getFunctionVars(func: FunctionRef): NativeType[] {
var count = binaryen._BinaryenFunctionGetNumVars(func);
var types = new Array<NativeType>(count);
for (let i: Index = 0; i < count; ++i) {
types[i] = binaryen._BinaryenFunctionGetVar(func, i);
}
return createType(types);
return types;
}

// globals
Expand Down Expand Up @@ -1963,6 +1962,96 @@ export class Relooper {
}
}

/** Builds a switch using a sequence of `br_if`s. */
export class SwitchBuilder {
// This is useful because Binaryen understands sequences of `br_if`s and
// knows how to make a `br_table` from such a sequence if switched over
// values are considered dense enough, respectively a size-efficient sequence
// of `if`s if not, depending on optimization levels.

private module: Module;
private condition: ExpressionRef;
private values: i32[] = new Array();
private indexes: i32[] = new Array();
private cases: ExpressionRef[][] = new Array();
private defaultIndex: i32 = -1;

/** Creates a new builder using the specified i32 condition. */
constructor(module: Module, condition: ExpressionRef) {
this.module = module;
this.condition = condition;
}

/** Links a case to the specified branch. */
addCase(value: i32, code: ExpressionRef[]): void {
var cases = this.cases;
var index = cases.indexOf(code);
if (index < 0) {
index = cases.length;
cases.push(code);
}
this.values.push(value);
this.indexes.push(index);
}

/** Links the default branch. */
addDefault(code: ExpressionRef[]): void {
assert(this.defaultIndex == -1);
var cases = this.cases;
this.defaultIndex = cases.length;
cases.push(code);
}

/** Renders the switch to a block. */
render(localIndex: i32, labelPostfix: string = ""): ExpressionRef {
var module = this.module;
var cases = this.cases;
var numCases = cases.length;
if (!numCases) {
return module.drop(this.condition);
}
var values = this.values;
var numValues = values.length;
var indexes = this.indexes;
var entry = new Array<ExpressionRef>(1 + numValues + 1);
var labels = new Array<string>(numCases);
for (let i = 0; i < numCases; ++i) {
labels[i] = "case" + i.toString() + labelPostfix;
}
entry[0] = module.local_set(localIndex, this.condition);
for (let i = 0; i < numValues; ++i) {
let index = indexes[i];
entry[1 + i] = module.br(labels[index],
module.binary(BinaryOp.EqI32,
module.local_get(localIndex, NativeType.I32),
module.i32(values[i])
)
);
}
var defaultIndex = this.defaultIndex;
var defaultLabel = "default" + labelPostfix;
entry[1 + numValues] = module.br(
~defaultIndex
? labels[defaultIndex]
: defaultLabel
);
var current = module.block(labels[0], entry);
for (let i = 1; i < numCases; ++i) {
let block = cases[i - 1];
block.unshift(current);
current = module.block(labels[i], block);
}
var lastCase = cases[numCases - 1];
lastCase.unshift(current);
return module.block(
~defaultIndex
? null
: defaultLabel,
lastCase
);
}
}

export enum SideEffects {
None = 0 /* _BinaryenSideEffectNone */,
Branches = 1 /* _BinaryenSideEffectBranches */,
Expand Down
Loading