diff --git a/.gitignore b/.gitignore index 0157738..3f68ba2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ !*/ !LICENSE +success.txt +failed.txt balde *.out *.log diff --git a/bali.nimble b/bali.nimble index 719049e..757c3a4 100644 --- a/bali.nimble +++ b/bali.nimble @@ -6,7 +6,7 @@ description = "The Bali JavaScript Engine" license = "GPL3" srcDir = "src" backend = "cpp" -bin = @["balde", "test262_v2", "test262"] +bin = @["balde", "test262"] installExt = @["nim"] binDir = "bin" diff --git a/src/bali/grammar/parser.nim b/src/bali/grammar/parser.nim index e1f1f9f..4462686 100644 --- a/src/bali/grammar/parser.nim +++ b/src/bali/grammar/parser.nim @@ -695,12 +695,13 @@ proc parseThrow*(parser: Parser): Option[Statement] = var throwStr: Option[string] + throwIdent: Option[string] throwErr: Option[void] # TODO: implement stuff like `throw new URIError();` while not parser.tokenizer.eof: let next = parser.tokenizer.next() - if next.kind == TokenKind.Whitespace and next.whitespace.contains(strutils.Newlines): + if next.kind == TokenKind.Whitespace and next.isNewline(): parser.error UnexpectedToken, "no line break is allowed between 'throw' and its expression" @@ -708,10 +709,14 @@ proc parseThrow*(parser: Parser): Option[Statement] = throwStr = some(next.str) break - if !throwStr and !throwErr: + if next.kind == TokenKind.Identifier: + throwIdent = some(next.ident) + break + + if !throwStr and !throwErr and !throwIdent: parser.error Other, "throw statement is missing an expression" - some(throwError(throwStr, throwErr)) + some(throwError(throwStr, throwErr, throwIdent)) proc parseReassignment*(parser: Parser, ident: string): Option[Statement] = info "parser: parsing re-assignment to identifier: " & ident diff --git a/src/bali/grammar/statement.nim b/src/bali/grammar/statement.nim index f95b385..19e049d 100644 --- a/src/bali/grammar/statement.nim +++ b/src/bali/grammar/statement.nim @@ -105,7 +105,7 @@ type reIdentifier*: string reAtom*: MAtom of ThrowError: - error*: tuple[str: Option[string], exc: Option[void]] + error*: tuple[str: Option[string], exc: Option[void], ident: Option[string]] of BinaryOp: binLeft*, binRight*: Statement op*: BinaryOperation = BinaryOperation.Invalid @@ -225,14 +225,15 @@ proc pushImmExpr*(args: var PositionedArguments, expr: Statement) {.inline.} = {.push checks: off, inline.} proc throwError*( errorStr: Option[string], errorExc: Option[void], + errorIdent: Option[string] ): Statement = - if *errorStr and *errorExc: + if *errorStr and *errorExc and *errorIdent: raise newException( ValueError, "Both `errorStr` and `errorExc` are full containers - something has went horribly wrong.", ) - Statement(kind: ThrowError, error: (str: errorStr, exc: errorExc)) + Statement(kind: ThrowError, error: (str: errorStr, exc: errorExc, ident: errorIdent)) proc createImmutVal*(name: string, atom: MAtom): Statement = Statement(kind: CreateImmutVal, imIdentifier: name, imAtom: atom) diff --git a/src/bali/runtime/interpreter.nim b/src/bali/runtime/interpreter.nim index 5d1c4f0..ee734ca 100644 --- a/src/bali/runtime/interpreter.nim +++ b/src/bali/runtime/interpreter.nim @@ -391,6 +391,11 @@ proc generateIR*( runtime.ir.passArgument(runtime.index("error_msg", internalIndex(stmt))) runtime.ir.call("BALI_THROWERROR") + elif *stmt.error.ident: + runtime.ir.passArgument(runtime.index(&stmt.error.ident, defaultParams(fn))) + runtime.ir.call("BALI_THROWERROR") + else: + unreachable of BinaryOp: info "emitter: emitting IR for binary operation" runtime.expand(fn, stmt, internal) @@ -855,7 +860,7 @@ proc run*(runtime: Runtime) = console.generateStdIR(runtime) math.generateStdIR(runtime) uri.generateStdIR(runtime) - generateErrorsStdIR(runtime) + errors_ir.generateStdIR(runtime) base64.generateStdIR(runtime) json.generateStdIR(runtime) encodeUri.generateStdIR(runtime) diff --git a/src/bali/stdlib/builtins/test262.nim b/src/bali/stdlib/builtins/test262.nim index 35a3179..dad9c49 100644 --- a/src/bali/stdlib/builtins/test262.nim +++ b/src/bali/stdlib/builtins/test262.nim @@ -1,4 +1,4 @@ -## Test262 required builtins +## Test262 required builtins/harnesses ## import std/[strutils, math, options, logging, tables] @@ -7,7 +7,7 @@ import mirage/runtime/prelude import bali/runtime/[normalize, bridge] import bali/runtime/abstract/to_string import bali/runtime/[arguments, types, atom_helpers] -import bali/stdlib/errors +import bali/stdlib/errors_common import bali/internal/sugar import pretty diff --git a/src/bali/stdlib/errors.nim b/src/bali/stdlib/errors.nim index f44b903..2839402 100644 --- a/src/bali/stdlib/errors.nim +++ b/src/bali/stdlib/errors.nim @@ -4,44 +4,15 @@ import std/[logging] import mirage/ir/generator import mirage/runtime/prelude import bali/grammar/errors -import bali/runtime/normalize +import bali/runtime/[arguments, atom_helpers, normalize] +import bali/runtime/abstract/to_string import bali/internal/sugar import bali/runtime/types - -type - DeathCallback* = proc(vm: PulsarInterpreter, exitCode: int = 1) - JSException* = ref object of RuntimeException - name: string = "" - -proc DefaultDeathCallback(vm: PulsarInterpreter, exitCode: int = 1) = - quit(exitCode) - -var deathCallback*: DeathCallback = DefaultDeathCallback +import bali/stdlib/errors_common proc setDeathCallback*(fn: DeathCallback) {.inline.} = deathCallback = fn -proc generateMessage*(exc: JSException, err: string): string = - var msg = "Uncaught " - - if exc.name.len > 0: - msg &= exc.name & ':' - - msg & err - -proc jsException*(msg: string): JSException {.inline.} = - var exc = JSException() - exc.message = exc.generateMessage(msg) - - exc - -proc logTracebackAndDie*(runtime: Runtime, exitCode: int = 1) = - let traceback = runtime.vm.generateTraceback() - assert *traceback, "Mirage failed to generate traceback!" - - stderr.write &traceback & '\n' - deathCallback(runtime.vm, exitCode) - proc typeError*(runtime: Runtime, message: string, exitCode: int = 1) {.inline.} = ## Meant for other Bali stdlib methods to use. runtime.vm.throw(jsException("TypeError: " & message)) @@ -62,13 +33,3 @@ proc syntaxError*( runtime: Runtime, error: ParseError, exitCode: int = 1 ) {.inline.} = runtime.syntaxError(error.message, exitCode) - -proc generateErrorsStdIr*(runtime: Runtime) = - info "errors: generate IR interface" - - runtime.vm.registerBuiltin( - "BALI_THROWERROR", - proc(op: Operation) = - runtime.vm.throw(jsException(&runtime.vm.registers.callArgs[0].getStr())) - runtime.logTracebackAndDie(), - ) diff --git a/src/bali/stdlib/errors_common.nim b/src/bali/stdlib/errors_common.nim new file mode 100644 index 0000000..ff49e73 --- /dev/null +++ b/src/bali/stdlib/errors_common.nim @@ -0,0 +1,42 @@ +import std/[importutils, strutils] +import mirage/runtime/prelude +import bali/runtime/types +import bali/internal/sugar + +privateAccess(PulsarInterpreter) + +type + JSException* = ref object of RuntimeException + name: string = "" + + DeathCallback* = proc(vm: PulsarInterpreter, exitCode: int = 1) + +proc DefaultDeathCallback(vm: PulsarInterpreter, exitCode: int = 1) = + quit(exitCode) + +var deathCallback*: DeathCallback = DefaultDeathCallback + +proc generateMessage*(exc: JSException, err: string): string = + var msg = "Uncaught " + + if exc.name.len > 0: + msg &= exc.name & ':' + + msg & err + +proc jsException*(msg: string): JSException {.inline.} = + var exc = JSException() + exc.message = exc.generateMessage(msg) + + exc + +proc logTracebackAndDie*(runtime: Runtime, exitCode: int = 1) = + let traceback = runtime.vm.generateTraceback() + assert *traceback, "Mirage failed to generate traceback!" + + if runtime.vm.trace.exception.message.contains(runtime.test262.negative.`type`): + stdout.write(&traceback & '\n') + deathCallback(runtime.vm, exitCode) + else: + stderr.write &traceback & '\n' + deathCallback(runtime.vm, exitCode) diff --git a/src/bali/stdlib/errors_ir.nim b/src/bali/stdlib/errors_ir.nim new file mode 100644 index 0000000..14f6245 --- /dev/null +++ b/src/bali/stdlib/errors_ir.nim @@ -0,0 +1,23 @@ +## Implementation of the error throw IR builtin +## Refactored here because Nim hates me +## Authors: +## Trayambak Rai (xtrayambak at disroot dot org) +import std/[logging] +import mirage/runtime/prelude +import bali/stdlib/errors_common +import bali/runtime/[arguments, atom_helpers, types] +import bali/runtime/abstract/to_string +import bali/internal/sugar + +proc generateStdIr*(runtime: Runtime) = + info "errors: generate IR interface" + + runtime.vm.registerBuiltin( + "BALI_THROWERROR", + proc(op: Operation) = + let atom = runtime.argument(1, required = true, message = "BUG: BALI_THROWERROR got {nargs} atoms, expected one!") + runtime.vm.throw(jsException( + runtime.ToString(&atom) + )) + runtime.logTracebackAndDie(), + ) diff --git a/src/bali/stdlib/prelude.nim b/src/bali/stdlib/prelude.nim index b34f6ff..82d0999 100644 --- a/src/bali/stdlib/prelude.nim +++ b/src/bali/stdlib/prelude.nim @@ -1,5 +1,5 @@ -import ./[console, math, uri, errors, json, constants, date] +import ./[console, math, uri, errors, errors_ir, errors_common, json, constants, date] import ./builtins/[base64, parse_int, test262, encode_uri] export console, math, uri, parse_int, errors, test262, base64, json, constants, date, - encode_uri + encode_uri, errors_ir, errors_common diff --git a/src/test262_v2.nim b/src/test262_v2.nim deleted file mode 100644 index a32b6ab..0000000 --- a/src/test262_v2.nim +++ /dev/null @@ -1,99 +0,0 @@ -## Test262 runner, take 2 -when not defined(posix): - {.error: "test262_v2 cannot run on a non-POSIX compliant system.".} - -import std/[os, strutils, posix, locks, logging, importutils] -import pkg/[colored_logger] -import bali/grammar/prelude -import mirage/runtime/pulsar/interpreter -import bali/stdlib/errors -import bali/internal/sugar -import bali/runtime/prelude - -privateAccess(PulsarInterpreter) - -const BASE_DIR = "test262/test" - -type - AgentParentSharedBuffer* = object - statusLock*, consoleBufferLock*: Lock - - status* {.guard: statusLock.}: bool = false - consoleBuffer* {.guard: consoleBufferLock.}: string - sourceBuffer*: string - fileName*: string - - Report* = object - passing*: seq[string] - failing*: seq[string] - total*: uint - -proc dump*(report: Report) = - echo "Result: $1/$2 error$3" % [$report.failing.len, $report.total, if report.total != 1: "s" else: newString(0)] - -proc startAgent*(buffer: pointer) = - var buffer = cast[ptr AgentParentSharedBuffer](buffer) - - let parser = newParser(buffer.sourceBuffer) - let ast = parser.parse() - - var runtime = newRuntime(buffer.fileName, ast, opts = InterpreterOpts(test262: true)) - runtime.run() - - withLock buffer[].statusLock: - buffer[].status = runtime.vm.trace == nil - -proc summonAgent*(report: var Report, file: string) = - var buffer: AgentParentSharedBuffer - buffer.statusLock.initLock() - buffer.consoleBufferLock.initLock() - withLock buffer.statusLock: buffer.status = false - withLock buffer.consoleBufferLock: buffer.consoleBuffer = newString(0) - buffer.sourceBuffer = readFile(file) - buffer.fileName = file - - startAgent(buffer.addr) - withLock buffer.statusLock: - if buffer.status: - info file & " has passed successfully." - report.passing &= file - else: - warn file & " has failed execution." - report.failing &= file - - #var thr: Thread[pointer] - #createThread(thr, startAgent, buffer.addr) - -proc main {.inline.} = - setDeathCallback( - proc(_: PulsarInterpreter, exitCode: int = 1) = - # A temporary hack around exception traces immediately quitting - warn "Interpreter instance wanted to exit with code: " & $exitCode - ) - var - report: Report - files: seq[string] - skipped: seq[string] - - let head = paramStr(1) - - for file in walkDirRec(BASE_DIR / head): - if not fileExists(file): - continue - if file.contains("harness"): - continue - if not file.endsWith(".js"): - skipped &= file - continue - - files &= file - - report.total = files.len.uint - - for file in files: - report.summonAgent(file) - - report.dump() - -when isMainModule: - main() diff --git a/tests/data/run_nim_on_bali.js b/tests/data/run_nim_on_bali.js new file mode 100644 index 0000000..0838113 --- /dev/null +++ b/tests/data/run_nim_on_bali.js @@ -0,0 +1,85 @@ +/* Generated by the Nim Compiler v2.2.0 */ +var framePtr = null; +var excHandler = 0; +var lastJSError = null; + +function toJSStr(s_p0) { + var result_33556919 = null; + + var res_33556973 = newSeq__flfgrz_u2516((s_p0).length); + var i_33556974 = 0; + var j_33556975 = 0; + Label1: { + Label2: while (true) { + if (!(i_33556974 < (s_p0).length)) break Label2; + var c_33556976 = s_p0[i_33556974]; + if ((c_33556976 < 128)) { + res_33556973[j_33556975] = String.fromCharCode(c_33556976); + i_33556974 += 1; + } + else { + var helper_33557002 = newSeq__flfgrz_u2516(0); + Label3: { + Label4: while (true) { + if (!true) break Label4; + var code_33557003 = c_33556976.toString(16); + if ((((code_33557003) == null ? 0 : (code_33557003).length) == 1)) { + helper_33557002.push("%0");; + } + else { + helper_33557002.push("%");; + } + + helper_33557002.push(code_33557003);; + i_33556974 += 1; + if ((((s_p0).length <= i_33556974) || (s_p0[i_33556974] < 128))) { + break Label3; + } + + c_33556976 = s_p0[i_33556974]; + } + }; +++excHandler; + try { + res_33556973[j_33556975] = decodeURIComponent(helper_33557002.join("")); +--excHandler; +} catch (EXCEPTION) { + var prevJSError = lastJSError; + lastJSError = EXCEPTION; + --excHandler; + res_33556973[j_33556975] = helper_33557002.join(""); + lastJSError = prevJSError; + } finally { + } + } + + j_33556975 += 1; + } + }; + if (res_33556973.length < j_33556975) { for (var i = res_33556973.length ; i < j_33556975 ; ++i) res_33556973.push(null); } + else { res_33556973.length = j_33556975; }; + result_33556919 = res_33556973.join(""); + + return result_33556919; + +} + +function rawEcho() { + var buf = ""; + for (var i = 0; i < arguments.length; ++i) { + buf += toJSStr(arguments[i]); + } + console.log(buf); + + + +} + +function newSeq__flfgrz_u2516(len_p0) { + var result_33556952 = []; + + result_33556952 = new Array(len_p0); for (var i = 0 ; i < len_p0 ; ++i) { result_33556952[i] = null; } + return result_33556952; + +} +rawEcho([104,105,32,116,104,101,114,101,33]); diff --git a/tests/data/run_nim_on_bali.nim b/tests/data/run_nim_on_bali.nim new file mode 100644 index 0000000..8d03aa6 --- /dev/null +++ b/tests/data/run_nim_on_bali.nim @@ -0,0 +1 @@ +echo "hi there!" diff --git a/tests/data/throw-new-exc.js b/tests/data/throw-new-exc.js new file mode 100644 index 0000000..38ecbdf --- /dev/null +++ b/tests/data/throw-new-exc.js @@ -0,0 +1 @@ +throw undefined;