diff --git a/src/builtins.ts b/src/builtins.ts index eac6391b..d37045f4 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -1567,7 +1567,6 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); type = compiler.currentType; - 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 @@ -1579,6 +1578,21 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty return arg0; } + var abort: ExpressionRef = module.createUnreachable(); + var abortPrototype = compiler.program.elements.get("abort"); + if (abortPrototype && abortPrototype.kind == ElementKind.FUNCTION_PROTOTYPE) { + var abortInstance = (abortPrototype).resolve(); + if (abortInstance && compiler.compileFunction(abortInstance)) { + abort = compiler.makeCall(abortInstance, [ + operands.length == 2 ? compiler.compileExpression(operands[1], compiler.options.usizeType) : compiler.options.usizeType.toNativeZero(module), + compiler.compileStaticString(reportNode.range.source.path), + module.createI32(reportNode.range.line), + module.createI32(reportNode.range.column) + ]); + } + } + compiler.currentType = type.nonNullableType; + if (contextualType == Type.void) { // simplify if dropped anyway switch (compiler.currentType.kind) { @@ -1587,7 +1601,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createUnary(UnaryOp.EqzI32, arg0 ), - module.createUnreachable() + abort ); break; @@ -1597,7 +1611,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createUnary(UnaryOp.EqzI64, arg0 ), - module.createUnreachable() + abort ); break; @@ -1607,7 +1621,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createUnary(compiler.options.target == Target.WASM64 ? UnaryOp.EqzI64 : UnaryOp.EqzI32, arg0 ), - module.createUnreachable() + abort ); break; @@ -1619,7 +1633,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty arg0, module.createF32(0) ), - module.createUnreachable() + abort ); break; @@ -1629,13 +1643,13 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty arg0, module.createF64(0) ), - module.createUnreachable() + abort ); break; case TypeKind.VOID: compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); - ret = module.createUnreachable(); + ret = abort; break; } compiler.currentType = Type.void; @@ -1648,7 +1662,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createUnary(UnaryOp.EqzI32, module.createTeeLocal(tempLocal0.index, arg0) ), - module.createUnreachable(), + abort, module.createGetLocal(tempLocal0.index, NativeType.I32) ); break; @@ -1660,7 +1674,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createUnary(UnaryOp.EqzI64, module.createTeeLocal(tempLocal0.index, arg0) ), - module.createUnreachable(), + abort, module.createGetLocal(tempLocal0.index, NativeType.I64) ); break; @@ -1672,7 +1686,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createUnary(compiler.options.target == Target.WASM64 ? UnaryOp.EqzI64 : UnaryOp.EqzI32, module.createTeeLocal(tempLocal0.index, arg0) ), - module.createUnreachable(), + abort, module.createGetLocal(tempLocal0.index, compiler.options.nativeSizeType) ); break; @@ -1684,7 +1698,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createTeeLocal(tempLocal0.index, arg0), module.createF32(0) ), - module.createUnreachable(), + abort, module.createGetLocal(tempLocal0.index, NativeType.F32) ); break; @@ -1696,14 +1710,14 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createTeeLocal(tempLocal0.index, arg0), module.createF64(0) ), - module.createUnreachable(), + abort, module.createGetLocal(tempLocal0.index, NativeType.F64) ); break; case TypeKind.VOID: compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); - ret = module.createUnreachable(); + ret = abort; break; } } diff --git a/src/compiler.ts b/src/compiler.ts index 1ec3f47c..f64fcfe5 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2862,27 +2862,7 @@ export class Compiler extends DiagnosticEmitter { 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); + return this.compileStaticString((expression).value); // case LiteralKind.OBJECT: // case LiteralKind.REGEXP: @@ -2891,6 +2871,29 @@ export class Compiler extends DiagnosticEmitter { throw new Error("not implemented"); } + compileStaticString(stringValue: string): ExpressionRef { + 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); + } + compileNewExpression(expression: NewExpression, contextualType: Type): ExpressionRef { var resolved = this.program.resolveExpression(expression.expression, this.currentFunction); // reports if (resolved) { diff --git a/src/diagnostics.ts b/src/diagnostics.ts index cee2717e..b82236b9 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -121,20 +121,12 @@ export function formatDiagnosticMessage(message: DiagnosticMessage, useColors: b sb.push(context); } sb.push("\n"); - var pos = range.start; - var line = 1; - var column = 1; - while (pos-- > 0) - if (text.charCodeAt(pos) == CharCode.LINEFEED) - line++; - else if (line == 1) - column++; sb.push(" in "); sb.push(range.source.path); sb.push("("); - sb.push(line.toString(10)); + sb.push(range.line.toString(10)); sb.push(","); - sb.push(column.toString(10)); + sb.push(range.column.toString(10)); sb.push(")"); } return sb.join(""); diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 8cc6c8d3..4ccf3f69 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -293,6 +293,27 @@ export class Range { get atStart(): Range { return new Range(this.source, this.start, this.start); } get atEnd(): Range { return new Range(this.source, this.end, this.end); } + get line(): i32 { + var text = this.source.text; + var pos = this.start; + var line = 1; + while (pos-- > 0) + if (text.charCodeAt(pos) == CharCode.LINEFEED) + line++; + return line; + } + + get column(): i32 { + var text = this.source.text; + var pos = this.start; + var column = 1; + while (pos-- > 0) + if (text.charCodeAt(pos) == CharCode.LINEFEED) + break; + column++; + return column; + } + toString(): string { return this.source.text.substring(this.start, this.end); } diff --git a/std/assembly/string.ts b/std/assembly/string.ts index d3adf31c..001e044e 100644 --- a/std/assembly/string.ts +++ b/std/assembly/string.ts @@ -1,12 +1,14 @@ const EMPTY: String = changetype(""); +const HEAD: usize = 4; + function allocate(length: i32): String { - var ptr = allocate_memory(4 + length * 2); + assert(length >= 1); + var ptr = allocate_memory(HEAD + length * 2); store(ptr, length); return changetype(ptr); } -@unmanaged export class String { readonly length: i32; @@ -16,23 +18,27 @@ export class String { if (pos >= this.length) return EMPTY; var out = allocate(1); - store(changetype(out), load(changetype(this) + (pos << 1), 4), 4); + store( + changetype(out), + load(changetype(this) + (pos << 1), HEAD), + HEAD + ); return out; } charCodeAt(pos: i32): i32 { if (pos >= this.length) return -1; // NaN - return load(changetype(this) + (pos << 1), 4); + return load(changetype(this) + (pos << 1), HEAD); } codePointAt(pos: i32): i32 { if (pos >= this.length) return -1; // undefined - var first = load(changetype(this) + (pos << 1), 4); + 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), 4); + var second = load(changetype(this) + ((pos + 1) << 1), HEAD); if (second < 0xDC00 || second > 0xDFFF) return first; return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000; @@ -45,9 +51,19 @@ export class String { var thisLen: isize = this.length; var otherLen: isize = other.length; var len: usize = thisLen + otherLen; + if (len == 0) + return EMPTY; 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); + move_memory( + changetype(out) + HEAD, + changetype(this) + HEAD, + thisLen << 1 + ); + move_memory( + changetype(out) + HEAD + (thisLen << 1), + changetype(other) + HEAD, + otherLen << 1 + ); return out; } @@ -58,7 +74,11 @@ export class String { var start: isize = end - searchLength; if (start < 0) return false; - return !compare_memory(changetype(this) + 4 + (start << 1), changetype(searchString) + 4, searchLength << 1); + return !compare_memory( + changetype(this) + HEAD + (start << 1), + changetype(searchString) + HEAD, + searchLength << 1 + ); } @operator("==") @@ -69,7 +89,11 @@ export class String { return false; if (this.length != other.length) return false; - return !compare_memory(changetype(this) + 4, changetype(other) + 4, (this.length << 1)); + return !compare_memory( + changetype(this) + HEAD, + changetype(other) + HEAD, + (this.length << 1) + ); } includes(searchString: String, position: i32 = 0): bool { @@ -83,7 +107,11 @@ export class String { 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(changetype(this) + 4 + (k << 1), changetype(searchString) + 4, searchLen << 1)) + if (!compare_memory( + changetype(this) + HEAD + (k << 1), + changetype(searchString) + HEAD, + searchLen << 1) + ) return k; return -1; } @@ -97,7 +125,11 @@ export class String { var searchLength: isize = searchString.length; if (searchLength + start > len) return false; - return !compare_memory(changetype(this) + 4 + (start << 1), changetype(searchString) + 4, searchLength << 1); + return !compare_memory( + changetype(this) + HEAD + (start << 1), + changetype(searchString) + HEAD, + searchLength << 1 + ); } substr(start: i32, length: i32 = i32.MAX_VALUE): String { @@ -108,10 +140,14 @@ export class String { if (intStart < 0) intStart = max(size + intStart, 0); var resultLength: isize = min(max(end, 0), size - intStart); - if (resultLength < 0) + if (resultLength <= 0) return EMPTY; var out = allocate(resultLength); - move_memory(changetype(out) + 4, changetype(this) + 4 + (intStart << 1), resultLength << 1); + move_memory( + changetype(out) + HEAD, + changetype(this) + HEAD + (intStart << 1), + resultLength << 1 + ); return out; } @@ -128,17 +164,21 @@ export class String { if (!from && to == this.length) return this; var out = allocate(len); - move_memory(changetype(out) + 4, changetype(this) + 4 + (from << 1), len << 1); + move_memory( + changetype(out) + HEAD, + changetype(this) + HEAD + (from << 1), + len << 1 + ); return out; } trim(): String { assert(this != null); var length: usize = this.length; - while (length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (length << 1), 4))) + while (length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (length << 1), HEAD))) --length; var start: usize = 0; - while (start < length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (start << 1), 4))) { + while (start < length && isWhiteSpaceOrLineTerminator(load(changetype(this) + (start << 1), HEAD))) { ++start; --length; } if (!length) @@ -146,7 +186,11 @@ export class String { if (!start && length == this.length) return this; var out = allocate(length); - move_memory(changetype(out) + 4, changetype(this) + 4 + (start << 1), length << 1); + move_memory( + changetype(out) + HEAD, + changetype(this) + HEAD + (start << 1), + length << 1 + ); return out; } @@ -154,27 +198,37 @@ export class String { assert(this != null); var start: isize = 0; var len: isize = this.length; - while (start < len && isWhiteSpaceOrLineTerminator(load(changetype(this) + (start << 1), 4))) + 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) + 4, changetype(this) + 4 + (start << 1), outLen << 1); + move_memory( + changetype(out) + HEAD, + changetype(this) + HEAD + (start << 1), + outLen << 1 + ); return out; } trimRight(): String { assert(this != null); var len: isize = this.length; - while (len > 0 && isWhiteSpaceOrLineTerminator(load(changetype(this) + (len << 1), 4))) + 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) + 4, changetype(this) + 4, len << 1); + move_memory( + changetype(out) + HEAD, + changetype(this) + HEAD, + len << 1 + ); return out; } } diff --git a/tests/compiler/std/abort.ts b/tests/compiler/std/abort.ts new file mode 100644 index 00000000..d29a3602 --- /dev/null +++ b/tests/compiler/std/abort.ts @@ -0,0 +1,11 @@ +var abortCalled = false; + +@global +function abort(message: string | null, filename: string, line: i32, column: i32): void { + abortCalled = true; +} + +assert(false, "this is ok"); + +if (!abortCalled) + unreachable();