From b1e7b75ad7cd7d7c75a7cbc34065f2f13080ac8d Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Sun, 28 Jan 2018 06:18:27 +0100 Subject: [PATCH] Static operator overloads --- NOTICE | 19 +++- src/diagnosticMessages.generated.ts | 50 ++++----- src/diagnosticMessages.json | 25 ++--- src/diagnostics.ts | 2 +- src/program.ts | 41 ++++---- src/tokenizer.ts | 4 + std/assembly/set.ts | 8 ++ std/assembly/string.ts | 126 +++++++++++++++++------ tests/compiler/std/array.wast | 2 + tests/compiler/std/carray.wast | 2 + tests/compiler/std/heap.wast | 2 + tests/compiler/std/set.optimized.wast | 29 +++++- tests/compiler/std/set.wast | 38 +++++++ tests/compiler/std/string.optimized.wast | 110 ++++++++++++-------- tests/compiler/std/string.wast | 64 +++++++++--- tests/parser/regexp.ts.fixture.ts | 6 +- 16 files changed, 379 insertions(+), 149 deletions(-) diff --git a/NOTICE b/NOTICE index 33b55fbc..d7db4598 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,13 @@ -Includes parts of TypeScript: +Copyright (c) 2017-2018 AssemblyScript authors. + +The following authors have all licensed their contributions to AssemblyScript +under the licensing terms detailed in LICENSE. + +* Daniel Wirtz + +================================================================================ + +This program includes parts of TypeScript: https://github.com/Microsoft/TypeScript @@ -6,7 +15,9 @@ Includes parts of TypeScript: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 -Includes parts of Binaryen: +================================================================================ + +This program includes parts of Binaryen: https://github.com/WebAssembly/binaryen @@ -14,7 +25,9 @@ Includes parts of Binaryen: Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 -Includes parts of musl: +================================================================================ + +This program includes parts of musl: http://www.musl-libc.org diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index 1b3be527..a4134a3b 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -1,18 +1,19 @@ // code below is generated from diagnosticsMessages.json by scripts/build-diagnostics export enum DiagnosticCode { - Conversion_from_type_0_to_1_possibly_loses_information_and_thus_requires_an_explicit_cast = 100, - Basic_type_0_cannot_be_nullable = 101, - Operation_not_supported = 102, - Operation_is_unsafe = 103, - Cannot_export_a_mutable_global = 104, - Compiling_constant_with_non_constant_initializer_as_mutable = 105, - Type_0_cannot_be_changed_to_type_1 = 106, - Structs_cannot_extend_classes_and_vice_versa = 107, - Structs_cannot_implement_interfaces = 108, - Invalid_regular_expression_flags = 109, - Type_0_cannot_be_reinterpreted_as_type_1 = 110, - Implementation_0_must_match_the_signature_1 = 111, + Operation_not_supported = 100, + Operation_is_unsafe = 101, + Conversion_from_type_0_to_1_possibly_loses_information_and_thus_requires_an_explicit_cast = 200, + Conversion_from_type_0_to_1_will_require_an_explicit_cast_when_switching_between_32_64_bit = 201, + Type_0_cannot_be_changed_to_type_1 = 202, + Type_0_cannot_be_reinterpreted_as_type_1 = 203, + Basic_type_0_cannot_be_nullable = 204, + Cannot_export_a_mutable_global = 205, + Compiling_constant_with_non_constant_initializer_as_mutable = 206, + Structs_cannot_extend_classes_and_vice_versa = 207, + Structs_cannot_implement_interfaces = 208, + Invalid_regular_expression_flags = 209, + Implementation_0_must_match_the_signature_1 = 210, Unterminated_string_literal = 1002, Identifier_expected = 1003, _0_expected = 1005, @@ -94,18 +95,19 @@ export enum DiagnosticCode { export function diagnosticCodeToString(code: DiagnosticCode): string { switch (code) { - case 100: return "Conversion from type '{0}' to '{1}' possibly loses information and thus requires an explicit cast."; - case 101: return "Basic type '{0}' cannot be nullable."; - case 102: return "Operation not supported."; - case 103: return "Operation is unsafe."; - case 104: return "Cannot export a mutable global."; - case 105: return "Compiling constant with non-constant initializer as mutable."; - case 106: return "Type '{0}' cannot be changed to type '{1}'."; - case 107: return "Structs cannot extend classes and vice-versa."; - case 108: return "Structs cannot implement interfaces."; - case 109: return "Invalid regular expression flags."; - case 110: return "Type '{0}' cannot be reinterpreted as type '{1}'."; - case 111: return "Implementation '{0}' must match the signature '{1}'."; + case 100: return "Operation not supported."; + case 101: return "Operation is unsafe."; + case 200: return "Conversion from type '{0}' to '{1}' possibly loses information and thus requires an explicit cast."; + case 201: return "Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit."; + case 202: return "Type '{0}' cannot be changed to type '{1}'."; + case 203: return "Type '{0}' cannot be reinterpreted as type '{1}'."; + case 204: return "Basic type '{0}' cannot be nullable."; + case 205: return "Cannot export a mutable global."; + case 206: return "Compiling constant with non-constant initializer as mutable."; + case 207: return "Structs cannot extend classes and vice-versa."; + case 208: return "Structs cannot implement interfaces."; + case 209: return "Invalid regular expression flags."; + case 210: return "Implementation '{0}' must match the signature '{1}'."; case 1002: return "Unterminated string literal."; case 1003: return "Identifier expected."; case 1005: return "'{0}' expected."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 2949e198..28e6cfab 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -1,16 +1,17 @@ { - "Conversion from type '{0}' to '{1}' possibly loses information and thus requires an explicit cast.": 100, - "Basic type '{0}' cannot be nullable.": 101, - "Operation not supported.": 102, - "Operation is unsafe.": 103, - "Cannot export a mutable global.": 104, - "Compiling constant with non-constant initializer as mutable.": 105, - "Type '{0}' cannot be changed to type '{1}'.": 106, - "Structs cannot extend classes and vice-versa.": 107, - "Structs cannot implement interfaces.": 108, - "Invalid regular expression flags.": 109, - "Type '{0}' cannot be reinterpreted as type '{1}'.": 110, - "Implementation '{0}' must match the signature '{1}'.": 111, + "Operation not supported.": 100, + "Operation is unsafe.": 101, + "Conversion from type '{0}' to '{1}' possibly loses information and thus requires an explicit cast.": 200, + "Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.": 201, + "Type '{0}' cannot be changed to type '{1}'.": 202, + "Type '{0}' cannot be reinterpreted as type '{1}'.": 203, + "Basic type '{0}' cannot be nullable.": 204, + "Cannot export a mutable global.": 205, + "Compiling constant with non-constant initializer as mutable.": 206, + "Structs cannot extend classes and vice-versa.": 207, + "Structs cannot implement interfaces.": 208, + "Invalid regular expression flags.": 209, + "Implementation '{0}' must match the signature '{1}'.": 210, "Unterminated string literal.": 1002, "Identifier expected.": 1003, diff --git a/src/diagnostics.ts b/src/diagnostics.ts index b82236b9..2f49e3af 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -107,7 +107,7 @@ export function formatDiagnosticMessage(message: DiagnosticMessage, useColors: b if (useColors) sb.push(diagnosticCategoryToColor(message.category)); sb.push(diagnosticCategoryToString(message.category)); if (useColors) sb.push(colorReset); - sb.push(" AS"); + sb.push(message.code < 1000 ? " AS" : " TS"); sb.push(message.code.toString(10)); sb.push(": "); sb.push(message.message); diff --git a/src/program.ts b/src/program.ts index f864c049..925d1d2b 100644 --- a/src/program.ts +++ b/src/program.ts @@ -402,7 +402,7 @@ export class Program extends DiagnosticEmitter { private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void { var name = declaration.name.name; var internalName = declaration.fileLevelInternalName; - var instancePrototype: FunctionPrototype | null = null; + var prototype: FunctionPrototype | null = null; // static methods become global functions if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { @@ -419,9 +419,9 @@ export class Program extends DiagnosticEmitter { } } else classPrototype.members = new Map(); - var staticPrototype = new FunctionPrototype(this, name, internalName, declaration, null); - classPrototype.members.set(name, staticPrototype); - this.elements.set(internalName, staticPrototype); + prototype = new FunctionPrototype(this, name, internalName, declaration, null); + classPrototype.members.set(name, prototype); + this.elements.set(internalName, prototype); // instance methods are remembered until resolved } else { @@ -432,26 +432,31 @@ export class Program extends DiagnosticEmitter { } } else classPrototype.instanceMembers = new Map(); - instancePrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype); + prototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype); // if (classPrototype.isUnmanaged && instancePrototype.isAbstract) { // this.error( Unmanaged classes cannot declare abstract methods. ); // } - classPrototype.instanceMembers.set(name, instancePrototype); + classPrototype.instanceMembers.set(name, prototype); if (declaration.name.kind == NodeKind.CONSTRUCTOR) { if (classPrototype.constructorPrototype) this.error(DiagnosticCode.Multiple_constructor_implementations_are_not_allowed, declaration.name.range); else - classPrototype.constructorPrototype = instancePrototype; + classPrototype.constructorPrototype = prototype; } } - // handle operator annotations. operators are instance methods taking a second argument of the - // instance's type. return values vary depending on the operation. - if (declaration.decorators) { - for (var i = 0, k = declaration.decorators.length; i < k; ++i) { - var decorator = declaration.decorators[i]; + this.checkOperators(declaration.decorators, prototype, classPrototype); + } + + private checkOperators(decorators: Decorator[] | null, prototype: FunctionPrototype, classPrototype: ClassPrototype) { + // handle operator annotations. operators are either instance methods taking a second argument of the + // instance's type or static methods taking two arguments of the instance's type. return values vary + // depending on the operation. + if (decorators) { + for (var i = 0, k = decorators.length; i < k; ++i) { + var decorator = decorators[i]; if (decorator.decoratorKind == DecoratorKind.OPERATOR) { - if (!instancePrototype) { + if (!prototype) { this.error(DiagnosticCode.Operation_not_supported, decorator.range); continue; } @@ -462,22 +467,22 @@ export class Program extends DiagnosticEmitter { switch ((firstArg).value) { case "[]": - classPrototype.fnIndexedGet = instancePrototype.simpleName; + classPrototype.fnIndexedGet = prototype.simpleName; break; case "[]=": - classPrototype.fnIndexedSet = instancePrototype.simpleName; + classPrototype.fnIndexedSet = prototype.simpleName; break; case "+": - classPrototype.fnConcat = instancePrototype.simpleName; + classPrototype.fnConcat = prototype.simpleName; break; case "==": - classPrototype.fnEquals = instancePrototype.simpleName; + classPrototype.fnEquals = prototype.simpleName; break; - default: // TBD: does it make sense to provide more, even though not JS/TS-compatible? + default: this.error(DiagnosticCode.Operation_not_supported, firstArg.range); } } else diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 4ccf3f69..cbd3aed6 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -278,6 +278,10 @@ export class Range { start: i32; end: i32; + // TODO: set these while tokenizing + // line: i32; + // column: i32; + constructor(source: Source, start: i32, end: i32) { this.source = source; this.start = start; diff --git a/std/assembly/set.ts b/std/assembly/set.ts index 6ce9d04a..35fc199e 100644 --- a/std/assembly/set.ts +++ b/std/assembly/set.ts @@ -19,6 +19,8 @@ export class Set { // FIXME: not a proper set implementation, just a filler has(value: T): bool { + assert(this != null); + for (var index: usize = 0, limit: usize = this.__size; index < limit; ++index) if (load(this.__memory + index * sizeof()) == value) return true; @@ -26,6 +28,8 @@ export class Set { } add(value: T): Set { + assert(this != null); + if (this.__size >= this.__capacity) { var newCapacity = max(this.__capacity << 1, 8); var newMemory = allocate_memory(newCapacity * sizeof()); @@ -42,6 +46,8 @@ export class Set { } delete(value: T): bool { + assert(this != null); + for (var index: usize = 0, limit: usize = this.__size; index < limit; ++index) if (load(this.__memory + index * sizeof()) == value) { if (index + 1 < this.__size) @@ -53,6 +59,8 @@ export class Set { } clear(): void { + assert(this != null); + this.__size = 0; } diff --git a/std/assembly/string.ts b/std/assembly/string.ts index 001e044e..6d06e480 100644 --- a/std/assembly/string.ts +++ b/std/assembly/string.ts @@ -1,10 +1,12 @@ +// singleton empty string const EMPTY: String = changetype(""); +// number of bytes preceeding string data const HEAD: usize = 4; function allocate(length: i32): String { - assert(length >= 1); - var ptr = allocate_memory(HEAD + length * 2); + assert(length > 0); // 0 -> EMPTY + var ptr = allocate_memory(HEAD + (length << 1)); store(ptr, length); return changetype(ptr); } @@ -15,45 +17,75 @@ export class String { @operator("[]") charAt(pos: i32): String { + assert(this != null); + if (pos >= this.length) return EMPTY; + var out = allocate(1); store( changetype(out), - load(changetype(this) + (pos << 1), HEAD), + load( + changetype(this) + (pos << 1), + HEAD + ), HEAD ); return out; } charCodeAt(pos: i32): i32 { + assert(this != null); + if (pos >= this.length) - return -1; // NaN - return load(changetype(this) + (pos << 1), HEAD); + return -1; // (NaN) + + return load( + changetype(this) + (pos << 1), + HEAD + ); } codePointAt(pos: i32): i32 { + assert(this != null); + if (pos >= this.length) - return -1; // undefined - var first = load(changetype(this) + (pos << 1), HEAD); + return -1; // (undefined) + var first = load( + changetype(this) + (pos << 1), + HEAD + ); if (first < 0xD800 || first > 0xDBFF || pos + 1 == this.length) return first; - var second = load(changetype(this) + ((pos + 1) << 1), HEAD); + var second = load( + changetype(this) + ((pos + 1) << 1), + HEAD + ); if (second < 0xDC00 || second > 0xDFFF) return first; return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000; } @operator("+") + private static __concat(left: String, right: String): String { + if (left == null) + left = changetype("null"); + return left.concat(right); + } + concat(other: String): String { assert(this != null); - assert(other != null); + + if (other == null) + other = changetype("null"); + var thisLen: isize = this.length; var otherLen: isize = other.length; - var len: usize = thisLen + otherLen; - if (len == 0) + var outLen: usize = thisLen + otherLen; + if (outLen == 0) return EMPTY; - var out = allocate(len); + + var out = allocate(outLen); move_memory( changetype(out) + HEAD, changetype(this) + HEAD, @@ -68,12 +100,17 @@ export class String { } endsWith(searchString: String, endPosition: i32 = 0x7fffffff): bool { - assert(searchString != null); - var end: isize = min(max(endPosition, 0), this.length); + assert(this != null); + + if (searchString == null) + return false; + + var end: isize = min(max(endPosition, 0), this.length); var searchLength: isize = searchString.length; var start: isize = end - searchLength; if (start < 0) return false; + return !compare_memory( changetype(this) + HEAD + (start << 1), changetype(searchString) + HEAD, @@ -82,17 +119,20 @@ export class String { } @operator("==") - private __eq(other: String): bool { - if (this == null) - return other == null; - else if (other == null) + private static __eq(left: String, right: String): bool { + if (left == null) + return right == null; + else if (right == null) return false; - if (this.length != other.length) + + var leftLength = left.length; + if (leftLength != right.length) return false; + return !compare_memory( - changetype(this) + HEAD, - changetype(other) + HEAD, - (this.length << 1) + changetype(left) + HEAD, + changetype(right) + HEAD, + (leftLength << 1) ); } @@ -101,11 +141,17 @@ export class String { } indexOf(searchString: String, position: i32 = 0): i32 { - assert(searchString != null); + assert(this != null); + + if (searchString == null) + searchString = changetype("null"); + var pos: isize = position; var len: isize = this.length; var start: isize = min(max(pos, 0), len); - var searchLen: isize = searchString.length; + var searchLen: isize = searchString.length; + + // TODO: two-way, multiple char codes for (var k: usize = start; k + searchLen <= len; ++k) if (!compare_memory( changetype(this) + HEAD + (k << 1), @@ -118,13 +164,17 @@ export class String { startsWith(searchString: String, position: i32 = 0): bool { assert(this != null); - assert(searchString != null); + + if (searchString == null) + searchString = changetype("null"); + var pos: isize = position; var len: isize = this.length; var start: isize = min(max(position, 0), len); - var searchLength: isize = searchString.length; + var searchLength: isize = searchString.length; if (searchLength + start > len) return false; + return !compare_memory( changetype(this) + HEAD + (start << 1), changetype(searchString) + HEAD, @@ -134,14 +184,17 @@ export class String { substr(start: i32, length: i32 = i32.MAX_VALUE): String { assert(this != null); + var intStart: isize = start; var end: isize = length; var size: isize = this.length; if (intStart < 0) intStart = max(size + intStart, 0); + var resultLength: isize = min(max(end, 0), size - intStart); if (resultLength <= 0) return EMPTY; + var out = allocate(resultLength); move_memory( changetype(out) + HEAD, @@ -153,6 +206,7 @@ export class String { substring(start: i32, end: i32 = i32.MAX_VALUE): String { assert(this != null); + var len = this.length; var finalStart = min(max(start, 0), len); var finalEnd = min(max(end, 0), len); @@ -161,8 +215,10 @@ export class String { len = to - from; if (!len) return EMPTY; + if (!from && to == this.length) return this; + var out = allocate(len); move_memory( changetype(out) + HEAD, @@ -174,17 +230,21 @@ export class String { trim(): String { assert(this != null); + var length: usize = this.length; while (length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (length << 1), HEAD))) --length; + var start: usize = 0; - while (start < length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (start << 1), HEAD))) { - ++start; --length; - } + while (start < length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (start << 1), HEAD))) + ++start, --length; + if (!length) return EMPTY; + if (!start && length == this.length) return this; + var out = allocate(length); move_memory( changetype(out) + HEAD, @@ -196,15 +256,19 @@ export class String { trimLeft(): String { assert(this != null); + var start: isize = 0; var len: isize = this.length; while (start < len && isWhiteSpaceOrLineTerminator(load(changetype(this) + (start << 1), HEAD))) ++start; + if (!start) return this; + var outLen = len - start; if (!outLen) return EMPTY; + var out = allocate(outLen); move_memory( changetype(out) + HEAD, @@ -216,13 +280,17 @@ export class String { trimRight(): String { assert(this != null); + var len: isize = this.length; while (len > 0 && isWhiteSpaceOrLineTerminator(load(changetype(this) + (len << 1), HEAD))) --len; + if (len <= 0) return EMPTY; + if (len == this.length) return this; + var out = allocate(len); move_memory( changetype(out) + HEAD, diff --git a/tests/compiler/std/array.wast b/tests/compiler/std/array.wast index 7a6d5b19..6c585ab5 100644 --- a/tests/compiler/std/array.wast +++ b/tests/compiler/std/array.wast @@ -4071,6 +4071,8 @@ GLOBAL: std:string/HEAD FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String + FUNCTION_PROTOTYPE: std:string/String.__concat + FUNCTION_PROTOTYPE: std:string/String.__eq CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator FUNCTION_PROTOTYPE: std:string/parseInt diff --git a/tests/compiler/std/carray.wast b/tests/compiler/std/carray.wast index 3ed9c169..3f4ad3e1 100644 --- a/tests/compiler/std/carray.wast +++ b/tests/compiler/std/carray.wast @@ -284,6 +284,8 @@ GLOBAL: std:string/HEAD FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String + FUNCTION_PROTOTYPE: std:string/String.__concat + FUNCTION_PROTOTYPE: std:string/String.__eq CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator FUNCTION_PROTOTYPE: std:string/parseInt diff --git a/tests/compiler/std/heap.wast b/tests/compiler/std/heap.wast index ce31b032..dc070316 100644 --- a/tests/compiler/std/heap.wast +++ b/tests/compiler/std/heap.wast @@ -2867,6 +2867,8 @@ GLOBAL: std:string/HEAD FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String + FUNCTION_PROTOTYPE: std:string/String.__concat + FUNCTION_PROTOTYPE: std:string/String.__eq CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator FUNCTION_PROTOTYPE: std:string/parseInt diff --git a/tests/compiler/std/set.optimized.wast b/tests/compiler/std/set.optimized.wast index fbc29002..2528ccd3 100644 --- a/tests/compiler/std/set.optimized.wast +++ b/tests/compiler/std/set.optimized.wast @@ -1972,6 +1972,12 @@ (func $std:set/Set#add (; 4 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) + (if + (i32.eqz + (get_local $0) + ) + (unreachable) + ) (if (i32.ge_u (i32.load offset=8 @@ -2068,6 +2074,12 @@ (func $std:set/Set#has (; 5 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) + (if + (i32.eqz + (get_local $0) + ) + (unreachable) + ) (set_local $3 (i32.load offset=8 (get_local $0) @@ -2114,6 +2126,12 @@ (func $std:set/Set#delete (; 6 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) + (if + (i32.eqz + (get_local $0) + ) + (unreachable) + ) (set_local $3 (i32.load offset=8 (get_local $0) @@ -2212,6 +2230,7 @@ (i32.const 0) ) (func $start (; 7 ;) (type $v) + (local $0 i32) (set_global $std:heap/HEAP_OFFSET (get_global $HEAP_BASE) ) @@ -2327,8 +2346,16 @@ ) (unreachable) ) + (if + (i32.eqz + (tee_local $0 + (get_global $std/set/set) + ) + ) + (unreachable) + ) (i32.store offset=8 - (get_global $std/set/set) + (get_local $0) (i32.const 0) ) (if diff --git a/tests/compiler/std/set.wast b/tests/compiler/std/set.wast index 210467ab..d07d6163 100644 --- a/tests/compiler/std/set.wast +++ b/tests/compiler/std/set.wast @@ -2247,6 +2247,15 @@ (local $3 i32) (local $4 i32) (local $5 i32) + (if + (i32.eqz + (i32.ne + (get_local $0) + (i32.const 0) + ) + ) + (unreachable) + ) (if (i32.ge_u (i32.load offset=8 @@ -2348,6 +2357,15 @@ (func $std:set/Set#has (; 6 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) + (if + (i32.eqz + (i32.ne + (get_local $0) + (i32.const 0) + ) + ) + (unreachable) + ) (block $break|0 (block (set_local $2 @@ -2403,6 +2421,15 @@ (func $std:set/Set#delete (; 7 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) + (if + (i32.eqz + (i32.ne + (get_local $0) + (i32.const 0) + ) + ) + (unreachable) + ) (block $break|0 (block (set_local $2 @@ -2510,6 +2537,15 @@ ) ) (func $std:set/Set#clear (; 8 ;) (type $iv) (param $0 i32) + (if + (i32.eqz + (i32.ne + (get_local $0) + (i32.const 0) + ) + ) + (unreachable) + ) (i32.store offset=8 (get_local $0) (i32.const 0) @@ -2760,6 +2796,8 @@ GLOBAL: std:string/HEAD FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String + FUNCTION_PROTOTYPE: std:string/String.__concat + FUNCTION_PROTOTYPE: std:string/String.__eq CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator FUNCTION_PROTOTYPE: std:string/parseInt diff --git a/tests/compiler/std/string.optimized.wast b/tests/compiler/std/string.optimized.wast index 1c350b2a..a7cd446d 100644 --- a/tests/compiler/std/string.optimized.wast +++ b/tests/compiler/std/string.optimized.wast @@ -1,19 +1,49 @@ (module (type $i (func (result i32))) + (type $iii (func (param i32 i32) (result i32))) (type $iiii (func (param i32 i32 i32) (result i32))) (type $v (func)) (global $std/string/str (mut i32) (i32.const 8)) (memory $0 1) (data (i32.const 8) "\10\00\00\00h\00i\00,\00 \00I\00\'\00m\00 \00a\00 \00s\00t\00r\00i\00n\00g") (data (i32.const 48) "\02\00\00\00h\00i") - (data (i32.const 56) "\06\00\00\00s\00t\00r\00i\00n\00g") - (data (i32.const 72) "\03\00\00\00I\00\'\00m") - (data (i32.const 88) "\01\00\00\00,") - (data (i32.const 96) "\01\00\00\00x") + (data (i32.const 56) "\04\00\00\00n\00u\00l\00l") + (data (i32.const 72) "\06\00\00\00s\00t\00r\00i\00n\00g") + (data (i32.const 88) "\03\00\00\00I\00\'\00m") + (data (i32.const 104) "\01\00\00\00,") + (data (i32.const 112) "\01\00\00\00x") (export "getString" (func $std/string/getString)) (export "memory" (memory $0)) (start $start) - (func $std:heap/compare_memory (; 0 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $std:string/String#charCodeAt (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (if + (i32.eqz + (get_local $0) + ) + (unreachable) + ) + (if + (i32.ge_u + (get_local $1) + (i32.load + (get_local $0) + ) + ) + (return + (i32.const -1) + ) + ) + (i32.load16_u offset=4 + (i32.add + (get_local $0) + (i32.shl + (get_local $1) + (i32.const 1) + ) + ) + ) + ) + (func $std:heap/compare_memory (; 1 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (if (i32.eq (get_local $0) @@ -73,7 +103,7 @@ (i32.const 0) ) ) - (func $std:string/String#startsWith (; 1 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $std:string/String#startsWith (; 2 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -87,7 +117,9 @@ (i32.eqz (get_local $1) ) - (unreachable) + (set_local $1 + (i32.const 56) + ) ) (if (i32.gt_s @@ -152,13 +184,21 @@ ) ) ) - (func $std:string/String#endsWith (; 2 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $std:string/String#endsWith (; 3 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) + (if + (i32.eqz + (get_local $0) + ) + (unreachable) + ) (if (i32.eqz (get_local $1) ) - (unreachable) + (return + (i32.const 0) + ) ) (if (i32.lt_s @@ -221,16 +261,24 @@ ) ) ) - (func $std:string/String#indexOf (; 3 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (func $std:string/String#indexOf (; 4 ;) (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 - (get_local $1) + (get_local $0) ) (unreachable) ) + (if + (i32.eqz + (get_local $1) + ) + (set_local $1 + (i32.const 56) + ) + ) (set_local $4 (i32.load (get_local $1) @@ -310,11 +358,10 @@ ) (i32.const -1) ) - (func $std/string/getString (; 4 ;) (type $i) (result i32) + (func $std/string/getString (; 5 ;) (type $i) (result i32) (get_global $std/string/str) ) - (func $start (; 5 ;) (type $v) - (local $0 i32) + (func $start (; 6 ;) (type $v) (if (i32.ne (get_global $std/string/str) @@ -333,26 +380,9 @@ ) (if (i32.ne - (block $__inlined_func$std:string/String#charCodeAt (result i32) - (drop - (br_if $__inlined_func$std:string/String#charCodeAt - (i32.const -1) - (i32.ge_u - (i32.const 0) - (i32.load - (tee_local $0 - (get_global $std/string/str) - ) - ) - ) - ) - ) - (i32.load16_u offset=4 - (i32.add - (get_local $0) - (i32.const 0) - ) - ) + (call $std:string/String#charCodeAt + (get_global $std/string/str) + (i32.const 0) ) (i32.const 104) ) @@ -372,7 +402,7 @@ (i32.eqz (call $std:string/String#endsWith (get_global $std/string/str) - (i32.const 56) + (i32.const 72) (i32.const 2147483647) ) ) @@ -382,10 +412,8 @@ (i32.eqz (i32.ne (call $std:string/String#indexOf - (tee_local $0 - (get_global $std/string/str) - ) - (i32.const 72) + (get_global $std/string/str) + (i32.const 88) (i32.const 0) ) (i32.const -1) @@ -397,7 +425,7 @@ (i32.ne (call $std:string/String#indexOf (get_global $std/string/str) - (i32.const 88) + (i32.const 104) (i32.const 0) ) (i32.const 2) @@ -408,7 +436,7 @@ (i32.ne (call $std:string/String#indexOf (get_global $std/string/str) - (i32.const 96) + (i32.const 112) (i32.const 0) ) (i32.const -1) diff --git a/tests/compiler/std/string.wast b/tests/compiler/std/string.wast index f672f60b..90ce8307 100644 --- a/tests/compiler/std/string.wast +++ b/tests/compiler/std/string.wast @@ -5,18 +5,28 @@ (type $v (func)) (global $std/string/str (mut i32) (i32.const 8)) (global $std:string/HEAD i32 (i32.const 4)) - (global $HEAP_BASE i32 (i32.const 102)) + (global $HEAP_BASE i32 (i32.const 118)) (memory $0 1) (data (i32.const 8) "\10\00\00\00h\00i\00,\00 \00I\00\'\00m\00 \00a\00 \00s\00t\00r\00i\00n\00g\00") (data (i32.const 48) "\02\00\00\00h\00i\00") - (data (i32.const 56) "\06\00\00\00s\00t\00r\00i\00n\00g\00") - (data (i32.const 72) "\03\00\00\00I\00\'\00m\00") - (data (i32.const 88) "\01\00\00\00,\00") - (data (i32.const 96) "\01\00\00\00x\00") + (data (i32.const 56) "\04\00\00\00n\00u\00l\00l\00") + (data (i32.const 72) "\06\00\00\00s\00t\00r\00i\00n\00g\00") + (data (i32.const 88) "\03\00\00\00I\00\'\00m\00") + (data (i32.const 104) "\01\00\00\00,\00") + (data (i32.const 112) "\01\00\00\00x\00") (export "getString" (func $std/string/getString)) (export "memory" (memory $0)) (start $start) (func $std:string/String#charCodeAt (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (if + (i32.eqz + (i32.ne + (get_local $0) + (i32.const 0) + ) + ) + (unreachable) + ) (if (i32.ge_u (get_local $1) @@ -129,13 +139,13 @@ (unreachable) ) (if - (i32.eqz - (i32.ne - (get_local $1) - (i32.const 0) - ) + (i32.eq + (get_local $1) + (i32.const 0) + ) + (set_local $1 + (i32.const 56) ) - (unreachable) ) (set_local $3 (get_local $2) @@ -221,12 +231,21 @@ (if (i32.eqz (i32.ne - (get_local $1) + (get_local $0) (i32.const 0) ) ) (unreachable) ) + (if + (i32.eq + (get_local $1) + (i32.const 0) + ) + (return + (i32.const 0) + ) + ) (set_local $5 (select (tee_local $3 @@ -310,12 +329,21 @@ (if (i32.eqz (i32.ne - (get_local $1) + (get_local $0) (i32.const 0) ) ) (unreachable) ) + (if + (i32.eq + (get_local $1) + (i32.const 0) + ) + (set_local $1 + (i32.const 56) + ) + ) (set_local $3 (get_local $2) ) @@ -480,7 +508,7 @@ (i32.eqz (call $std:string/String#endsWith (get_global $std/string/str) - (i32.const 56) + (i32.const 72) (i32.const 2147483647) ) ) @@ -490,7 +518,7 @@ (i32.eqz (call $std:string/String#includes (get_global $std/string/str) - (i32.const 72) + (i32.const 88) (i32.const 0) ) ) @@ -501,7 +529,7 @@ (i32.eq (call $std:string/String#indexOf (get_global $std/string/str) - (i32.const 88) + (i32.const 104) (i32.const 0) ) (i32.const 2) @@ -514,7 +542,7 @@ (i32.eq (call $std:string/String#indexOf (get_global $std/string/str) - (i32.const 96) + (i32.const 112) (i32.const 0) ) (i32.sub @@ -606,6 +634,8 @@ GLOBAL: std:string/HEAD FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String + FUNCTION_PROTOTYPE: std:string/String.__concat + FUNCTION_PROTOTYPE: std:string/String.__eq CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator FUNCTION_PROTOTYPE: std:string/parseInt diff --git a/tests/parser/regexp.ts.fixture.ts b/tests/parser/regexp.ts.fixture.ts index fd046c41..9c2d50a0 100644 --- a/tests/parser/regexp.ts.fixture.ts +++ b/tests/parser/regexp.ts.fixture.ts @@ -8,6 +8,6 @@ b / ig; false && /abc/gX.test(someString) || true; // ERROR 1161: "Unterminated regular expression literal." in regexp.ts @ 75,76 // ERROR 1005: "'/' expected." in regexp.ts @ 74,76 -// ERROR 109: "Invalid regular expression flags." in regexp.ts @ 95,98 -// ERROR 109: "Invalid regular expression flags." in regexp.ts @ 111,113 -// ERROR 109: "Invalid regular expression flags." in regexp.ts @ 131,133 +// ERROR 209: "Invalid regular expression flags." in regexp.ts @ 95,98 +// ERROR 209: "Invalid regular expression flags." in regexp.ts @ 111,113 +// ERROR 209: "Invalid regular expression flags." in regexp.ts @ 131,133