diff options
Diffstat (limited to 'compiler/vm.nim')
-rw-r--r-- | compiler/vm.nim | 691 |
1 files changed, 455 insertions, 236 deletions
diff --git a/compiler/vm.nim b/compiler/vm.nim index 0a706b0fc..e38642de8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -19,7 +19,7 @@ import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, parser, vmdeps, idents, trees, renderer, options, transf, parseutils, - vmmarshal, gorgeimpl + vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -61,12 +61,12 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = while x != nil: inc calls x = x.next - msgWriteln($calls & " calls omitted\n") + msgWriteln(c.config, $calls & " calls omitted\n") return stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1) var info = c.debug[pc] # we now use the same format as in system/except.nim - var s = substr(toFilename(info), 0) + var s = substr(toFilename(c.config, info), 0) # this 'substr' prevents a strange corruption. XXX This needs to be # investigated eventually but first attempts to fix it broke everything # see the araq-wip-fixed-writebarrier branch. @@ -78,19 +78,21 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = if x.prc != nil: for k in 1..max(1, 25-s.len): add(s, ' ') add(s, x.prc.name.s) - msgWriteln(s) + msgWriteln(c.config, s) proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, - msg: TMsgKind, arg = "", n: PNode = nil) = - msgWriteln("stack trace: (most recent call last)") + msg: string, lineInfo: TLineInfo) = + msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) # XXX test if we want 'globalError' for every mode - let lineInfo = if n == nil: c.debug[pc] else: n.info - if c.mode == emRepl: globalError(lineInfo, msg, arg) - else: localError(lineInfo, msg, arg) + if c.mode == emRepl: globalError(c.config, lineInfo, msg) + else: localError(c.config, lineInfo, msg) + +proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) = + stackTrace(c, tos, pc, msg, c.debug[pc]) proc bailOut(c: PCtx; tos: PStackFrame) = - stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX, + stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " & c.currentExceptionA.sons[3].skipColon.strVal) when not defined(nimComputedGoto): @@ -204,16 +206,14 @@ proc asgnComplex(x: var TFullReg, y: TFullReg) = of rkRegisterAddr: x.regAddr = y.regAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr -proc putIntoNode(n: var PNode; x: TFullReg) = +proc writeField(n: var PNode, x: TFullReg) = case x.kind of rkNone: discard of rkInt: n.intVal = x.intVal of rkFloat: n.floatVal = x.floatVal - of rkNode: - if nfIsRef in x.node.flags: n = x.node - else: n[] = x.node[] - of rkRegisterAddr: putIntoNode(n, x.regAddr[]) - of rkNodeAddr: n[] = x.nodeAddr[][] + of rkNode: n = copyValue(x.node) + of rkRegisterAddr: writeField(n, x.regAddr[]) + of rkNodeAddr: n = x.nodeAddr[] proc putIntoReg(dest: var TFullReg; n: PNode) = case n.kind @@ -244,7 +244,8 @@ template getstr(a: untyped): untyped = (if a.kind == rkNode: a.node.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = - if f.safePoints.isNil: f.safePoints = @[] + when not defined(nimNoNilSeqs): + if f.safePoints.isNil: f.safePoints = @[] f.safePoints.add(pc) proc popSafePoint(f: PStackFrame) = @@ -257,7 +258,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: - while f.safePoints.isNil or f.safePoints.len == 0: + while f.safePoints.len == 0: f = f.next if f.isNil: return (-1, nil) var pc2 = f.safePoints[f.safePoints.high] @@ -272,7 +273,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): abstractPtrs) else: nil #echo typeToString(exceptType), " ", typeToString(raisedType) - if exceptType.isNil or inheritanceDiff(exceptType, raisedType) <= 0: + if exceptType.isNil or inheritanceDiff(raisedType, exceptType) <= 0: # mark exception as handled but keep it in B for # the getCurrentException() builtin: c.currentExceptionB = c.currentExceptionA @@ -299,7 +300,6 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): discard f.safePoints.pop proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = - if f.safePoints.isNil: return -1 for s in f.safePoints: var pc = s while c.code[pc].opcode == opcExcept: @@ -308,7 +308,7 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = return pc return -1 -proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = +proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = if desttyp.kind == tyString: if dest.kind != rkNode: myreset(dest) @@ -323,7 +323,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal else: for i in 0..<n.len: - if n.sons[i].kind != nkSym: internalError("opConv for enum") + if n.sons[i].kind != nkSym: internalError(c.config, "opConv for enum") let f = n.sons[i].sym if f.position == x: dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal @@ -353,7 +353,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = of tyChar: dest.node.strVal = $chr(src.intVal) else: - internalError("cannot convert to string " & desttyp.typeToString) + internalError(c.config, "cannot convert to string " & desttyp.typeToString) else: case skipTypes(desttyp, abstractRange).kind of tyInt..tyInt64: @@ -364,7 +364,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = dest.intVal = int(src.floatVal) else: dest.intVal = src.intVal - if dest.intVal < firstOrd(desttyp) or dest.intVal > lastOrd(desttyp): + if dest.intVal < firstOrd(c.config, desttyp) or dest.intVal > lastOrd(c.config, desttyp): return true of tyUInt..tyUInt64: if dest.kind != rkInt: @@ -402,9 +402,9 @@ template handleJmpBack() {.dirty.} = if allowInfiniteLoops in c.features: c.loopIterations = MaxLoopIterations else: - msgWriteln("stack trace: (most recent call last)") + msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) - globalError(c.debug[pc], errTooManyIterations) + globalError(c.config, c.debug[pc], errTooManyIterations) dec(c.loopIterations) proc recSetFlagIsRef(arg: PNode) = @@ -418,14 +418,14 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typeKind = case typeEntry.kind - of tyUInt..tyUInt64: nkUIntLit - of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit - of tyFloat..tyFloat128: nkFloatLit - of tyString: nkStrLit - of tyObject: nkObjConstr - of tySequence: nkNilLit - of tyProc, tyTuple: nkPar - else: nkEmpty + of tyUInt..tyUInt64: nkUIntLit + of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit + of tyFloat..tyFloat128: nkFloatLit + of tyString: nkStrLit + of tyObject: nkObjConstr + of tySequence: nkNilLit + of tyProc, tyTuple: nkTupleConstr + else: nkEmpty let oldLen = node.len setLen(node.sons, newLen) @@ -434,6 +434,17 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = for i in oldLen ..< newLen: node.sons[i] = newNodeI(typeKind, info) +const + errIndexOutOfBounds = "index out of bounds" + errNilAccess = "attempt to access a nil address" + errOverOrUnderflow = "over- or underflow" + errConstantDivisionByZero = "division by zero" + errIllegalConvFromXtoY = "illegal conversion from '$1' to '$2'" + errTooManyIterations = "interpretation requires too many iterations; " & + "if you are sure this is not a bug in your code edit " & + "compiler/vmdef.MaxLoopIterations and rebuild the compiler" + errFieldXNotFound = "node lacks field: " + proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos @@ -447,7 +458,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = #if c.traceActive: when traceCode: echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC - # message(c.debug[pc], warnUser, "Trace") + # message(c.config, c.debug[pc], warnUser, "Trace") case instr.opcode of opcEof: return regs[ra] @@ -481,9 +492,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = asgnComplex(regs[ra], regs[instr.regB]) of opcAsgnRef: asgnRef(regs[ra], regs[instr.regB]) - of opcRegToNode: - decodeB(rkNode) - putIntoNode(regs[ra].node, regs[rb]) of opcNodeToReg: let ra = instr.regA let rb = instr.regB @@ -525,10 +533,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkInt) let idx = regs[rc].intVal.int let s = regs[rb].node.strVal - if s.isNil: - stackTrace(c, tos, pc, errNilAccess) - elif idx <=% s.len: + if idx <% s.len: regs[ra].intVal = s[idx].ord + elif idx == s.len and optLaxStrings in c.config.options: + regs[ra].intVal = 0 else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArr: @@ -542,7 +550,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: stackTrace(c, tos, pc, errIndexOutOfBounds) elif idx <% arr.len: - putIntoNode(arr.sons[idx], regs[rc]) + writeField(arr.sons[idx], regs[rc]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdObj: @@ -562,9 +570,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if dest.kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) elif dest.sons[shiftedRb].kind == nkExprColonExpr: - putIntoNode(dest.sons[shiftedRb].sons[1], regs[rc]) + writeField(dest.sons[shiftedRb].sons[1], regs[rc]) else: - putIntoNode(dest.sons[shiftedRb], regs[rc]) + writeField(dest.sons[shiftedRb], regs[rc]) of opcWrStrIdx: decodeBC(rkNode) let idx = regs[rb].intVal.int @@ -580,7 +588,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rb].kind == rkNode: regs[ra].nodeAddr = addr(regs[rb].node) else: - stackTrace(c, tos, pc, errGenerated, "limited VM support for 'addr'") + stackTrace(c, tos, pc, "limited VM support for 'addr'") of opcLdDeref: # a = b[] let ra = instr.regA @@ -607,9 +615,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ra = instr.regA let rc = instr.regC case regs[ra].kind - of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc]) + of rkNodeAddr: + let n = regs[rc].regToNode + # `var object` parameters are sent as rkNodeAddr. When they are mutated + # vmgen generates opcWrDeref, which means that we must dereference + # twice. + # TODO: This should likely be handled differently in vmgen. + if (nfIsRef notin regs[ra].nodeAddr[].flags and + nfIsRef notin n.flags): + regs[ra].nodeAddr[][] = n[] + else: + regs[ra].nodeAddr[] = n of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] - of rkNode: putIntoNode(regs[ra].node, regs[rc]) + of rkNode: + if regs[ra].node.kind == nkNilLit: + stackTrace(c, tos, pc, errNilAccess) + assert nfIsRef in regs[ra].node.flags + regs[ra].node[] = regs[rc].regToNode[] + regs[ra].node.flags.incl nfIsRef else: stackTrace(c, tos, pc, errNilAccess) of opcAddInt: decodeBC(rkInt) @@ -623,7 +646,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, errOverOrUnderflow) of opcAddImmInt: decodeBImm(rkInt) - #message(c.debug[pc], warnUser, "came here") + #message(c.config, c.debug[pc], warnUser, "came here") #debug regs[rb].node let bVal = regs[rb].intVal @@ -683,12 +706,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNode) var b = newNodeIT(nkCurly, regs[ra].node.info, regs[ra].node.typ) addSon(b, regs[rb].regToNode) - var r = diffSets(regs[ra].node, b) + var r = diffSets(c.config, regs[ra].node, b) discardSons(regs[ra].node) for i in countup(0, sonsLen(r) - 1): addSon(regs[ra].node, r.sons[i]) of opcCard: decodeB(rkInt) - regs[ra].intVal = nimsets.cardSet(regs[rb].node) + regs[ra].intVal = nimsets.cardSet(c.config, regs[rb].node) of opcMulInt: decodeBC(rkInt) let @@ -729,6 +752,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcShlInt: decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal + of opcAshrInt: + decodeBC(rkInt) + regs[ra].intVal = ashr(regs[rb].intVal, regs[rc].intVal) of opcBitandInt: decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal and regs[rc].intVal @@ -779,10 +805,22 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) of opcEqRef: decodeBC(rkInt) - regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and - regs[rc].node.kind == nkNilLit) or - regs[rb].node == regs[rc].node) - of opcEqNimrodNode: + if regs[rb].kind == rkNodeAddr: + if regs[rc].kind == rkNodeAddr: + regs[ra].intVal = ord(regs[rb].nodeAddr == regs[rc].nodeAddr) + else: + assert regs[rc].kind == rkNode + # we know these cannot be equal + regs[ra].intVal = ord(false) + elif regs[rc].kind == rkNodeAddr: + assert regs[rb].kind == rkNode + # we know these cannot be equal + regs[ra].intVal = ord(false) + else: + regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and + regs[rc].node.kind == nkNilLit) or + regs[rb].node == regs[rc].node) + of opcEqNimNode: decodeBC(rkInt) regs[ra].intVal = ord(exprStructuralEquivalent(regs[rb].node, regs[rc].node, @@ -824,35 +862,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = ord(regs[rb].node.strVal < regs[rc].node.strVal) of opcLeSet: decodeBC(rkInt) - regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node)) + regs[ra].intVal = ord(containsSets(c.config, regs[rb].node, regs[rc].node)) of opcEqSet: decodeBC(rkInt) - regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node)) + regs[ra].intVal = ord(equalSets(c.config, regs[rb].node, regs[rc].node)) of opcLtSet: decodeBC(rkInt) let a = regs[rb].node let b = regs[rc].node - regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b)) + regs[ra].intVal = ord(containsSets(c.config, a, b) and not equalSets(c.config, a, b)) of opcMulSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.intersectSets(regs[rb].node, regs[rc].node).sons) + nimsets.intersectSets(c.config, regs[rb].node, regs[rc].node).sons) of opcPlusSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.unionSets(regs[rb].node, regs[rc].node).sons) + nimsets.unionSets(c.config, regs[rb].node, regs[rc].node).sons) of opcMinusSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.diffSets(regs[rb].node, regs[rc].node).sons) + nimsets.diffSets(c.config, regs[rb].node, regs[rc].node).sons) of opcSymdiffSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) + nimsets.symdiffSets(c.config, regs[rb].node, regs[rc].node).sons) of opcConcatStr: decodeBC(rkNode) createStr regs[ra] @@ -879,18 +917,28 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if a.kind == nkSym: regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit) else: copyTree(a.sym.ast) + regs[ra].node.flags.incl nfIsRef + else: + stackTrace(c, tos, pc, "node is not a symbol") + of opcSymOwner: + decodeB(rkNode) + let a = regs[rb].node + if a.kind == nkSym: + regs[ra].node = if a.sym.owner.isNil: newNode(nkNilLit) + else: newSymNode(a.sym.skipGenericOwner) + regs[ra].node.flags.incl nfIsRef else: - stackTrace(c, tos, pc, errFieldXNotFound, "symbol") + stackTrace(c, tos, pc, "node is not a symbol") of opcEcho: let rb = instr.regB if rb == 1: - msgWriteln(regs[ra].node.strVal, {msgStdout}) + msgWriteln(c.config, regs[ra].node.strVal, {msgStdout}) else: var outp = "" for i in ra..ra+rb-1: #if regs[i].kind != rkNode: debug regs[i] outp.add(regs[i].node.strVal) - msgWriteln(outp, {msgStdout}) + msgWriteln(c.config, outp, {msgStdout}) of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) @@ -919,15 +967,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rc = instr.regC if not (leValueConv(regs[rb].regToNode, regs[ra].regToNode) and leValueConv(regs[ra].regToNode, regs[rc].regToNode)): - stackTrace(c, tos, pc, errGenerated, - msgKindToString(errIllegalConvFromXtoY) % [ - $regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"]) + stackTrace(c, tos, pc, + errIllegalConvFromXtoY % [ + $regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"]) of opcIndCall, opcIndCallAsgn: # dest = call regStart, n; where regStart = fn, arg1, ... let rb = instr.regB let rc = instr.regC let bb = regs[rb].node - let isClosure = bb.kind == nkPar + let isClosure = bb.kind == nkTupleConstr let prc = if not isClosure: bb.sym else: bb.sons[0].sym if prc.offset < -1: # it's a callback: @@ -937,20 +985,20 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = currentLineInfo: c.debug[pc])) elif sfImportc in prc.flags: if allowFFI notin c.features: - globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI") + globalError(c.config, c.debug[pc], "VM not allowed to do FFI") # we pass 'tos.slots' instead of 'regs' so that the compiler can keep # 'regs' in a register: when hasFFI: let prcValue = c.globals.sons[prc.position-1] if prcValue.kind == nkEmpty: - globalError(c.debug[pc], errGenerated, "canot run " & prc.name.s) + globalError(c.config, c.debug[pc], "cannot run " & prc.name.s) let newValue = callForeignFunction(prcValue, prc.typ, tos.slots, rb+1, rc-1, c.debug[pc]) if newValue.kind != nkEmpty: assert instr.opcode == opcIndCallAsgn putIntoReg(regs[ra], newValue) else: - globalError(c.debug[pc], errGenerated, "VM not built with FFI support") + globalError(c.config, c.debug[pc], "VM not built with FFI support") elif prc.kind != skTemplate: let newPc = compile(c, prc) # tricky: a recursion is also a jump back, so we use the same @@ -960,7 +1008,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset+ord(isClosure)) if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro: - putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info)) + putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info, c.config)) for i in 1 .. rc-1: newFrame.slots[i] = regs[rb+i] if isClosure: @@ -982,7 +1030,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let node = regs[rb+i].regToNode node.info = c.debug[pc] macroCall.add(node) - var a = evalTemplate(macroCall, prc, genSymOwner) + var a = evalTemplate(macroCall, prc, genSymOwner, c.config) if a.kind == nkStmtList and a.len == 1: a = a[0] a.recSetFlagIsRef ensureKind(rkNode) @@ -1067,7 +1115,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNew: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] - regs[ra].node = getNullValue(typ, c.debug[pc]) + regs[ra].node = getNullValue(typ, c.debug[pc], c.config) regs[ra].node.flags.incl nfIsRef of opcNewSeq: let typ = c.types[instr.regBx - wordExcess] @@ -1079,7 +1127,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.typ = typ newSeq(regs[ra].node.sons, count) for i in 0 ..< count: - regs[ra].node.sons[i] = getNullValue(typ.sons[0], c.debug[pc]) + regs[ra].node.sons[i] = getNullValue(typ.sons[0], c.debug[pc], c.config) of opcNewStr: decodeB(rkNode) regs[ra].node = newNodeI(nkStrLit, c.debug[pc]) @@ -1091,7 +1139,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdNull: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] - regs[ra].node = getNullValue(typ, c.debug[pc]) + regs[ra].node = getNullValue(typ, c.debug[pc], c.config) # opcLdNull really is the gist of the VM's problems: should it load # a fresh null to regs[ra].node or to regs[ra].node[]? This really # depends on whether regs[ra] represents the variable itself or wether @@ -1138,7 +1186,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments}) of opcQuit: if c.mode in {emRepl, emStaticExpr, emStaticStmt}: - message(c.debug[pc], hintQuitCalled) + message(c.config, c.debug[pc], hintQuitCalled) msgQuit(int8(getOrdValue(regs[ra].regToNode))) else: return TFullReg(kind: rkNone) @@ -1164,25 +1212,46 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc]) of opcReset: - internalError(c.debug[pc], "too implement") + internalError(c.config, c.debug[pc], "too implement") of opcNarrowS: decodeB(rkInt) let min = -(1.BiggestInt shl (rb-1)) let max = (1.BiggestInt shl (rb-1))-1 if regs[ra].intVal < min or regs[ra].intVal > max: - stackTrace(c, tos, pc, errGenerated, - msgKindToString(errUnhandledExceptionX) % "value out of range") + stackTrace(c, tos, pc, "unhandled exception: value out of range") of opcNarrowU: decodeB(rkInt) regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1) of opcIsNil: decodeB(rkInt) let node = regs[rb].node - regs[ra].intVal = ord(node.kind == nkNilLit or - (node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil)) + regs[ra].intVal = ord( + # Note that `nfIsRef` + `nkNilLit` represents an allocated + # reference with the value `nil`, so `isNil` should be false! + (node.kind == nkNilLit and nfIsRef notin node.flags) or + (not node.typ.isNil and node.typ.kind == tyProc and + node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and + node.sons[1].kind == nkNilLit)) of opcNBindSym: + # cannot use this simple check + # if dynamicBindSym notin c.config.features: + + # bindSym with static input decodeBx(rkNode) regs[ra].node = copyTree(c.constants.sons[rbx]) + regs[ra].node.flags.incl nfIsRef + of opcNDynBindSym: + # experimental bindSym + let + rb = instr.regB + rc = instr.regC + idx = int(regs[rb+rc-1].intVal) + callback = c.callbacks[idx].value + args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs), + currentException: c.currentExceptionB, + currentLineInfo: c.debug[pc]) + callback(args) + regs[ra].node.flags.incl nfIsRef of opcNChild: decodeBC(rkNode) let idx = regs[rc].intVal.int @@ -1205,7 +1274,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if u.kind notin {nkEmpty..nkNilLit}: u.add(regs[rc].node) else: - stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind) + stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind) regs[ra].node = u of opcNAddMultiple: decodeBC(rkNode) @@ -1215,38 +1284,46 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # XXX can be optimized: for i in 0..<x.len: u.add(x.sons[i]) else: - stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind) + stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind) regs[ra].node = u of opcNKind: decodeB(rkInt) regs[ra].intVal = ord(regs[rb].node.kind) c.comesFromHeuristic = regs[rb].node.info + of opcNSymKind: + decodeB(rkInt) + let a = regs[rb].node + if a.kind == nkSym: + regs[ra].intVal = ord(a.sym.kind) + else: + stackTrace(c, tos, pc, "node is not a symbol") + c.comesFromHeuristic = regs[rb].node.info of opcNIntVal: decodeB(rkInt) let a = regs[rb].node case a.kind of nkCharLit..nkUInt64Lit: regs[ra].intVal = a.intVal - else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal") + else: stackTrace(c, tos, pc, errFieldXNotFound & "intVal") of opcNFloatVal: decodeB(rkFloat) let a = regs[rb].node case a.kind of nkFloatLit..nkFloat64Lit: regs[ra].floatVal = a.floatVal - else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") + else: stackTrace(c, tos, pc, errFieldXNotFound & "floatVal") of opcNSymbol: decodeB(rkNode) let a = regs[rb].node if a.kind == nkSym: regs[ra].node = copyNode(a) else: - stackTrace(c, tos, pc, errFieldXNotFound, "symbol") + stackTrace(c, tos, pc, errFieldXNotFound & "symbol") of opcNIdent: decodeB(rkNode) let a = regs[rb].node if a.kind == nkIdent: regs[ra].node = copyNode(a) else: - stackTrace(c, tos, pc, errFieldXNotFound, "ident") + stackTrace(c, tos, pc, errFieldXNotFound & "ident") of opcNGetType: let rb = instr.regB let rc = instr.regC @@ -1255,84 +1332,106 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # getType opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: - regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc]) + regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: - stackTrace(c, tos, pc, errGenerated, "node has no type") + stackTrace(c, tos, pc, "node has no type") of 1: # typeKind opcode: ensureKind(rkInt) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].intVal = ord(regs[rb].node.typ.kind) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].intVal = ord(regs[rb].node.sym.typ.kind) #else: - # stackTrace(c, tos, pc, errGenerated, "node has no type") + # stackTrace(c, tos, pc, "node has no type") of 2: # getTypeInst opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: - regs[ra].node = opMapTypeInstToAst(regs[rb].node.typ, c.debug[pc]) + regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: - stackTrace(c, tos, pc, errGenerated, "node has no type") + stackTrace(c, tos, pc, "node has no type") else: # getTypeImpl opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: - regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc]) + regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: - stackTrace(c, tos, pc, errGenerated, "node has no type") + stackTrace(c, tos, pc, "node has no type") of opcNStrVal: decodeB(rkNode) createStr regs[ra] let a = regs[rb].node - if a.kind in {nkStrLit..nkTripleStrLit}: regs[ra].node.strVal = a.strVal - elif a.kind == nkCommentStmt: regs[ra].node.strVal = a.comment - else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + case a.kind + of {nkStrLit..nkTripleStrLit}: + regs[ra].node.strVal = a.strVal + of nkCommentStmt: + regs[ra].node.strVal = a.comment + of nkIdent: + regs[ra].node.strVal = a.ident.s + of nkSym: + regs[ra].node.strVal = a.sym.name.s + else: + stackTrace(c, tos, pc, errFieldXNotFound & "strVal") of opcSlurp: decodeB(rkNode) createStr regs[ra] regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc], - c.module) + c.module, c.config) of opcGorge: - decodeBC(rkNode) - inc pc - let rd = c.code[pc].regA - - createStr regs[ra] - regs[ra].node.strVal = opGorge(regs[rb].node.strVal, - regs[rc].node.strVal, regs[rd].node.strVal, - c.debug[pc])[0] - of opcNError: + when defined(nimcore): + decodeBC(rkNode) + inc pc + let rd = c.code[pc].regA + + createStr regs[ra] + regs[ra].node.strVal = opGorge(regs[rb].node.strVal, + regs[rc].node.strVal, regs[rd].node.strVal, + c.debug[pc], c.config)[0] + else: + globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support") + of opcNError, opcNWarning, opcNHint: decodeB(rkNode) let a = regs[ra].node let b = regs[rb].node - stackTrace(c, tos, pc, errUser, a.strVal, if b.kind == nkNilLit: nil else: b) - of opcNWarning: - message(c.debug[pc], warnUser, regs[ra].node.strVal) - of opcNHint: - message(c.debug[pc], hintUser, regs[ra].node.strVal) + let info = if b.kind == nkNilLit: c.debug[pc] else: b.info + if instr.opcode == opcNError: + stackTrace(c, tos, pc, a.strVal, info) + elif instr.opcode == opcNWarning: + message(c.config, info, warnUser, a.strVal) + elif instr.opcode == opcNHint: + message(c.config, info, hintUser, a.strVal) of opcParseExprToAst: decodeB(rkNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? var error: string - let ast = parseString(regs[rb].node.strVal, c.cache, c.debug[pc].toFullPath, - c.debug[pc].line.int, - proc (info: TLineInfo; msg: TMsgKind; arg: string) = - if error.isNil and msg <= msgs.errMax: - error = formatMsg(info, msg, arg)) - if not error.isNil: + let ast = parseString(regs[rb].node.strVal, c.cache, c.config, + toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, + proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = + if error.len == 0 and msg <= errMax: + error = formatMsg(conf, info, msg, arg)) + if error.len > 0: c.errorFlag = error elif sonsLen(ast) != 1: - c.errorFlag = formatMsg(c.debug[pc], errExprExpected, "multiple statements") + c.errorFlag = formatMsg(c.config, c.debug[pc], errGenerated, + "expected expression, but got multiple statements") else: regs[ra].node = ast.sons[0] of opcParseStmtToAst: decodeB(rkNode) var error: string - let ast = parseString(regs[rb].node.strVal, c.cache, c.debug[pc].toFullPath, - c.debug[pc].line.int, - proc (info: TLineInfo; msg: TMsgKind; arg: string) = - if error.isNil and msg <= msgs.errMax: - error = formatMsg(info, msg, arg)) - if not error.isNil: + let ast = parseString(regs[rb].node.strVal, c.cache, c.config, + toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, + proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = + if error.len == 0 and msg <= errMax: + error = formatMsg(conf, info, msg, arg)) + if error.len > 0: c.errorFlag = error else: regs[ra].node = ast @@ -1343,52 +1442,75 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcCallSite: ensureKind(rkNode) if c.callsite != nil: regs[ra].node = c.callsite - else: stackTrace(c, tos, pc, errFieldXNotFound, "callsite") - of opcNGetFile: - decodeB(rkNode) - let n = regs[rb].node - regs[ra].node = newStrNode(nkStrLit, n.info.toFilename) - regs[ra].node.info = n.info - regs[ra].node.typ = n.typ - of opcNGetLine: - decodeB(rkNode) + else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite") + of opcNGetLineInfo: + decodeBImm(rkNode) let n = regs[rb].node - regs[ra].node = newIntNode(nkIntLit, n.info.line) + case imm + of 0: # getFile + regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info)) + of 1: # getLine + regs[ra].node = newIntNode(nkIntLit, n.info.line.int) + of 2: # getColumn + regs[ra].node = newIntNode(nkIntLit, n.info.col) + else: + internalAssert c.config, false regs[ra].node.info = n.info regs[ra].node.typ = n.typ - of opcNGetColumn: + of opcNSetLineInfo: decodeB(rkNode) - let n = regs[rb].node - regs[ra].node = newIntNode(nkIntLit, n.info.col) - regs[ra].node.info = n.info - regs[ra].node.typ = n.typ + regs[ra].node.info = regs[rb].node.info of opcEqIdent: decodeBC(rkInt) - if regs[rb].node.kind == nkIdent and regs[rc].node.kind == nkIdent: - regs[ra].intVal = ord(regs[rb].node.ident.id == regs[rc].node.ident.id) + # aliases for shorter and easier to understand code below + let aNode = regs[rb].node + let bNode = regs[rc].node + # these are cstring to prevent string copy, and cmpIgnoreStyle from + # takes cstring arguments + var aStrVal: cstring = nil + var bStrVal: cstring = nil + # extract strVal from argument ``a`` + case aNode.kind + of {nkStrLit..nkTripleStrLit}: + aStrVal = aNode.strVal.cstring + of nkIdent: + aStrVal = aNode.ident.s.cstring + of nkSym: + aStrVal = aNode.sym.name.s.cstring + of nkOpenSymChoice, nkClosedSymChoice: + aStrVal = aNode[0].sym.name.s.cstring else: - regs[ra].intVal = 0 + discard + # extract strVal from argument ``b`` + case bNode.kind + of {nkStrLit..nkTripleStrLit}: + bStrVal = bNode.strVal.cstring + of nkIdent: + bStrVal = bNode.ident.s.cstring + of nkSym: + bStrVal = bNode.sym.name.s.cstring + of nkOpenSymChoice, nkClosedSymChoice: + bStrVal = bNode[0].sym.name.s.cstring + else: + discard + # set result + regs[ra].intVal = + if aStrVal != nil and bStrVal != nil: + ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0) + else: + 0 + of opcStrToIdent: decodeB(rkNode) if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}: - stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + stackTrace(c, tos, pc, errFieldXNotFound & "strVal") else: regs[ra].node = newNodeI(nkIdent, c.debug[pc]) - regs[ra].node.ident = getIdent(regs[rb].node.strVal) - of opcIdentToStr: - decodeB(rkNode) - let a = regs[rb].node - createStr regs[ra] - regs[ra].node.info = c.debug[pc] - if a.kind == nkSym: - regs[ra].node.strVal = a.sym.name.s - elif a.kind == nkIdent: - regs[ra].node.strVal = a.ident.s - else: - stackTrace(c, tos, pc, errFieldXNotFound, "ident") + regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal) + regs[ra].node.flags.incl nfIsRef of opcSetType: if regs[ra].kind != rkNode: - internalError(c.debug[pc], "cannot set type") + internalError(c.config, c.debug[pc], "cannot set type") regs[ra].node.typ = c.types[instr.regBx - wordExcess] of opcConv: let rb = instr.regB @@ -1397,9 +1519,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = inc pc let srctyp = c.types[c.code[pc].regBx - wordExcess] - if opConv(regs[ra], regs[rb], desttyp, srctyp): - stackTrace(c, tos, pc, errGenerated, - msgKindToString(errIllegalConvFromXtoY) % [ + if opConv(c, regs[ra], regs[rb], desttyp, srctyp): + stackTrace(c, tos, pc, + errIllegalConvFromXtoY % [ typeToString(srctyp), typeToString(desttyp)]) of opcCast: let rb = instr.regB @@ -1412,7 +1534,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let dest = fficast(regs[rb], desttyp) asgnRef(regs[ra], dest) else: - globalError(c.debug[pc], "cannot evaluate cast") + globalError(c.config, c.debug[pc], "cannot evaluate cast") of opcNSetIntVal: decodeB(rkNode) var dest = regs[ra].node @@ -1420,7 +1542,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[rb].kind in {rkInt}: dest.intVal = regs[rb].intVal else: - stackTrace(c, tos, pc, errFieldXNotFound, "intVal") + stackTrace(c, tos, pc, errFieldXNotFound & "intVal") of opcNSetFloatVal: decodeB(rkNode) var dest = regs[ra].node @@ -1428,26 +1550,26 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[rb].kind in {rkFloat}: dest.floatVal = regs[rb].floatVal else: - stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") + stackTrace(c, tos, pc, errFieldXNotFound & "floatVal") of opcNSetSymbol: decodeB(rkNode) var dest = regs[ra].node if dest.kind == nkSym and regs[rb].node.kind == nkSym: dest.sym = regs[rb].node.sym else: - stackTrace(c, tos, pc, errFieldXNotFound, "symbol") + stackTrace(c, tos, pc, errFieldXNotFound & "symbol") of opcNSetIdent: decodeB(rkNode) var dest = regs[ra].node if dest.kind == nkIdent and regs[rb].node.kind == nkIdent: dest.ident = regs[rb].node.ident else: - stackTrace(c, tos, pc, errFieldXNotFound, "ident") + stackTrace(c, tos, pc, errFieldXNotFound & "ident") of opcNSetType: decodeB(rkNode) let b = regs[rb].node - internalAssert b.kind == nkSym and b.sym.kind == skType - internalAssert regs[ra].node != nil + internalAssert c.config, b.kind == nkSym and b.sym.kind == skType + internalAssert c.config, regs[ra].node != nil regs[ra].node.typ = b.sym.typ of opcNSetStrVal: decodeB(rkNode) @@ -1458,19 +1580,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = elif dest.kind == nkCommentStmt and regs[rb].kind in {rkNode}: dest.comment = regs[rb].node.strVal else: - stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + stackTrace(c, tos, pc, errFieldXNotFound & "strVal") of opcNNewNimNode: decodeBC(rkNode) var k = regs[rb].intVal if k < 0 or k > ord(high(TNodeKind)): - internalError(c.debug[pc], + internalError(c.config, c.debug[pc], "request to create a NimNode of invalid kind") let cc = regs[rc].node let x = newNodeI(TNodeKind(int(k)), if cc.kind != nkNilLit: cc.info - elif c.comesFromHeuristic.line > -1: + elif c.comesFromHeuristic.line != 0'u16: c.comesFromHeuristic elif c.callsite != nil and c.callsite.safeLen > 1: c.callsite[1].info @@ -1478,7 +1600,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.debug[pc]) x.flags.incl nfIsRef # prevent crashes in the compiler resulting from wrong macros: - if x.kind == nkIdent: x.ident = getIdent"" + if x.kind == nkIdent: x.ident = c.cache.emptyIdent regs[ra].node = x of opcNCopyNimNode: decodeB(rkNode) @@ -1497,16 +1619,117 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let name = if regs[rc].node.strVal.len == 0: ":tmp" else: regs[rc].node.strVal if k < 0 or k > ord(high(TSymKind)): - internalError(c.debug[pc], "request to create symbol of invalid kind") - var sym = newSym(k.TSymKind, name.getIdent, c.module.owner, c.debug[pc]) + internalError(c.config, c.debug[pc], "request to create symbol of invalid kind") + var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) + regs[ra].node.flags.incl nfIsRef + of opcNccValue: + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = getOrDefault(c.graph.cacheCounters, destKey) + of opcNccInc: + let g = c.graph + let destKey = regs[ra].node.strVal + let by = regs[instr.regB].intVal + let v = getOrDefault(g.cacheCounters, destKey) + g.cacheCounters[destKey] = v+by + recordInc(c, c.debug[pc], destKey, by) + of opcNcsAdd: + let g = c.graph + let destKey = regs[ra].node.strVal + let val = regs[instr.regB].node + if not contains(g.cacheSeqs, destKey): + g.cacheSeqs[destKey] = newTree(nkStmtList, val) + # newNodeI(nkStmtList, c.debug[pc]) + else: + g.cacheSeqs[destKey].add val + recordAdd(c, c.debug[pc], destKey, val) + of opcNcsIncl: + let g = c.graph + let destKey = regs[ra].node.strVal + let val = regs[instr.regB].node + if not contains(g.cacheSeqs, destKey): + g.cacheSeqs[destKey] = newTree(nkStmtList, val) + else: + block search: + for existing in g.cacheSeqs[destKey]: + if exprStructuralEquivalent(existing, val, strictSymEquality=true): + break search + g.cacheSeqs[destKey].add val + recordIncl(c, c.debug[pc], destKey, val) + of opcNcsLen: + let g = c.graph + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey].len else: 0 + of opcNcsAt: + let g = c.graph + decodeBC(rkNode) + let idx = regs[rc].intVal + let destKey = regs[rb].node.strVal + if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len: + regs[ra].node = g.cacheSeqs[destKey][idx.int] + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) + of opcNctPut: + let g = c.graph + let destKey = regs[ra].node.strVal + let key = regs[instr.regB].node.strVal + let val = regs[instr.regC].node + if not contains(g.cacheTables, destKey): + g.cacheTables[destKey] = initBTree[string, PNode]() + if not contains(g.cacheTables[destKey], key): + g.cacheTables[destKey].add(key, val) + recordPut(c, c.debug[pc], destKey, key, val) + else: + stackTrace(c, tos, pc, "key already exists: " & key) + of opcNctLen: + let g = c.graph + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if contains(g.cacheTables, destKey): g.cacheTables[destKey].len else: 0 + of opcNctGet: + let g = c.graph + decodeBC(rkNode) + let destKey = regs[rb].node.strVal + let key = regs[rc].node.strVal + if contains(g.cacheTables, destKey): + if contains(g.cacheTables[destKey], key): + regs[ra].node = getOrDefault(g.cacheTables[destKey], key) + else: + stackTrace(c, tos, pc, "key does not exist: " & key) + else: + stackTrace(c, tos, pc, "key does not exist: " & destKey) + of opcNctHasNext: + let g = c.graph + decodeBC(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if g.cacheTables.contains(destKey): + ord(btrees.hasNext(g.cacheTables[destKey], regs[rc].intVal.int)) + else: + 0 + of opcNctNext: + let g = c.graph + decodeBC(rkNode) + let destKey = regs[rb].node.strVal + let index = regs[rc].intVal + if contains(g.cacheTables, destKey): + let (k, v, nextIndex) = btrees.next(g.cacheTables[destKey], index.int) + regs[ra].node = newTree(nkTupleConstr, newStrNode(k, c.debug[pc]), v, + newIntNode(nkIntLit, nextIndex)) + else: + stackTrace(c, tos, pc, "key does not exist: " & destKey) + of opcTypeTrait: # XXX only supports 'name' for now; we can use regC to encode the # type trait operation decodeB(rkNode) var typ = regs[rb].node.typ - internalAssert typ != nil + internalAssert c.config, typ != nil while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0] createStr regs[ra] regs[ra].node.strVal = typ.typeToString(preferExported) @@ -1515,14 +1738,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regB inc pc let typ = c.types[c.code[pc].regBx - wordExcess] - putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ)) + putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config)) of opcMarshalStore: decodeB(rkNode) inc pc let typ = c.types[c.code[pc].regBx - wordExcess] createStrKeepNode(regs[ra]) - if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) - storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode) + when not defined(nimNoNilSeqs): + if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) + storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config) of opcToNarrowInt: decodeBC(rkInt) let mask = (1'i64 shl rc) - 1 # 0xFF @@ -1544,7 +1768,7 @@ proc execute(c: PCtx, start: int): PNode = proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = if sym.kind in routineKinds: if sym.typ.len-1 != args.len: - localError(sym.info, + localError(c.config, sym.info, "NimScript: expected $# arguments, but got $#" % [ $(sym.typ.len-1), $args.len]) else: @@ -1556,18 +1780,18 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = # setup parameters: if not isEmptyType(sym.typ.sons[0]) or sym.kind == skMacro: - putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info)) + putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info, c.config)) # XXX We could perform some type checking here. for i in 1..<sym.typ.len: putIntoReg(tos.slots[i], args[i-1]) result = rawExecute(c, start, tos).regToNode else: - localError(sym.info, + localError(c.config, sym.info, "NimScript: attempt to call non-routine: " & sym.name.s) proc evalStmt*(c: PCtx, n: PNode) = - let n = transformExpr(c.module, n) + let n = transformExpr(c.graph, c.module, n) let start = genStmt(c, n) # execute new instructions; this redundant opcEof check saves us lots # of allocations in 'execute': @@ -1575,86 +1799,80 @@ proc evalStmt*(c: PCtx, n: PNode) = discard execute(c, start) proc evalExpr*(c: PCtx, n: PNode): PNode = - let n = transformExpr(c.module, n) + let n = transformExpr(c.graph, c.module, n) let start = genExpr(c, n) assert c.code[start].opcode != opcEof result = execute(c, start) proc getGlobalValue*(c: PCtx; s: PSym): PNode = - internalAssert s.kind in {skLet, skVar} and sfGlobal in s.flags + internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags result = c.globals.sons[s.position-1] include vmops -# for now we share the 'globals' environment. XXX Coming soon: An API for -# storing&loading the 'globals' environment to get what a component system -# requires. -var - globalCtx*: PCtx - -proc setupGlobalCtx(module: PSym; cache: IdentCache) = - if globalCtx.isNil: - globalCtx = newCtx(module, cache) - registerAdditionalOps(globalCtx) +proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) = + if graph.vm.isNil: + graph.vm = newCtx(module, graph.cache, graph) + registerAdditionalOps(PCtx graph.vm) else: - refresh(globalCtx, module) + refresh(PCtx graph.vm, module) -proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = +proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = #var c = newEvalContext(module, emRepl) #c.features = {allowCast, allowFFI, allowInfiniteLoops} #pushStackFrame(c, newStackFrame()) # XXX produce a new 'globals' environment here: - setupGlobalCtx(module, cache) - result = globalCtx + setupGlobalCtx(module, graph) + result = PCtx graph.vm when hasFFI: - globalCtx.features = {allowFFI, allowCast} - -var oldErrorCount: int + PCtx(graph.vm).features = {allowFFI, allowCast} proc myProcess(c: PPassContext, n: PNode): PNode = + let c = PCtx(c) # don't eval errornous code: - if oldErrorCount == msgs.gErrorCounter: - evalStmt(PCtx(c), n) - result = emptyNode + if c.oldErrorCount == c.config.errorCounter: + evalStmt(c, n) + result = newNodeI(nkEmpty, n.info) else: result = n - oldErrorCount = msgs.gErrorCounter + c.oldErrorCount = c.config.errorCounter proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = myProcess(c, n) -const evalPass* = makePass(myOpen, nil, myProcess, myClose) +const evalPass* = makePass(myOpen, myProcess, myClose) -proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode, +proc evalConstExprAux(module: PSym; + g: ModuleGraph; prc: PSym, n: PNode, mode: TEvalMode): PNode = - let n = transformExpr(module, n) - setupGlobalCtx(module, cache) - var c = globalCtx + let n = transformExpr(g, module, n) + setupGlobalCtx(module, g) + var c = PCtx g.vm let oldMode = c.mode defer: c.mode = oldMode c.mode = mode let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) - if c.code[start].opcode == opcEof: return emptyNode + if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info) assert c.code[start].opcode != opcEof when debugEchoCode: c.echoCode start var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) #for i in 0 ..< c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode - if result.info.line < 0: result.info = n.info + if result.info.col < 0: result.info = n.info -proc evalConstExpr*(module: PSym; cache: IdentCache, e: PNode): PNode = - result = evalConstExprAux(module, cache, nil, e, emConst) +proc evalConstExpr*(module: PSym; g: ModuleGraph; e: PNode): PNode = + result = evalConstExprAux(module, g, nil, e, emConst) -proc evalStaticExpr*(module: PSym; cache: IdentCache, e: PNode, prc: PSym): PNode = - result = evalConstExprAux(module, cache, prc, e, emStaticExpr) +proc evalStaticExpr*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym): PNode = + result = evalConstExprAux(module, g, prc, e, emStaticExpr) -proc evalStaticStmt*(module: PSym; cache: IdentCache, e: PNode, prc: PSym) = - discard evalConstExprAux(module, cache, prc, e, emStaticStmt) +proc evalStaticStmt*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym) = + discard evalConstExprAux(module, g, prc, e, emStaticStmt) -proc setupCompileTimeVar*(module: PSym; cache: IdentCache, n: PNode) = - discard evalConstExprAux(module, cache, nil, n, emStaticStmt) +proc setupCompileTimeVar*(module: PSym; g: ModuleGraph; n: PNode) = + discard evalConstExprAux(module, g, nil, n, emStaticStmt) proc setupMacroParam(x: PNode, typ: PType): TFullReg = case typ.kind @@ -1678,24 +1896,25 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = let posInCall = macroSym.typ.len + i yield (genericParam, call[posInCall]) -var evalMacroCounter: int +# to prevent endless recursion in macro instantiation +const evalMacroLimit = 1000 -proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, - sym: PSym): PNode = +proc evalMacroCall*(module: PSym; g: ModuleGraph; + n, nOrig: PNode, sym: PSym): PNode = # XXX globalError() is ugly here, but I don't know a better solution for now - inc(evalMacroCounter) - if evalMacroCounter > 100: - globalError(n.info, errTemplateInstantiationTooNested) + inc(g.config.evalMacroCounter) + if g.config.evalMacroCounter > evalMacroLimit: + globalError(g.config, n.info, "macro instantiation too nested") # immediate macros can bypass any type and arity checking so we check the # arity here too: if sym.typ.len > n.safeLen and sym.typ.len > 1: - globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [ + globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [ n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)]) - setupGlobalCtx(module, cache) - var c = globalCtx - c.comesFromHeuristic.line = -1 + setupGlobalCtx(module, g) + var c = PCtx g.vm + c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig let start = genProc(c, sym) @@ -1725,18 +1944,18 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, if idx < n.len: tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ) else: - dec(evalMacroCounter) + dec(g.config.evalMacroCounter) c.callsite = nil - localError(n.info, "expected " & $gp.len & + localError(c.config, n.info, "expected " & $gp.len & " generic parameter(s)") elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}: - dec(evalMacroCounter) + dec(g.config.evalMacroCounter) c.callsite = nil - globalError(n.info, "static[T] or typedesc nor supported for .immediate macros") + globalError(c.config, n.info, "static[T] or typedesc nor supported for .immediate macros") # temporary storage: #for i in L ..< maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode if result.info.line < 0: result.info = n.info - if cyclicTree(result): globalError(n.info, errCyclicTree) - dec(evalMacroCounter) + if cyclicTree(result): globalError(c.config, n.info, "macro produced a cyclic tree") + dec(g.config.evalMacroCounter) c.callsite = nil |