diff options
Diffstat (limited to 'lib/system/jssys.nim')
-rw-r--r--[-rwxr-xr-x] | lib/system/jssys.nim | 908 |
1 files changed, 527 insertions, 381 deletions
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 789e39d6d..5599240fd 100755..100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -1,37 +1,43 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -when defined(nodejs): - proc alert*(s: cstring) {.importc: "console.log", nodecl.} -else: - proc alert*(s: cstring) {.importc, nodecl.} +include system/indexerrors +import std/private/miscdollars -proc log*(s: cstring) {.importc: "console.log", nodecl.} +proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.} type - PSafePoint = ptr TSafePoint - TSafePoint {.compilerproc, final.} = object + PSafePoint = ptr SafePoint + SafePoint {.compilerproc, final.} = object prev: PSafePoint # points to next safe point - exc: ref E_Base + exc: ref Exception - PCallFrame = ptr TCallFrame - TCallFrame {.importc, nodecl, final.} = object + PCallFrame = ptr CallFrame + CallFrame {.importc, nodecl, final.} = object prev: PCallFrame - procname: CString + procname: cstring line: int # current line number - filename: CString + filename: cstring + + PJSError = ref object + columnNumber {.importc.}: int + fileName {.importc.}: cstring + lineNumber {.importc.}: int + message {.importc.}: cstring + stack {.importc.}: cstring + + JSRef = ref RootObj # Fake type. var framePtr {.importc, nodecl, volatile.}: PCallFrame - excHandler {.importc, nodecl, volatile.}: PSafePoint = nil - # list of exception handlers - # a global variable for the root of all try blocks + excHandler {.importc, nodecl, volatile.}: int = 0 + lastJSError {.importc, nodecl, volatile.}: PJSError = nil {.push stacktrace: off, profiler:off.} proc nimBoolToStr(x: bool): string {.compilerproc.} = @@ -42,21 +48,46 @@ proc nimCharToStr(x: char): string {.compilerproc.} = result = newString(1) result[0] = x +proc isNimException(): bool {.asmNoStackFrame.} = + {.emit: "return `lastJSError` && `lastJSError`.m_type;".} + +proc getCurrentException*(): ref Exception {.compilerRtl, benign.} = + if isNimException(): result = cast[ref Exception](lastJSError) + proc getCurrentExceptionMsg*(): string = - if excHandler != nil: return $excHandler.exc.msg + if lastJSError != nil: + if isNimException(): + return cast[Exception](lastJSError).msg + else: + var msg: cstring + {.emit: """ + if (`lastJSError`.message !== undefined) { + `msg` = `lastJSError`.message; + } + """.} + if not msg.isNil: + return $msg return "" +proc setCurrentException*(exc: ref Exception) = + lastJSError = cast[PJSError](exc) + +proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = + ## Used to set up exception handling for closure iterators + setCurrentException(e) + proc auxWriteStackTrace(f: PCallFrame): string = type - TTempFrame = tuple[procname: CString, line: int] + TempFrame = tuple[procname: cstring, line: int, filename: cstring] var it = f i = 0 total = 0 - tempFrames: array [0..63, TTempFrame] + tempFrames: array[0..63, TempFrame] while it != nil and i <= high(tempFrames): tempFrames[i].procname = it.procname tempFrames[i].line = it.line + tempFrames[i].filename = it.filename inc(i) inc(total) it = it.prev @@ -70,63 +101,86 @@ proc auxWriteStackTrace(f: PCallFrame): string = add(result, $(total-i)) add(result, " calls omitted) ...\n") for j in countdown(i-1, 0): + result.toLocation($tempFrames[j].filename, tempFrames[j].line, 0) + add(result, " at ") add(result, tempFrames[j].procname) - if tempFrames[j].line > 0: - add(result, ", line: ") - add(result, $tempFrames[j].line) add(result, "\n") proc rawWriteStackTrace(): string = - if framePtr == nil: + if framePtr != nil: + result = "Traceback (most recent call last)\n" & auxWriteStackTrace(framePtr) + else: result = "No stack traceback available\n" + +proc writeStackTrace() = + var trace = rawWriteStackTrace() + trace.setLen(trace.len - 1) + echo trace + +proc getStackTrace*(): string = rawWriteStackTrace() +proc getStackTrace*(e: ref Exception): string = e.trace + +proc unhandledException(e: ref Exception) {. + compilerproc, asmNoStackFrame.} = + var buf = "" + if e.msg.len != 0: + add(buf, "Error: unhandled exception: ") + add(buf, e.msg) else: - result = "Traceback (most recent call last)\n"& auxWriteStackTrace(framePtr) + add(buf, "Error: unhandled exception") + add(buf, " [") + add(buf, e.name) + add(buf, "]\n") + when NimStackTrace: + add(buf, rawWriteStackTrace()) + let cbuf = cstring(buf) + when NimStackTrace: framePtr = nil - -proc raiseException(e: ref E_Base, ename: cstring) {. - compilerproc, noStackFrame.} = + {.emit: """ + if (typeof(Error) !== "undefined") { + throw new Error(`cbuf`); + } + else { + throw `cbuf`; + } + """.} + +proc raiseException(e: ref Exception, ename: cstring) {. + compilerproc, asmNoStackFrame.} = e.name = ename - if excHandler != nil: - excHandler.exc = e - else: - when nimrodStackTrace: - var buf = rawWriteStackTrace() - else: - var buf = "" - if e.msg != nil and e.msg[0] != '\0': - add(buf, "Error: unhandled exception: ") - add(buf, e.msg) - else: - add(buf, "Error: unhandled exception") - add(buf, " [") - add(buf, ename) - add(buf, "]\n") - alert(buf) - asm """throw `e`;""" - -proc reraiseException() {.compilerproc, noStackFrame.} = - if excHandler == nil: - raise newException(ENoExceptionToReraise, "no exception to reraise") + if excHandler == 0: + unhandledException(e) + when NimStackTrace: + e.trace = rawWriteStackTrace() + {.emit: "throw `e`;".} + +proc reraiseException() {.compilerproc, asmNoStackFrame.} = + if lastJSError == nil: + raise newException(ReraiseDefect, "no exception to reraise") else: - asm """throw excHandler.exc;""" + if excHandler == 0: + if isNimException(): + unhandledException(cast[ref Exception](lastJSError)) -proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} = - raise newException(EOverflow, "over- or underflow") + {.emit: "throw lastJSError;".} -proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn.} = - raise newException(EDivByZero, "divison by zero") +proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerproc.} = + raise newException(OverflowDefect, "over- or underflow") + +proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn, compilerproc.} = + raise newException(DivByZeroDefect, "division by zero") proc raiseRangeError() {.compilerproc, noreturn.} = - raise newException(EOutOfRange, "value out of range") + raise newException(RangeDefect, "value out of range") -proc raiseIndexError() {.compilerproc, noreturn.} = - raise newException(EInvalidIndex, "index out of bounds") +proc raiseIndexError(i, a, b: int) {.compilerproc, noreturn.} = + raise newException(IndexDefect, formatErrorIndexBound(int(i), int(a), int(b))) -proc raiseFieldError(f: string) {.compilerproc, noreturn.} = - raise newException(EInvalidField, f & " is not accessible") +proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noreturn.} = + raise newException(FieldDefect, formatFieldDefect(f, discVal)) -proc SetConstr() {.varargs, noStackFrame, compilerproc.} = - asm """ +proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = + {.emit: """ var result = {}; for (var i = 0; i < arguments.length; ++i) { var x = arguments[i]; @@ -139,358 +193,329 @@ proc SetConstr() {.varargs, noStackFrame, compilerproc.} = } } return result; - """ - -proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} = - asm """ - var result = []; - for (var i = 0; i < `c`.length; ++i) { - result[i] = `c`.charCodeAt(i); + """.} + +proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} = + {.emit: """ + var result = []; + for (var i = 0; i < `c`.length; ++i) { + result[i] = `c`.charCodeAt(i); + } + return result; + """.} + +proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} = + {.emit: """ + var ln = `c`.length; + var result = new Array(ln); + var r = 0; + for (var i = 0; i < ln; ++i) { + var ch = `c`.charCodeAt(i); + + if (ch < 128) { + result[r] = ch; } - result[result.length] = 0; // terminating zero - return result; - """ - -proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} = - asm """ - var len = `s`.length-1; - var result = new Array(len); - var fcc = String.fromCharCode; - for (var i = 0; i < len; ++i) { - result[i] = fcc(`s`[i]); + else { + if (ch < 2048) { + result[r] = (ch >> 6) | 192; + } + else { + if (ch < 55296 || ch >= 57344) { + result[r] = (ch >> 12) | 224; + } + else { + ++i; + ch = 65536 + (((ch & 1023) << 10) | (`c`.charCodeAt(i) & 1023)); + result[r] = (ch >> 18) | 240; + ++r; + result[r] = ((ch >> 12) & 63) | 128; + } + ++r; + result[r] = ((ch >> 6) & 63) | 128; + } + ++r; + result[r] = (ch & 63) | 128; } - return result.join(""); - """ - -proc mnewString(len: int): string {.noStackFrame, compilerproc.} = - asm """ - var result = new Array(`len`+1); - result[0] = 0; - result[`len`] = 0; + ++r; + } + return result; + """.} + +proc toJSStr(s: string): cstring {.compilerproc.} = + proc fromCharCode(c: char): cstring {.importc: "String.fromCharCode".} + proc join(x: openArray[cstring]; d = cstring""): cstring {. + importcpp: "#.join(@)".} + proc decodeURIComponent(x: cstring): cstring {. + importc: "decodeURIComponent".} + + proc toHexString(c: char; d = 16): cstring {.importcpp: "#.toString(@)".} + + proc log(x: cstring) {.importc: "console.log".} + + var res = newSeq[cstring](s.len) + var i = 0 + var j = 0 + while i < s.len: + var c = s[i] + if c < '\128': + res[j] = fromCharCode(c) + inc i + else: + var helper = newSeq[cstring]() + while true: + let code = toHexString(c) + if code.len == 1: + helper.add cstring"%0" + else: + helper.add cstring"%" + helper.add code + inc i + if i >= s.len or s[i] < '\128': break + c = s[i] + try: + res[j] = decodeURIComponent join(helper) + except: + res[j] = join(helper) + inc j + setLen(res, j) + result = join(res) + +proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = + {.emit: """ + var result = new Array(`len`); + for (var i = 0; i < `len`; i++) {result[i] = 0;} return result; - """ + """.} -proc SetCard(a: int): int {.compilerproc, noStackFrame.} = +proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = # argument type is a fake - asm """ + {.emit: """ var result = 0; for (var elem in `a`) { ++result; } return result; - """ + """.} -proc SetEq(a, b: int): bool {.compilerproc, noStackFrame.} = - asm """ +proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} = + {.emit: """ for (var elem in `a`) { if (!`b`[elem]) return false; } for (var elem in `b`) { if (!`a`[elem]) return false; } return true; - """ + """.} -proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} = - asm """ +proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} = + {.emit: """ for (var elem in `a`) { if (!`b`[elem]) return false; } return true; - """ + """.} proc SetLt(a, b: int): bool {.compilerproc.} = result = SetLe(a, b) and not SetEq(a, b) -proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} = - asm """ +proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} = + {.emit: """ var result = {}; for (var elem in `a`) { if (`b`[elem]) { result[elem] = true; } } return result; - """ + """.} -proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} = - asm """ +proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} = + {.emit: """ var result = {}; for (var elem in `a`) { result[elem] = true; } for (var elem in `b`) { result[elem] = true; } return result; - """ + """.} -proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} = - asm """ +proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} = + {.emit: """ var result = {}; for (var elem in `a`) { if (!`b`[elem]) { result[elem] = true; } } return result; - """ + """.} -proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} = - asm """ +proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerproc.} = + {.emit: """ if (`a` == `b`) return 0; if (!`a`) return -1; if (!`b`) return 1; - for (var i = 0; i < `a`.length-1; ++i) { + for (var i = 0; i < `a`.length && i < `b`.length; i++) { var result = `a`[i] - `b`[i]; if (result != 0) return result; } - return 0; - """ - -proc cmp(x, y: string): int = return cmpStrings(x, y) + return `a`.length - `b`.length; + """.} + +proc cmp(x, y: string): int = + when nimvm: + if x == y: result = 0 + elif x < y: result = -1 + else: result = 1 + else: + result = cmpStrings(x, y) -proc eqStrings(a, b: string): bool {.noStackFrame, compilerProc.} = - asm """ +proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerproc.} = + {.emit: """ if (`a` == `b`) return true; + if (`a` === null && `b`.length == 0) return true; + if (`b` === null && `a`.length == 0) return true; if ((!`a`) || (!`b`)) return false; var alen = `a`.length; if (alen != `b`.length) return false; for (var i = 0; i < alen; ++i) if (`a`[i] != `b`[i]) return false; return true; - """ - -type - TDocument {.importc.} = object of TObject - write: proc (text: cstring) {.nimcall.} - writeln: proc (text: cstring) {.nimcall.} - createAttribute: proc (identifier: cstring): ref TNode {.nimcall.} - createElement: proc (identifier: cstring): ref TNode {.nimcall.} - createTextNode: proc (identifier: cstring): ref TNode {.nimcall.} - getElementById: proc (id: cstring): ref TNode {.nimcall.} - getElementsByName: proc (name: cstring): seq[ref TNode] {.nimcall.} - getElementsByTagName: proc (name: cstring): seq[ref TNode] {.nimcall.} - - TNodeType* = enum - ElementNode = 1, - AttributeNode, - TextNode, - CDATANode, - EntityRefNode, - EntityNode, - ProcessingInstructionNode, - CommentNode, - DocumentNode, - DocumentTypeNode, - DocumentFragmentNode, - NotationNode - TNode* {.importc.} = object of TObject - attributes*: seq[ref TNode] - childNodes*: seq[ref TNode] - data*: cstring - firstChild*: ref TNode - lastChild*: ref TNode - nextSibling*: ref TNode - nodeName*: cstring - nodeType*: TNodeType - nodeValue*: cstring - parentNode*: ref TNode - previousSibling*: ref TNode - appendChild*: proc (child: ref TNode) {.nimcall.} - appendData*: proc (data: cstring) {.nimcall.} - cloneNode*: proc (copyContent: bool) {.nimcall.} - deleteData*: proc (start, len: int) {.nimcall.} - getAttribute*: proc (attr: cstring): cstring {.nimcall.} - getAttributeNode*: proc (attr: cstring): ref TNode {.nimcall.} - getElementsByTagName*: proc (): seq[ref TNode] {.nimcall.} - hasChildNodes*: proc (): bool {.nimcall.} - insertBefore*: proc (newNode, before: ref TNode) {.nimcall.} - insertData*: proc (position: int, data: cstring) {.nimcall.} - removeAttribute*: proc (attr: cstring) {.nimcall.} - removeAttributeNode*: proc (attr: ref TNode) {.nimcall.} - removeChild*: proc (child: ref TNode) {.nimcall.} - replaceChild*: proc (newNode, oldNode: ref TNode) {.nimcall.} - replaceData*: proc (start, len: int, text: cstring) {.nimcall.} - setAttribute*: proc (name, value: cstring) {.nimcall.} - setAttributeNode*: proc (attr: ref TNode) {.nimcall.} + """.} when defined(kwin): - proc rawEcho {.compilerproc, nostackframe.} = - asm """ + proc rawEcho {.compilerproc, asmNoStackFrame.} = + {.emit: """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { buf += `toJSStr`(arguments[i]); } print(buf); - """ - -elif defined(nodejs): + """.} + +elif not defined(nimOldEcho): proc ewriteln(x: cstring) = log(x) - - proc rawEcho {.compilerproc, nostackframe.} = - asm """ + + proc rawEcho {.compilerproc, asmNoStackFrame.} = + {.emit: """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { buf += `toJSStr`(arguments[i]); } console.log(buf); - """ + """.} else: - var - document {.importc, nodecl.}: ref TDocument - - proc ewriteln(x: cstring) = - var node = document.getElementsByTagName("body")[0] - if node != nil: - node.appendChild(document.createTextNode(x)) - node.appendChild(document.createElement("br")) - else: - raise newException(EInvalidValue, "<body> element does not exist yet!") + proc ewriteln(x: cstring) = + var node : JSRef + {.emit: "`node` = document.getElementsByTagName('body')[0];".} + if node.isNil: + raise newException(ValueError, "<body> element does not exist yet!") + {.emit: """ + `node`.appendChild(document.createTextNode(`x`)); + `node`.appendChild(document.createElement("br")); + """.} proc rawEcho {.compilerproc.} = - var node = document.getElementsByTagName("body")[0] - if node == nil: raise newException(EIO, "<body> element does not exist yet!") - asm """ - for (var i = 0; i < arguments.length; ++i) { - var x = `toJSStr`(arguments[i]); - `node`.appendChild(document.createTextNode(x)) - } - """ - node.appendChild(document.createElement("br")) + var node : JSRef + {.emit: "`node` = document.getElementsByTagName('body')[0];".} + if node.isNil: + raise newException(IOError, "<body> element does not exist yet!") + {.emit: """ + for (var i = 0; i < arguments.length; ++i) { + var x = `toJSStr`(arguments[i]); + `node`.appendChild(document.createTextNode(x)); + } + `node`.appendChild(document.createElement("br")); + """.} # Arithmetic: -proc addInt(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ +proc checkOverflowInt(a: int) {.asmNoStackFrame, compilerproc.} = + {.emit: """ + if (`a` > 2147483647 || `a` < -2147483648) `raiseOverflow`(); + """.} + +proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = + {.emit: """ var result = `a` + `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + `checkOverflowInt`(result); return result; - """ + """.} -proc subInt(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ +proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = + {.emit: """ var result = `a` - `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + `checkOverflowInt`(result); return result; - """ + """.} -proc mulInt(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ +proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = + {.emit: """ var result = `a` * `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + `checkOverflowInt`(result); return result; - """ + """.} -proc divInt(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ +proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = + {.emit: """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); - return Math.floor(`a` / `b`); - """ + return Math.trunc(`a` / `b`); + """.} -proc modInt(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ +proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = + {.emit: """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); - return Math.floor(`a` % `b`); - """ + return Math.trunc(`a` % `b`); + """.} + +proc checkOverflowInt64(a: int64) {.asmNoStackFrame, compilerproc.} = + {.emit: """ + if (`a` > 9223372036854775807n || `a` < -9223372036854775808n) `raiseOverflow`(); + """.} -proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ +proc addInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = + {.emit: """ var result = `a` + `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); + `checkOverflowInt64`(result); return result; - """ + """.} -proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ +proc subInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = + {.emit: """ var result = `a` - `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); + `checkOverflowInt64`(result); return result; - """ + """.} -proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ +proc mulInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = + {.emit: """ var result = `a` * `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); + `checkOverflowInt64`(result); return result; - """ - -proc divInt64(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); - return Math.floor(`a` / `b`); - """ - -proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} = - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); - return Math.floor(`a` % `b`); - """ - -proc NegInt(a: int): int {.compilerproc.} = + """.} + +proc divInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = + {.emit: """ + if (`b` == 0n) `raiseDivByZero`(); + if (`b` == -1n && `a` == 9223372036854775807n) `raiseOverflow`(); + return `a` / `b`; + """.} + +proc modInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = + {.emit: """ + if (`b` == 0n) `raiseDivByZero`(); + if (`b` == -1n && `a` == 9223372036854775807n) `raiseOverflow`(); + return `a` % `b`; + """.} + +proc negInt(a: int): int {.compilerproc.} = result = a*(-1) -proc NegInt64(a: int64): int64 {.compilerproc.} = +proc negInt64(a: int64): int64 {.compilerproc.} = result = a*(-1) -proc AbsInt(a: int): int {.compilerproc.} = +proc absInt(a: int): int {.compilerproc.} = result = if a < 0: a*(-1) else: a -proc AbsInt64(a: int64): int64 {.compilerproc.} = +proc absInt64(a: int64): int64 {.compilerproc.} = result = if a < 0: a*(-1) else: a -proc LeU(a, b: int): bool {.compilerproc.} = - result = abs(a) <= abs(b) - -proc LtU(a, b: int): bool {.compilerproc.} = - result = abs(a) < abs(b) - -proc LeU64(a, b: int64): bool {.compilerproc.} = - result = abs(a) <= abs(b) - -proc LtU64(a, b: int64): bool {.compilerproc.} = - result = abs(a) < abs(b) - -proc AddU(a, b: int): int {.compilerproc.} = - result = abs(a) + abs(b) -proc AddU64(a, b: int64): int64 {.compilerproc.} = - result = abs(a) + abs(b) - -proc SubU(a, b: int): int {.compilerproc.} = - result = abs(a) - abs(b) -proc SubU64(a, b: int64): int64 {.compilerproc.} = - result = abs(a) - abs(b) - -proc MulU(a, b: int): int {.compilerproc.} = - result = abs(a) * abs(b) -proc MulU64(a, b: int64): int64 {.compilerproc.} = - result = abs(a) * abs(b) - -proc DivU(a, b: int): int {.compilerproc.} = - result = abs(a) div abs(b) -proc DivU64(a, b: int64): int64 {.compilerproc.} = - result = abs(a) div abs(b) - -proc ModU(a, b: int): int {.compilerproc.} = - result = abs(a) mod abs(b) -proc ModU64(a, b: int64): int64 {.compilerproc.} = - result = abs(a) mod abs(b) - -proc Ze(a: int): int {.compilerproc.} = - result = a -proc Ze64(a: int64): int64 {.compilerproc.} = - result = a - -proc toU8(a: int): int8 {.noStackFrame, compilerproc.} = - asm """ - return `a`; - """ - -proc toU16(a: int): int16 {.noStackFrame, compilerproc.} = - asm """ - return `a`; - """ - -proc toU32(a: int): int32 {.noStackFrame, compilerproc.} = - asm """ - return `a`; - """ - - proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b +proc chckNilDisp(p: JSRef) {.compilerproc.} = + if p == nil: + sysFatal(NilAccessDefect, "cannot dispatch; dispatcher is nil") + include "system/hti" proc isFatPointer(ti: PNimType): bool = @@ -499,103 +524,116 @@ proc isFatPointer(ti: PNimType): bool = tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr} -proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} +proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef {.compilerproc.} -proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = +proc nimCopyAux(dest, src: JSRef, n: ptr TNimNode) {.compilerproc.} = case n.kind - of nkNone: sysAssert(false, "NimCopyAux") + of nkNone: sysAssert(false, "nimCopyAux") of nkSlot: - asm "`dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);" + {.emit: """ + `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); + """.} of nkList: - for i in 0..n.len-1: - NimCopyAux(dest, src, n.sons[i]) + {.emit: """ + for (var i = 0; i < `n`.sons.length; i++) { + nimCopyAux(`dest`, `src`, `n`.sons[i]); + } + """.} of nkCase: - asm """ - `dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ); + {.emit: """ + `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); for (var i = 0; i < `n`.sons.length; ++i) { - NimCopyAux(`dest`, `src`, `n`.sons[i][1]); + nimCopyAux(`dest`, `src`, `n`.sons[i][1]); } - """ + """.} -proc NimCopy(x: pointer, ti: PNimType): pointer = +proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = case ti.kind of tyPtr, tyRef, tyVar, tyNil: if not isFatPointer(ti): - result = x + result = src else: - asm """ - `result` = [null, 0]; - `result`[0] = `x`[0]; - `result`[1] = `x`[1]; - """ + {.emit: "`result` = [`src`[0], `src`[1]];".} of tySet: - asm """ - `result` = {}; - for (var key in `x`) { `result`[key] = `x`[key]; } - """ + {.emit: """ + if (`dest` === null || `dest` === undefined) { + `dest` = {}; + } + else { + for (var key in `dest`) { delete `dest`[key]; } + } + for (var key in `src`) { `dest`[key] = `src`[key]; } + `result` = `dest`; + """.} of tyTuple, tyObject: - if ti.base != nil: result = NimCopy(x, ti.base) + if ti.base != nil: result = nimCopy(dest, src, ti.base) elif ti.kind == tyObject: - asm "`result` = {m_type: `ti`};" + {.emit: "`result` = (`dest` === null || `dest` === undefined) ? {m_type: `ti`} : `dest`;".} else: - asm "`result` = {};" - NimCopyAux(result, x, ti.node) - of tySequence, tyArrayConstr, tyOpenArray, tyArray: - asm """ - `result` = new Array(`x`.length); - for (var i = 0; i < `x`.length; ++i) { - `result`[i] = NimCopy(`x`[i], `ti`.base); + {.emit: "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;".} + nimCopyAux(result, src, ti.node) + of tyArrayConstr, tyArray: + # In order to prevent a type change (TypedArray -> Array) and to have better copying performance, + # arrays constructors are considered separately + {.emit: """ + if(ArrayBuffer.isView(`src`)) { + if(`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { + `dest` = new `src`.constructor(`src`); + } else { + `dest`.set(`src`, 0); + } + `result` = `dest`; + } else { + if (`src` === null) { + `result` = null; + } + else { + if (`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { + `dest` = new Array(`src`.length); + } + `result` = `dest`; + for (var i = 0; i < `src`.length; ++i) { + `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); + } + } } - """ - of tyString: - asm "`result` = `x`.slice(0);" - else: - result = x - -proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} = - case ti.kind - of tyPtr, tyRef, tyVar, tyNil: - if not isFatPointer(ti): - result = nil - else: - asm """ - `result` = [null, 0]; - """ - of tySet: - asm """ - `result` = {}; - """ - of tyTuple, tyObject: - if ti.kind == tyObject: - asm "`result` = {m_type: `ti`};" - else: - asm "`result` = {};" + """.} of tySequence, tyOpenArray: - asm """ - `result` = []; - """ - of tyArrayConstr, tyArray: - asm """ - `result` = new Array(`x`.length); - for (var i = 0; i < `x`.length; ++i) { - `result`[i] = genericReset(`x`[i], `ti`.base); + {.emit: """ + if (`src` === null) { + `result` = null; + } + else { + if (`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { + `dest` = new Array(`src`.length); + } + `result` = `dest`; + for (var i = 0; i < `src`.length; ++i) { + `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); + } } - """ + """.} + of tyString: + {.emit: """ + if (`src` !== null) { + `result` = `src`.slice(0); + } + """.} else: - result = nil + result = src -proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. - noStackFrame, compilerproc.} = +proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {. + asmNoStackFrame, compilerproc.} = # types are fake - asm """ + {.emit: """ var result = new Array(`len`); - for (var i = 0; i < `len`; ++i) result[i] = NimCopy(`value`, `typ`); + for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`); return result; - """ + """.} proc chckIndx(i, a, b: int): int {.compilerproc.} = if i >= a and i <= b: return i - else: raiseIndexError() + else: raiseIndexError(i, a, b) proc chckRange(i, a, b: int): int {.compilerproc.} = if i >= a and i <= b: return i @@ -607,7 +645,7 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} = if x == subclass: return # optimized fast path while x != subclass: if x == nil: - raise newException(EInvalidObjectConversion, "invalid object conversion") + raise newException(ObjectConversionDefect, "invalid object conversion") x = x.base proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = @@ -619,4 +657,112 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = x = x.base return true +proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} = + {.emit: "`x`.push(`c`);".} + {.pop.} + +proc tenToThePowerOf(b: int): BiggestFloat = + # xxx deadcode + var b = b + var a = 10.0 + result = 1.0 + while true: + if (b and 1) == 1: + result = result * a + b = b shr 1 + if b == 0: break + a = a * a + +const + IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} + + +proc parseFloatNative(a: openarray[char]): float = + var str = "" + for x in a: + str.add x + + let cstr = cstring str + + {.emit: """ + `result` = Number(`cstr`); + """.} + +proc nimParseBiggestFloat(s: openarray[char], number: var BiggestFloat): int {.compilerproc.} = + var sign: bool + var i = 0 + if s[i] == '+': inc(i) + elif s[i] == '-': + sign = true + inc(i) + if s[i] == 'N' or s[i] == 'n': + if s[i+1] == 'A' or s[i+1] == 'a': + if s[i+2] == 'N' or s[i+2] == 'n': + if s[i+3] notin IdentChars: + number = NaN + return i+3 + return 0 + if s[i] == 'I' or s[i] == 'i': + if s[i+1] == 'N' or s[i+1] == 'n': + if s[i+2] == 'F' or s[i+2] == 'f': + if s[i+3] notin IdentChars: + number = if sign: -Inf else: Inf + return i+3 + return 0 + + var buf: string + # we could also use an `array[char, N]` buffer to avoid reallocs, or + # use a 2-pass algorithm that first computes the length. + if sign: buf.add '-' + template addInc = + buf.add s[i] + inc(i) + template eatUnderscores = + while s[i] == '_': inc(i) + while s[i] in {'0'..'9'}: # Read integer part + buf.add s[i] + inc(i) + eatUnderscores() + if s[i] == '.': # Decimal? + addInc() + while s[i] in {'0'..'9'}: # Read fractional part + addInc() + eatUnderscores() + # Again, read integer and fractional part + if buf.len == ord(sign): return 0 + if s[i] in {'e', 'E'}: # Exponent? + addInc() + if s[i] == '+': inc(i) + elif s[i] == '-': addInc() + if s[i] notin {'0'..'9'}: return 0 + while s[i] in {'0'..'9'}: + addInc() + eatUnderscores() + number = parseFloatNative(buf) + result = i + +# Workaround for IE, IE up to version 11 lacks 'Math.trunc'. We produce +# 'Math.trunc' for Nim's ``div`` and ``mod`` operators: +when defined(nimJsMathTruncPolyfill): + {.emit: """ +if (!Math.trunc) { + Math.trunc = function(v) { + v = +v; + if (!isFinite(v)) return v; + return (v - v % 1) || (v < 0 ? -0 : v === 0 ? v : 0); + }; +} +""".} + +proc cmpClosures(a, b: JSRef): bool {.compilerproc, asmNoStackFrame.} = + # Both `a` and `b` need to be a closure + {.emit: """ + if (`a` !== null && `a`.ClP_0 !== undefined && + `b` !== null && `b`.ClP_0 !== undefined) { + return `a`.ClP_0 == `b`.ClP_0 && `a`.ClE_0 == `b`.ClE_0; + } else { + return `a` == `b`; + } + """ + .} |