diff --git a/examples/tlsf/assembly/tlsf.ts b/examples/tlsf/assembly/tlsf.ts index 75ed8cce..e7f3ef0a 100644 --- a/examples/tlsf/assembly/tlsf.ts +++ b/examples/tlsf/assembly/tlsf.ts @@ -17,7 +17,7 @@ const FL_INDEX_COUNT: u32 = FL_INDEX_MAX - FL_INDEX_SHIFT + 1; const SMALL_BLOCK_SIZE: u32 = 1 << FL_INDEX_SHIFT; /** Block header structure. */ -@explicit +@unmanaged class BlockHeader { /////////////////////////////// Constants /////////////////////////////////// @@ -41,7 +41,7 @@ class BlockHeader { // the prev_phys_block field, and no larger than the number of addressable // bits for FL_INDEX. static readonly BLOCK_SIZE_MIN: usize = BlockHeader.SIZE - sizeof(); - static readonly BLOCK_SIZE_MAX: usize = 1 << FL_INDEX_MAX; + static readonly BLOCK_SIZE_MAX: usize = 1 << FL_INDEX_MAX; ///////////////////////////////// Fields //////////////////////////////////// @@ -157,7 +157,7 @@ class BlockHeader { /** Marks this block as being 'free'. */ markAsFree(): void { - var next = this.linkNext(); // Link the block to the next block, first. + var next = this.linkNext(); // Link the block to the next block first. next.tagAsPrevFree(); this.tagAsFree(); } @@ -206,7 +206,7 @@ class BlockHeader { } /** The TLSF control structure. */ -@explicit +@unmanaged class Control extends BlockHeader { // Empty lists point here, indicating free // The control structure uses 3188 bytes in WASM32. @@ -297,6 +297,9 @@ class Control extends BlockHeader { // Empty lists point here, indicating free block.next_free = current; block.prev_free = this; current.prev_free = block; + assert(block.isFree, + "block must be free" + ); assert(block.toDataPtr() == align_ptr(block.toDataPtr(), ALIGN_SIZE), "block not aligned properly" ); diff --git a/examples/tlsf/tests/index.js b/examples/tlsf/tests/index.js index a3aaa5d1..73caedd0 100644 --- a/examples/tlsf/tests/index.js +++ b/examples/tlsf/tests/index.js @@ -53,7 +53,7 @@ function test(file) { tlsf.check_pool(0); } } finally { - mem(tlsf.memory, 0, 4096); + // mem(tlsf.memory, 0, 4096); console.log("memSize=" + memSize); } console.log(); diff --git a/examples/ugc/assembly/ugc.ts b/examples/ugc/assembly/ugc.ts index 7232fc82..a98bb752 100644 --- a/examples/ugc/assembly/ugc.ts +++ b/examples/ugc/assembly/ugc.ts @@ -11,7 +11,7 @@ const SWEEP: u8 = 2; const GRAY: u32 = 2; /** Header for a managed object. */ -@explicit +@unmanaged class ObjectHeader { /////////////////////////////// Constants /////////////////////////////////// @@ -73,7 +73,7 @@ class ObjectHeader { } /** Garbage collector data. */ -@explicit +@unmanaged class Control { /////////////////////////////// Constants /////////////////////////////////// diff --git a/src/ast.ts b/src/ast.ts index d7f3b0fe..173470fa 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -361,8 +361,8 @@ export abstract class Node { stmt.decoratorKind = DecoratorKind.OPERATOR; break; - case "explicit": - stmt.decoratorKind = DecoratorKind.EXPLICIT; + case "unmanaged": + stmt.decoratorKind = DecoratorKind.UNMANAGED; break; case "offset": @@ -1159,7 +1159,7 @@ export const enum DecoratorKind { CUSTOM, GLOBAL, OPERATOR, - EXPLICIT, + UNMANAGED, OFFSET } diff --git a/src/builtins.ts b/src/builtins.ts index 1ac74c9d..eac6391b 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -1566,9 +1566,8 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty } else arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); - // TODO: report message to embedder, requires strings type = compiler.currentType; - // arg1 = operands.length == 2 ? compiler.compileExpression(operands[1], Type.string) : usizeType.toNativeZero(module); + arg1 = operands.length == 2 ? compiler.compileExpression(operands[1], compiler.options.usizeType) : compiler.options.usizeType.toNativeZero(module); compiler.currentType = type.nonNullableType; // just return ifTrueish if assertions are disabled, or simplify if dropped anyway diff --git a/src/compiler.ts b/src/compiler.ts index 276230df..1ec3f47c 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -196,6 +196,8 @@ export class Compiler extends DiagnosticEmitter { memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL /** Memory segments being compiled. */ memorySegments: MemorySegment[] = new Array(); + /** Map of already compiled static string segments. */ + stringSegments: Map = new Map(); /** Already processed file names. */ files: Set = new Set(); @@ -2859,6 +2861,29 @@ export class Compiler extends DiagnosticEmitter { this.currentType = contextualType.is(TypeFlags.SIGNED) ? Type.i32 : Type.u32; return this.module.createI32(intValue.toI32()); + case LiteralKind.STRING: + var stringValue = (expression).value; + var stringSegment: MemorySegment | null = this.stringSegments.get(stringValue); + if (!stringSegment) { + var stringLength = stringValue.length; + var stringBuffer = new Uint8Array(4 + stringLength * 2); + stringBuffer[0] = stringLength & 0xff; + stringBuffer[1] = (stringLength >>> 8) & 0xff; + stringBuffer[2] = (stringLength >>> 16) & 0xff; + stringBuffer[3] = (stringLength >>> 24) & 0xff; + for (var i = 0; i < stringLength; ++i) { + stringBuffer[4 + i * 2] = stringValue.charCodeAt(i) & 0xff; + stringBuffer[5 + i * 2] = (stringValue.charCodeAt(i) >>> 8) & 0xff; + } + stringSegment = this.addMemorySegment(stringBuffer); + this.stringSegments.set(stringValue, stringSegment); + } + var stringOffset = stringSegment.offset; + this.currentType = this.options.usizeType; + return this.options.isWasm64 + ? this.module.createI64(stringOffset.lo, stringOffset.hi) + : this.module.createI32(stringOffset.lo); + // case LiteralKind.OBJECT: // case LiteralKind.REGEXP: // case LiteralKind.STRING: diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index 1519b36a..1b3be527 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -78,6 +78,7 @@ export enum DiagnosticCode { _get_and_set_accessor_must_have_the_same_type = 2380, 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, Export_declaration_conflicts_with_exported_declaration_of_0 = 2484, Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540, @@ -170,6 +171,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 2380: return "'get' and 'set' accessor must have the same type."; 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 2484: return "Export declaration conflicts with exported declaration of '{0}'."; case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 397c6067..2949e198 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -78,6 +78,7 @@ "'get' and 'set' accessor must have the same type.": 2380, "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, "Export declaration conflicts with exported declaration of '{0}'.": 2484, "Cannot assign to '{0}' because it is a constant or a read-only property.": 2540, diff --git a/src/parser.ts b/src/parser.ts index 888bfc5d..c99f9fa1 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -741,13 +741,14 @@ export class Parser extends DiagnosticEmitter { } } - if (tn.skip(Token.CONSTRUCTOR) || tn.skip(Token.IDENTIFIER)) { // order is important - var identifier: IdentifierExpression = tn.token == Token.CONSTRUCTOR + var isConstructor = tn.skip(Token.CONSTRUCTOR); + if (isConstructor || tn.skip(Token.IDENTIFIER)) { + var identifier = isConstructor ? Node.createConstructorExpression(tn.range()) : Node.createIdentifierExpression(tn.readIdentifier(), tn.range()); var typeParameters: TypeParameter[] | null; if (tn.skip(Token.LESSTHAN)) { - if (identifier.kind == NodeKind.CONSTRUCTOR) + if (isConstructor) this.error(DiagnosticCode.Type_parameters_cannot_appear_on_a_constructor_declaration, tn.range()); // recoverable typeParameters = this.parseTypeParameters(tn); if (!typeParameters) @@ -755,9 +756,6 @@ export class Parser extends DiagnosticEmitter { } else typeParameters = []; - if (identifier.kind == NodeKind.CONSTRUCTOR && tn.peek() != Token.OPENPAREN) - this.error(DiagnosticCode.Constructor_implementation_is_missing, tn.range()); - // method: '(' Parameters (':' Type)? '{' Statement* '}' ';'? if (tn.skip(Token.OPENPAREN)) { var parameters = this.parseParameters(tn); @@ -802,6 +800,12 @@ export class Parser extends DiagnosticEmitter { tn.skip(Token.SEMICOLON); return retMethod; + } else if (isConstructor) { + this.error(DiagnosticCode.Constructor_implementation_is_missing, identifier.range()); + + } else if (isGetter || isSetter) { + this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, identifier.range()); + // field: (':' Type)? ('=' Expression)? ';'? } else { var modifier: Modifier | null; diff --git a/src/program.ts b/src/program.ts index f48d4bf2..f864c049 100644 --- a/src/program.ts +++ b/src/program.ts @@ -276,15 +276,17 @@ export class Program extends DiagnosticEmitter { } while (true); } - private checkGlobalAlias(element: Element, declaration: DeclarationStatement) { + private checkGlobalAlias(element: Element, declaration: DeclarationStatement): bool { if (hasDecorator("global", declaration.decorators) || (declaration.range.source.isStdlib && assert(declaration.parent).kind == NodeKind.SOURCE && element.is(ElementFlags.EXPORTED))) { if (this.elements.has(declaration.name.name)) this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, element.internalName); else { this.elements.set(declaration.name.name, element); this.exports.set(declaration.name.name, element); + return true; } } + return false; } private initializeClass(declaration: ClassDeclaration, queuedDerivedClasses: ClassPrototype[], namespace: Element | null = null): void { @@ -297,10 +299,8 @@ export class Program extends DiagnosticEmitter { prototype.namespace = namespace; this.elements.set(internalName, prototype); - this.checkGlobalAlias(prototype, declaration); - - if (hasDecorator("explicit", declaration.decorators)) { - prototype.isExplicit = true; + if (hasDecorator("unmanaged", declaration.decorators)) { + prototype.isUnmanaged = true; if (declaration.implementsTypes && declaration.implementsTypes.length) this.error(DiagnosticCode.Structs_cannot_implement_interfaces, Range.join(declaration.name.range, declaration.implementsTypes[declaration.implementsTypes.length - 1].range)); } else if (declaration.implementsTypes.length) @@ -352,6 +352,16 @@ export class Program extends DiagnosticEmitter { throw new Error("class member expected"); } } + + if (this.checkGlobalAlias(prototype, declaration)) { + if (declaration.name.name === "String") { + var instance = prototype.resolve(null, null); + if (instance) { + assert(!this.types.has("string")); + this.types.set("string", instance.type); + } + } + } } private initializeField(declaration: FieldDeclaration, classPrototype: ClassPrototype): void { @@ -396,6 +406,7 @@ export class Program extends DiagnosticEmitter { // static methods become global functions if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { + assert(declaration.name.kind != NodeKind.CONSTRUCTOR); if (this.elements.has(internalName)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName); @@ -422,10 +433,16 @@ export class Program extends DiagnosticEmitter { } else classPrototype.instanceMembers = new Map(); instancePrototype = new FunctionPrototype(this, name, internalName, declaration, classPrototype); - // if (classPrototype.isExplicit && instancePrototype.isAbstract) { - // this.error( Explicit classes cannot declare abstract methods. ); + // if (classPrototype.isUnmanaged && instancePrototype.isAbstract) { + // this.error( Unmanaged classes cannot declare abstract methods. ); // } classPrototype.instanceMembers.set(name, instancePrototype); + 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; + } } // handle operator annotations. operators are instance methods taking a second argument of the @@ -1260,8 +1277,8 @@ export enum ElementFlags { PRIVATE = 1 << 15, /** Is an abstract member. */ ABSTRACT = 1 << 16, - /** Is an explicitly layed out and allocated class with limited capabilites. */ - EXPLICIT = 1 << 17, + /** Is an unmanaged class with limited capabilites. */ + UNMANAGED = 1 << 17, /** Has already inherited base class static members. */ HAS_STATIC_BASE_MEMBERS = 1 << 18, /** Is scoped. */ @@ -1879,6 +1896,8 @@ export class ClassPrototype extends Element { instanceMembers: Map | null = null; /** Base class prototype, if applicable. */ basePrototype: ClassPrototype | null = null; // set in Program#initialize + /** Constructor prototype. */ + constructorPrototype: FunctionPrototype | null = null; /** Overloaded indexed get method, if any. */ fnIndexedGet: string | null = null; @@ -1907,9 +1926,9 @@ export class ClassPrototype extends Element { } } - /** Whether explicitly layed out and allocated */ - get isExplicit(): bool { return (this.flags & ElementFlags.EXPLICIT) != 0; } - set isExplicit(is: bool) { if (is) this.flags |= ElementFlags.EXPLICIT; else this.flags &= ~ElementFlags.EXPLICIT; } + /** Whether an unamanaged class or not. */ + get isUnmanaged(): bool { return (this.flags & ElementFlags.UNMANAGED) != 0; } + set isUnmanaged(is: bool) { if (is) this.flags |= ElementFlags.UNMANAGED; else this.flags &= ~ElementFlags.UNMANAGED; } resolve(typeArguments: Type[] | null, contextualTypeArguments: Map | null = null): Class | null { var instanceKey = typeArguments ? typesToString(typeArguments) : ""; @@ -1937,7 +1956,7 @@ export class ClassPrototype extends Element { this.program.error(DiagnosticCode.A_class_may_only_extend_another_class, declaration.extendsType.range); return null; } - if (baseClass.prototype.isExplicit != this.isExplicit) { + if (baseClass.prototype.isUnmanaged != this.isUnmanaged) { this.program.error(DiagnosticCode.Structs_cannot_extend_classes_and_vice_versa, Range.join(declaration.name.range, declaration.extendsType.range)); return null; } diff --git a/std/assembly.d.ts b/std/assembly.d.ts index 52e3be04..71168780 100644 --- a/std/assembly.d.ts +++ b/std/assembly.d.ts @@ -287,8 +287,8 @@ declare function global(target: Function): any; /** Annotates a method as an operator overload. */ declare function operator(token: string): any; -/** Annotates a class as explicitly layed out and allocated. */ -declare function explicit(target: Function): any; +/** Annotates a class as being unmanaged with limited capabilities. */ +declare function unmanaged(target: Function): any; -/** Annoates a class field with an explicit offset. */ +/** Annotates a class field with an explicit offset. */ declare function offset(offset: usize): any; diff --git a/std/assembly/array.ts b/std/assembly/array.ts index f4be3c74..8ac36e55 100644 --- a/std/assembly/array.ts +++ b/std/assembly/array.ts @@ -163,7 +163,7 @@ export class Array { } } -@explicit +@unmanaged export class CArray { private constructor() {} diff --git a/std/assembly/string.ts b/std/assembly/string.ts index 0f732cdb..d3adf31c 100644 --- a/std/assembly/string.ts +++ b/std/assembly/string.ts @@ -1,93 +1,94 @@ const EMPTY: String = changetype(""); +function allocate(length: i32): String { + var ptr = allocate_memory(4 + length * 2); + store(ptr, length); + return changetype(ptr); +} + +@unmanaged export class String { - private __memory: usize; readonly length: i32; - constructor(ptr: usize, length: i32) { - if (length < 0) - throw new RangeError("invalid length"); - this.__memory = ptr; - this.length = length; - } - @operator("[]") charAt(pos: i32): String { if (pos >= this.length) - return changetype(""); - return new String(this.__memory + (pos << 1), 1); + return EMPTY; + var out = allocate(1); + store(changetype(out), load(changetype(this) + (pos << 1), 4), 4); + return out; } charCodeAt(pos: i32): i32 { if (pos >= this.length) return -1; // NaN - return load(this.__memory + (pos << 1)); + return load(changetype(this) + (pos << 1), 4); } codePointAt(pos: i32): i32 { if (pos >= this.length) return -1; // undefined - var first = load(this.__memory + (pos << 1)); + var first = load(changetype(this) + (pos << 1), 4); if (first < 0xD800 || first > 0xDBFF || pos + 1 == this.length) return first; - var second = load(this.__memory + ((pos + 1) << 1)); + var second = load(changetype(this) + ((pos + 1) << 1), 4); if (second < 0xDC00 || second > 0xDFFF) return first; return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000; } @operator("+") - concat(other: this): String { + concat(other: String): String { assert(this != null); assert(other != null); var thisLen: isize = this.length; var otherLen: isize = other.length; var len: usize = thisLen + otherLen; - var newMemory = allocate_memory(len << 1); - move_memory(newMemory, this.__memory, thisLen << 1); - move_memory(newMemory + (thisLen << 1), other.__memory, otherLen << 1); - return new String(newMemory, len); + var out = allocate(len); + move_memory(changetype(out) + 4, changetype(this) + 4, thisLen << 1); + move_memory(changetype(out) + 4 + (thisLen << 1), changetype(other) + 4, otherLen << 1); + return out; } - endsWith(searchString: this, endPosition: i32 = 0x7fffffff): bool { + endsWith(searchString: String, endPosition: i32 = 0x7fffffff): bool { assert(searchString != null); 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(this.__memory + (start << 1), searchString.__memory, searchLength << 1); + return !compare_memory(changetype(this) + 4 + (start << 1), changetype(searchString) + 4, searchLength << 1); } @operator("==") - private __eq(other: this): bool { + private __eq(other: String): bool { if (this == null) return other == null; else if (other == null) return false; if (this.length != other.length) return false; - return !compare_memory(this.__memory, other.__memory, this.length); + return !compare_memory(changetype(this) + 4, changetype(other) + 4, (this.length << 1)); } - includes(searchString: this, position: i32 = 0): bool { + includes(searchString: String, position: i32 = 0): bool { return this.indexOf(searchString, position) != -1; } - indexOf(searchString: this, position: i32 = 0): i32 { + indexOf(searchString: String, position: i32 = 0): i32 { assert(searchString != null); var pos: isize = position; var len: isize = this.length; var start: isize = min(max(pos, 0), len); var searchLen: isize = searchString.length; for (var k: usize = start; k + searchLen <= len; ++k) - if (!compare_memory(this.__memory + (k << 1), searchString.__memory, searchLen << 1)) + if (!compare_memory(changetype(this) + 4 + (k << 1), changetype(searchString) + 4, searchLen << 1)) return k; return -1; } - startsWith(searchString: this, position: i32 = 0): bool { + startsWith(searchString: String, position: i32 = 0): bool { assert(this != null); assert(searchString != null); var pos: isize = position; @@ -96,7 +97,7 @@ export class String { var searchLength: isize = searchString.length; if (searchLength + start > len) return false; - return !compare_memory(this.__memory + (start << 1), searchString.__memory, searchLength << 1); + return !compare_memory(changetype(this) + 4 + (start << 1), changetype(searchString) + 4, searchLength << 1); } substr(start: i32, length: i32 = i32.MAX_VALUE): String { @@ -109,7 +110,9 @@ export class String { var resultLength: isize = min(max(end, 0), size - intStart); if (resultLength < 0) return EMPTY; - return new String(this.__memory + (intStart << 1), resultLength); + var out = allocate(resultLength); + move_memory(changetype(out) + 4, changetype(this) + 4 + (intStart << 1), resultLength << 1); + return out; } substring(start: i32, end: i32 = i32.MAX_VALUE): String { @@ -124,46 +127,55 @@ export class String { return EMPTY; if (!from && to == this.length) return this; - return new String(this.__memory + (from << 1), len); + var out = allocate(len); + move_memory(changetype(out) + 4, changetype(this) + 4 + (from << 1), len << 1); + return out; } trim(): String { assert(this != null); var length: usize = this.length; - while (length && isWhiteSpaceOrLineTerminator(load(this.__memory + (length << 1)))) + while (length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (length << 1), 4))) --length; var start: usize = 0; - while (start < length && isWhiteSpaceOrLineTerminator(load(this.__memory + (start << 1)))) { + while (start < length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (start << 1), 4))) { ++start; --length; } if (!length) return EMPTY; if (!start && length == this.length) return this; - return new String(this.__memory + (start << 1), length); + var out = allocate(length); + move_memory(changetype(out) + 4, changetype(this) + 4 + (start << 1), length << 1); + return out; } trimLeft(): String { assert(this != null); var start: isize = 0; var len: isize = this.length; - while (start < len && isWhiteSpaceOrLineTerminator(load(this.__memory + (start << 1)))) + while (start < len && isWhiteSpaceOrLineTerminator(load(changetype(this) + (start << 1), 4))) ++start; if (!start) return this; - return new String(this.__memory + (start << 1), (len - start)); + var outLen = len - start; + var out = allocate(outLen); + move_memory(changetype(out) + 4, changetype(this) + 4 + (start << 1), outLen << 1); + return out; } trimRight(): String { assert(this != null); var len: isize = this.length; - while (len > 0 && isWhiteSpaceOrLineTerminator(load(this.__memory + (len << 1)))) + while (len > 0 && isWhiteSpaceOrLineTerminator(load(changetype(this) + (len << 1), 4))) --len; if (len <= 0) return EMPTY; if (len == this.length) return this; - return new String(this.__memory, len); + var out = allocate(len); + move_memory(changetype(out) + 4, changetype(this) + 4, len << 1); + return out; } } diff --git a/tests/compiler/assert.optimized.wast b/tests/compiler/assert.optimized.wast index 453abe27..69d82329 100644 --- a/tests/compiler/assert.optimized.wast +++ b/tests/compiler/assert.optimized.wast @@ -1,6 +1,7 @@ (module (type $v (func)) (memory $0 1) + (data (i32.const 8) "\0c\00\00\00m\00u\00s\00t\00 \00b\00e\00 \00t\00r\00u\00e") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/assert.wast b/tests/compiler/assert.wast index 87a71261..c243f5a0 100644 --- a/tests/compiler/assert.wast +++ b/tests/compiler/assert.wast @@ -1,7 +1,8 @@ (module (type $v (func)) - (global $HEAP_BASE i32 (i32.const 4)) + (global $HEAP_BASE i32 (i32.const 36)) (memory $0 1) + (data (i32.const 8) "\0c\00\00\00m\00u\00s\00t\00 \00b\00e\00 \00t\00r\00u\00e\00") (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/std/array.wast b/tests/compiler/std/array.wast index 45757463..4b56aa53 100644 --- a/tests/compiler/std/array.wast +++ b/tests/compiler/std/array.wast @@ -4037,8 +4037,8 @@ FUNCTION_PROTOTYPE: usize GLOBAL: HEAP_BASE CLASS_PROTOTYPE: std:array/Array - CLASS_PROTOTYPE: Array PROPERTY: std:array/Array#length + CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/CArray CLASS_PROTOTYPE: CArray CLASS_PROTOTYPE: std:error/Error @@ -4065,9 +4065,10 @@ CLASS_PROTOTYPE: std:regexp/RegExp CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set - CLASS_PROTOTYPE: Set PROPERTY: std:set/Set#size + CLASS_PROTOTYPE: Set GLOBAL: std:string/EMPTY + FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator @@ -4078,14 +4079,14 @@ GLOBAL: std/array/arr GLOBAL: std/array/i [program.exports] - CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/Array - CLASS_PROTOTYPE: CArray + CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/CArray - CLASS_PROTOTYPE: Error + CLASS_PROTOTYPE: CArray CLASS_PROTOTYPE: std:error/Error - CLASS_PROTOTYPE: RangeError + CLASS_PROTOTYPE: Error CLASS_PROTOTYPE: std:error/RangeError + CLASS_PROTOTYPE: RangeError FUNCTION_PROTOTYPE: allocate_memory FUNCTION_PROTOTYPE: std:heap/allocate_memory FUNCTION_PROTOTYPE: free_memory @@ -4096,14 +4097,14 @@ FUNCTION_PROTOTYPE: std:heap/set_memory FUNCTION_PROTOTYPE: compare_memory FUNCTION_PROTOTYPE: std:heap/compare_memory - CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: std:map/Map - CLASS_PROTOTYPE: RegExp + CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: std:regexp/RegExp - CLASS_PROTOTYPE: Set + CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set - CLASS_PROTOTYPE: String + CLASS_PROTOTYPE: Set CLASS_PROTOTYPE: std:string/String + CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: parseInt FUNCTION_PROTOTYPE: std:string/parseInt FUNCTION_PROTOTYPE: parseFloat diff --git a/tests/compiler/std/carray.wast b/tests/compiler/std/carray.wast index f1542b03..c80de815 100644 --- a/tests/compiler/std/carray.wast +++ b/tests/compiler/std/carray.wast @@ -250,8 +250,8 @@ FUNCTION_PROTOTYPE: usize GLOBAL: HEAP_BASE CLASS_PROTOTYPE: std:array/Array - CLASS_PROTOTYPE: Array PROPERTY: std:array/Array#length + CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/CArray CLASS_PROTOTYPE: CArray CLASS_PROTOTYPE: std:error/Error @@ -278,9 +278,10 @@ CLASS_PROTOTYPE: std:regexp/RegExp CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set - CLASS_PROTOTYPE: Set PROPERTY: std:set/Set#size + CLASS_PROTOTYPE: Set GLOBAL: std:string/EMPTY + FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator @@ -290,14 +291,14 @@ FUNCTION_PROTOTYPE: parseFloat GLOBAL: std/carray/arr [program.exports] - CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/Array - CLASS_PROTOTYPE: CArray + CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/CArray - CLASS_PROTOTYPE: Error + CLASS_PROTOTYPE: CArray CLASS_PROTOTYPE: std:error/Error - CLASS_PROTOTYPE: RangeError + CLASS_PROTOTYPE: Error CLASS_PROTOTYPE: std:error/RangeError + CLASS_PROTOTYPE: RangeError FUNCTION_PROTOTYPE: allocate_memory FUNCTION_PROTOTYPE: std:heap/allocate_memory FUNCTION_PROTOTYPE: free_memory @@ -308,14 +309,14 @@ FUNCTION_PROTOTYPE: std:heap/set_memory FUNCTION_PROTOTYPE: compare_memory FUNCTION_PROTOTYPE: std:heap/compare_memory - CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: std:map/Map - CLASS_PROTOTYPE: RegExp + CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: std:regexp/RegExp - CLASS_PROTOTYPE: Set + CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set - CLASS_PROTOTYPE: String + CLASS_PROTOTYPE: Set CLASS_PROTOTYPE: std:string/String + CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: parseInt FUNCTION_PROTOTYPE: std:string/parseInt FUNCTION_PROTOTYPE: parseFloat diff --git a/tests/compiler/std/heap.wast b/tests/compiler/std/heap.wast index 2c519069..cec95cd4 100644 --- a/tests/compiler/std/heap.wast +++ b/tests/compiler/std/heap.wast @@ -2833,8 +2833,8 @@ FUNCTION_PROTOTYPE: usize GLOBAL: HEAP_BASE CLASS_PROTOTYPE: std:array/Array - CLASS_PROTOTYPE: Array PROPERTY: std:array/Array#length + CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/CArray CLASS_PROTOTYPE: CArray CLASS_PROTOTYPE: std:error/Error @@ -2861,9 +2861,10 @@ CLASS_PROTOTYPE: std:regexp/RegExp CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set - CLASS_PROTOTYPE: Set PROPERTY: std:set/Set#size + CLASS_PROTOTYPE: Set GLOBAL: std:string/EMPTY + FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator @@ -2876,14 +2877,14 @@ GLOBAL: std/heap/ptr2 GLOBAL: std/heap/i [program.exports] - CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/Array - CLASS_PROTOTYPE: CArray + CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/CArray - CLASS_PROTOTYPE: Error + CLASS_PROTOTYPE: CArray CLASS_PROTOTYPE: std:error/Error - CLASS_PROTOTYPE: RangeError + CLASS_PROTOTYPE: Error CLASS_PROTOTYPE: std:error/RangeError + CLASS_PROTOTYPE: RangeError FUNCTION_PROTOTYPE: allocate_memory FUNCTION_PROTOTYPE: std:heap/allocate_memory FUNCTION_PROTOTYPE: free_memory @@ -2894,14 +2895,14 @@ FUNCTION_PROTOTYPE: std:heap/set_memory FUNCTION_PROTOTYPE: compare_memory FUNCTION_PROTOTYPE: std:heap/compare_memory - CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: std:map/Map - CLASS_PROTOTYPE: RegExp + CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: std:regexp/RegExp - CLASS_PROTOTYPE: Set + CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set - CLASS_PROTOTYPE: String + CLASS_PROTOTYPE: Set CLASS_PROTOTYPE: std:string/String + CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: parseInt FUNCTION_PROTOTYPE: std:string/parseInt FUNCTION_PROTOTYPE: parseFloat diff --git a/tests/compiler/std/set.wast b/tests/compiler/std/set.wast index 9fcfda31..3492397f 100644 --- a/tests/compiler/std/set.wast +++ b/tests/compiler/std/set.wast @@ -2726,8 +2726,8 @@ FUNCTION_PROTOTYPE: usize GLOBAL: HEAP_BASE CLASS_PROTOTYPE: std:array/Array - CLASS_PROTOTYPE: Array PROPERTY: std:array/Array#length + CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/CArray CLASS_PROTOTYPE: CArray CLASS_PROTOTYPE: std:error/Error @@ -2754,9 +2754,10 @@ CLASS_PROTOTYPE: std:regexp/RegExp CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set - CLASS_PROTOTYPE: Set PROPERTY: std:set/Set#size + CLASS_PROTOTYPE: Set GLOBAL: std:string/EMPTY + FUNCTION_PROTOTYPE: std:string/allocate CLASS_PROTOTYPE: std:string/String CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator @@ -2766,14 +2767,14 @@ FUNCTION_PROTOTYPE: parseFloat GLOBAL: std/set/set [program.exports] - CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/Array - CLASS_PROTOTYPE: CArray + CLASS_PROTOTYPE: Array CLASS_PROTOTYPE: std:array/CArray - CLASS_PROTOTYPE: Error + CLASS_PROTOTYPE: CArray CLASS_PROTOTYPE: std:error/Error - CLASS_PROTOTYPE: RangeError + CLASS_PROTOTYPE: Error CLASS_PROTOTYPE: std:error/RangeError + CLASS_PROTOTYPE: RangeError FUNCTION_PROTOTYPE: allocate_memory FUNCTION_PROTOTYPE: std:heap/allocate_memory FUNCTION_PROTOTYPE: free_memory @@ -2784,14 +2785,14 @@ FUNCTION_PROTOTYPE: std:heap/set_memory FUNCTION_PROTOTYPE: compare_memory FUNCTION_PROTOTYPE: std:heap/compare_memory - CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: std:map/Map - CLASS_PROTOTYPE: RegExp + CLASS_PROTOTYPE: Map CLASS_PROTOTYPE: std:regexp/RegExp - CLASS_PROTOTYPE: Set + CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set - CLASS_PROTOTYPE: String + CLASS_PROTOTYPE: Set CLASS_PROTOTYPE: std:string/String + CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: parseInt FUNCTION_PROTOTYPE: std:string/parseInt FUNCTION_PROTOTYPE: parseFloat diff --git a/tests/compiler/std/string.optimized.wast b/tests/compiler/std/string.optimized.wast new file mode 100644 index 00000000..1c350b2a --- /dev/null +++ b/tests/compiler/std/string.optimized.wast @@ -0,0 +1,419 @@ +(module + (type $i (func (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") + (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) + (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 $std:string/String#startsWith (; 1 ;) (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 $0) + ) + (unreachable) + ) + (if + (i32.eqz + (get_local $1) + ) + (unreachable) + ) + (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 $std:heap/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 $std:string/String#endsWith (; 2 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (if + (i32.eqz + (get_local $1) + ) + (unreachable) + ) + (if + (i32.lt_s + (tee_local $3 + (i32.sub + (select + (tee_local $2 + (select + (get_local $2) + (i32.const 0) + (i32.gt_s + (get_local $2) + (get_local $3) + ) + ) + ) + (tee_local $3 + (i32.load + (get_local $0) + ) + ) + (i32.lt_s + (get_local $2) + (get_local $3) + ) + ) + (tee_local $2 + (i32.load + (get_local $1) + ) + ) + ) + ) + (i32.const 0) + ) + (return + (i32.const 0) + ) + ) + (i32.eqz + (call $std:heap/compare_memory + (i32.add + (i32.add + (get_local $0) + (i32.const 4) + ) + (i32.shl + (get_local $3) + (i32.const 1) + ) + ) + (i32.add + (get_local $1) + (i32.const 4) + ) + (i32.shl + (get_local $2) + (i32.const 1) + ) + ) + ) + ) + (func $std:string/String#indexOf (; 3 ;) (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) + ) + (unreachable) + ) + (set_local $4 + (i32.load + (get_local $1) + ) + ) + (set_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) + ) + ) + ) + (loop $continue|0 + (if + (i32.le_s + (i32.add + (get_local $2) + (get_local $4) + ) + (get_local $5) + ) + (block + (if + (i32.eqz + (call $std:heap/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) + ) + ) + ) + (return + (get_local $2) + ) + ) + (set_local $2 + (i32.add + (get_local $2) + (i32.const 1) + ) + ) + (br $continue|0) + ) + ) + ) + (i32.const -1) + ) + (func $std/string/getString (; 4 ;) (type $i) (result i32) + (get_global $std/string/str) + ) + (func $start (; 5 ;) (type $v) + (local $0 i32) + (if + (i32.ne + (get_global $std/string/str) + (i32.const 8) + ) + (unreachable) + ) + (if + (i32.ne + (i32.load + (get_global $std/string/str) + ) + (i32.const 16) + ) + (unreachable) + ) + (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) + ) + ) + ) + (i32.const 104) + ) + (unreachable) + ) + (if + (i32.eqz + (call $std:string/String#startsWith + (get_global $std/string/str) + (i32.const 48) + (i32.const 0) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (call $std:string/String#endsWith + (get_global $std/string/str) + (i32.const 56) + (i32.const 2147483647) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.ne + (call $std:string/String#indexOf + (tee_local $0 + (get_global $std/string/str) + ) + (i32.const 72) + (i32.const 0) + ) + (i32.const -1) + ) + ) + (unreachable) + ) + (if + (i32.ne + (call $std:string/String#indexOf + (get_global $std/string/str) + (i32.const 88) + (i32.const 0) + ) + (i32.const 2) + ) + (unreachable) + ) + (if + (i32.ne + (call $std:string/String#indexOf + (get_global $std/string/str) + (i32.const 96) + (i32.const 0) + ) + (i32.const -1) + ) + (unreachable) + ) + ) +) diff --git a/tests/compiler/std/string.ts b/tests/compiler/std/string.ts new file mode 100644 index 00000000..02984a37 --- /dev/null +++ b/tests/compiler/std/string.ts @@ -0,0 +1,18 @@ +// preliminary + +var str: string = "hi, I'm a string"; + +// exactly once in static memory +assert(changetype(str) === changetype("hi, I'm a string")); + +assert(str.length == 16); +assert(str.charCodeAt(0) == 0x68); +assert(str.startsWith("hi")); +assert(str.endsWith("string")); +assert(str.includes("I'm")); +assert(str.indexOf(",") == 2); +assert(str.indexOf("x") == -1); + +export function getString(): string { + return str; +} diff --git a/tests/compiler/std/string.wast b/tests/compiler/std/string.wast new file mode 100644 index 00000000..f43aa838 --- /dev/null +++ b/tests/compiler/std/string.wast @@ -0,0 +1,647 @@ +(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)) + (global $HEAP_BASE i32 (i32.const 102)) + (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") + (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.ge_u + (get_local $1) + (i32.load + (get_local $0) + ) + ) + (return + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + ) + (return + (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) + (get_local $1) + ) + (return + (i32.const 0) + ) + ) + (block $break|0 + (loop $continue|0 + (if + (if (result i32) + (i32.ne + (get_local $2) + (i32.const 0) + ) + (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 $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) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (if + (i32.eqz + (i32.ne + (get_local $0) + (i32.const 0) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.ne + (get_local $1) + (i32.const 0) + ) + ) + (unreachable) + ) + (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 $2) + ) + (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 $std:heap/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 $std:string/String#endsWith (; 3 ;) (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) + (if + (i32.eqz + (i32.ne + (get_local $1) + (i32.const 0) + ) + ) + (unreachable) + ) + (set_local $5 + (select + (tee_local $3 + (select + (tee_local $3 + (get_local $2) + ) + (tee_local $4 + (i32.const 0) + ) + (i32.gt_s + (get_local $3) + (get_local $4) + ) + ) + ) + (tee_local $4 + (i32.load + (get_local $0) + ) + ) + (i32.lt_s + (get_local $3) + (get_local $4) + ) + ) + ) + (set_local $6 + (i32.load + (get_local $1) + ) + ) + (set_local $7 + (i32.sub + (get_local $5) + (get_local $6) + ) + ) + (if + (i32.lt_s + (get_local $7) + (i32.const 0) + ) + (return + (i32.const 0) + ) + ) + (return + (i32.eqz + (call $std:heap/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 $6) + (i32.const 1) + ) + ) + ) + ) + ) + (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) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (if + (i32.eqz + (i32.ne + (get_local $1) + (i32.const 0) + ) + ) + (unreachable) + ) + (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) + ) + ) + (block $break|0 + (set_local $9 + (get_local $7) + ) + (loop $continue|0 + (if + (i32.le_s + (i32.add + (get_local $9) + (get_local $8) + ) + (get_local $4) + ) + (block + (if + (i32.eqz + (call $std:heap/compare_memory + (i32.add + (i32.add + (get_local $0) + (i32.const 4) + ) + (i32.shl + (get_local $9) + (i32.const 1) + ) + ) + (i32.add + (get_local $1) + (i32.const 4) + ) + (i32.shl + (get_local $8) + (i32.const 1) + ) + ) + ) + (return + (get_local $9) + ) + ) + (set_local $9 + (i32.add + (get_local $9) + (i32.const 1) + ) + ) + (br $continue|0) + ) + ) + ) + ) + (return + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + ) + (func $std:string/String#includes (; 5 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (return + (i32.ne + (call $std:string/String#indexOf + (get_local $0) + (get_local $1) + (get_local $2) + ) + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + ) + ) + (func $std/string/getString (; 6 ;) (type $i) (result i32) + (return + (get_global $std/string/str) + ) + ) + (func $start (; 7 ;) (type $v) + (if + (i32.eqz + (i32.eq + (get_global $std/string/str) + (i32.const 8) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.load + (get_global $std/string/str) + ) + (i32.const 16) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (call $std:string/String#charCodeAt + (get_global $std/string/str) + (i32.const 0) + ) + (i32.const 104) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (call $std:string/String#startsWith + (get_global $std/string/str) + (i32.const 48) + (i32.const 0) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (call $std:string/String#endsWith + (get_global $std/string/str) + (i32.const 56) + (i32.const 2147483647) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (call $std:string/String#includes + (get_global $std/string/str) + (i32.const 72) + (i32.const 0) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (call $std:string/String#indexOf + (get_global $std/string/str) + (i32.const 88) + (i32.const 0) + ) + (i32.const 2) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (call $std:string/String#indexOf + (get_global $std/string/str) + (i32.const 96) + (i32.const 0) + ) + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + ) + (unreachable) + ) + ) +) +(; +[program.elements] + GLOBAL: NaN + GLOBAL: Infinity + FUNCTION_PROTOTYPE: isNaN + FUNCTION_PROTOTYPE: isFinite + FUNCTION_PROTOTYPE: clz + FUNCTION_PROTOTYPE: ctz + FUNCTION_PROTOTYPE: popcnt + FUNCTION_PROTOTYPE: rotl + FUNCTION_PROTOTYPE: rotr + FUNCTION_PROTOTYPE: abs + FUNCTION_PROTOTYPE: max + FUNCTION_PROTOTYPE: min + FUNCTION_PROTOTYPE: ceil + FUNCTION_PROTOTYPE: floor + FUNCTION_PROTOTYPE: copysign + FUNCTION_PROTOTYPE: nearest + FUNCTION_PROTOTYPE: reinterpret + FUNCTION_PROTOTYPE: sqrt + FUNCTION_PROTOTYPE: trunc + FUNCTION_PROTOTYPE: load + FUNCTION_PROTOTYPE: store + FUNCTION_PROTOTYPE: sizeof + FUNCTION_PROTOTYPE: select + FUNCTION_PROTOTYPE: unreachable + FUNCTION_PROTOTYPE: current_memory + FUNCTION_PROTOTYPE: grow_memory + FUNCTION_PROTOTYPE: changetype + FUNCTION_PROTOTYPE: assert + FUNCTION_PROTOTYPE: i8 + FUNCTION_PROTOTYPE: i16 + FUNCTION_PROTOTYPE: i32 + FUNCTION_PROTOTYPE: i64 + FUNCTION_PROTOTYPE: u8 + FUNCTION_PROTOTYPE: u16 + FUNCTION_PROTOTYPE: u32 + FUNCTION_PROTOTYPE: u64 + FUNCTION_PROTOTYPE: bool + FUNCTION_PROTOTYPE: f32 + FUNCTION_PROTOTYPE: f64 + FUNCTION_PROTOTYPE: isize + FUNCTION_PROTOTYPE: usize + GLOBAL: HEAP_BASE + CLASS_PROTOTYPE: std:array/Array + PROPERTY: std:array/Array#length + CLASS_PROTOTYPE: Array + CLASS_PROTOTYPE: std:array/CArray + CLASS_PROTOTYPE: CArray + CLASS_PROTOTYPE: std:error/Error + CLASS_PROTOTYPE: Error + CLASS_PROTOTYPE: std:error/RangeError + CLASS_PROTOTYPE: RangeError + GLOBAL: std:heap/ALIGN_LOG2 + GLOBAL: std:heap/ALIGN_SIZE + GLOBAL: std:heap/ALIGN_MASK + GLOBAL: std:heap/HEAP_OFFSET + FUNCTION_PROTOTYPE: std:heap/allocate_memory + FUNCTION_PROTOTYPE: allocate_memory + FUNCTION_PROTOTYPE: std:heap/free_memory + FUNCTION_PROTOTYPE: free_memory + FUNCTION_PROTOTYPE: std:heap/copy_memory + FUNCTION_PROTOTYPE: std:heap/move_memory + FUNCTION_PROTOTYPE: move_memory + FUNCTION_PROTOTYPE: std:heap/set_memory + FUNCTION_PROTOTYPE: set_memory + FUNCTION_PROTOTYPE: std:heap/compare_memory + FUNCTION_PROTOTYPE: compare_memory + CLASS_PROTOTYPE: std:map/Map + CLASS_PROTOTYPE: Map + CLASS_PROTOTYPE: std:regexp/RegExp + CLASS_PROTOTYPE: RegExp + CLASS_PROTOTYPE: std:set/Set + PROPERTY: std:set/Set#size + CLASS_PROTOTYPE: Set + GLOBAL: std:string/EMPTY + FUNCTION_PROTOTYPE: std:string/allocate + CLASS_PROTOTYPE: std:string/String + CLASS_PROTOTYPE: String + FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator + FUNCTION_PROTOTYPE: std:string/parseInt + FUNCTION_PROTOTYPE: parseInt + FUNCTION_PROTOTYPE: std:string/parseFloat + FUNCTION_PROTOTYPE: parseFloat + GLOBAL: std/string/str + FUNCTION_PROTOTYPE: std/string/getString +[program.exports] + CLASS_PROTOTYPE: std:array/Array + CLASS_PROTOTYPE: Array + CLASS_PROTOTYPE: std:array/CArray + CLASS_PROTOTYPE: CArray + CLASS_PROTOTYPE: std:error/Error + CLASS_PROTOTYPE: Error + CLASS_PROTOTYPE: std:error/RangeError + CLASS_PROTOTYPE: RangeError + FUNCTION_PROTOTYPE: allocate_memory + FUNCTION_PROTOTYPE: std:heap/allocate_memory + FUNCTION_PROTOTYPE: free_memory + FUNCTION_PROTOTYPE: std:heap/free_memory + FUNCTION_PROTOTYPE: move_memory + FUNCTION_PROTOTYPE: std:heap/move_memory + FUNCTION_PROTOTYPE: set_memory + FUNCTION_PROTOTYPE: std:heap/set_memory + FUNCTION_PROTOTYPE: compare_memory + FUNCTION_PROTOTYPE: std:heap/compare_memory + CLASS_PROTOTYPE: std:map/Map + CLASS_PROTOTYPE: Map + CLASS_PROTOTYPE: std:regexp/RegExp + CLASS_PROTOTYPE: RegExp + CLASS_PROTOTYPE: std:set/Set + CLASS_PROTOTYPE: Set + CLASS_PROTOTYPE: std:string/String + CLASS_PROTOTYPE: String + FUNCTION_PROTOTYPE: parseInt + FUNCTION_PROTOTYPE: std:string/parseInt + FUNCTION_PROTOTYPE: parseFloat + FUNCTION_PROTOTYPE: std:string/parseFloat + FUNCTION_PROTOTYPE: std/string/getString +;)