# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This file implements the new evaluation engine for Nim code. ## An instruction is 1-3 int32s in memory, it is a register based VM. import ast except getstr import strutils, msgs, vmdef, vmgen, nimsets, types, passes, parser, vmdeps, idents, trees, renderer, options, transf, parseutils, vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl, modulegraphs, sighashes, int128, vmprofiler from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate from magicsys import getSysType const traceCode = defined(nimVMDebug) when hasFFI: import evalffi proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = if x != nil: if recursionLimit == 0: var calls = 0 var x = x while x != nil: inc calls x = x.next msgWriteln(c.config, $calls & " calls omitted\n") return stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1) var info = c.debug[pc] # we now use a format similar to the one in lib/system/excpt.nim var s = "" # todo: factor with quotedFilename if optExcessiveStackTrace in c.config.globalOptions: s = toFullPath(c.config, info) else: s = toFilename(c.config, info) var line = toLinenumber(info) var col = toColumn(info) if line > 0: s.add('(') s.add($line) s.add(", ") s.add($(col + ColOffset)) s.add(')') if x.prc != nil: for k in 1..max(1, 25-s.len): s.add(' ') s.add(x.prc.name.s) msgWriteln(c.config, s) proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int, msg: string, lineInfo: TLineInfo, infoOrigin: InstantiationInfo) {.noinline.} = # noinline to avoid code bloat msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) let action = if c.mode == emRepl: doRaise else: doNothing # XXX test if we want 'globalError' for every mode let lineInfo = if lineInfo == TLineInfo.default: c.debug[pc] else: lineInfo liMessage(c.config, lineInfo, errGenerated, msg, action, infoOrigin) template stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string, lineInfo: TLineInfo = TLineInfo.default) = stackTraceImpl(c, tos, pc, msg, lineInfo, instantiationInfo(-2, fullPaths = true)) return proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " & c.currentExceptionA[3].skipColon.strVal & " [" & c.currentExceptionA[2].skipColon.strVal & "]") when not defined(nimComputedGoto): {.pragma: computedGoto.} proc ensureKind(n: var TFullReg, kind: TRegisterKind) = if n.kind != kind: n = TFullReg(kind: kind) template ensureKind(k: untyped) {.dirty.} = ensureKind(regs[ra], k) template decodeB(k: untyped) {.dirty.} = let rb = instr.regB ensureKind(k) template decodeBC(k: untyped) {.dirty.} = let rb = instr.regB let rc = instr.regC ensureKind(k) template declBC() {.dirty.} = let rb = instr.regB let rc = instr.regC template decodeBImm(k: untyped) {.dirty.} = let rb = instr.regB let imm = instr.regC - byteExcess ensureKind(k) template decodeBx(k: untyped) {.dirty.} = let rbx = instr.regBx - wordExcess ensureKind(k) template move(a, b: untyped) {.dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: bool): bool = # nim bug: `isAssign: static bool` doesn't work, giving odd compiler error template fun(field, T, rkind) = if isAssign: cast[ptr T](address)[] = T(r.field) else: r.ensureKind(rkind) let val = cast[ptr T](address)[] when T is SomeInteger | char: r.field = BiggestInt(val) else: r.field = val return true ## see also typeinfo.getBiggestInt case typ.kind of tyChar: fun(intVal, char, rkInt) of tyInt: fun(intVal, int, rkInt) of tyInt8: fun(intVal, int8, rkInt) of tyInt16: fun(intVal, int16, rkInt) of tyInt32: fun(intVal, int32, rkInt) of tyInt64: fun(intVal, int64, rkInt) of tyUInt: fun(intVal, uint, rkInt) of tyUInt8: fun(intVal, uint8, rkInt) of tyUInt16: fun(intVal, uint16, rkInt) of tyUInt32: fun(intVal, uint32, rkInt) of tyUInt64: fun(intVal, uint64, rkInt) # note: differs from typeinfo.getBiggestInt of tyFloat: fun(floatVal, float, rkFloat) of tyFloat32: fun(floatVal, float32, rkFloat) of tyFloat64: fun(floatVal, float64, rkFloat) else: return false proc createStrKeepNode(x: var TFullReg; keepNode=true) = if x.node.isNil or not keepNode: x.node = newNode(nkStrLit) elif x.node.kind == nkNilLit and keepNode: when defined(useNodeIds): let id = x.node.id x.node[] = TNode(kind: nkStrLit) when defined(useNodeIds): x.node.id = id elif x.node.kind notin {nkStrLit..nkTripleStrLit} or nfAllConst in x.node.flags: # XXX this is hacky; tests/txmlgen triggers it: x.node = newNode(nkStrLit) # It not only hackey, it is also wrong for tgentemplate. The primary # cause of bugs like these is that the VM does not properly distinguish # between variable definitions (var foo = e) and variable updates (foo = e). include vmhooks template createStr(x) = x.node = newNode(nkStrLit) template createSet(x) = x.node = newNode(nkCurly) proc moveConst(x: var TFullReg, y: TFullReg) = x.ensureKind(y.kind) case x.kind of rkNone: discard of rkInt: x.intVal = y.intVal of rkFloat: x.floatVal = y.floatVal of rkNode: x.node = y.node of rkRegisterAddr: x.regAddr = y.regAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr # this seems to be the best way to model the reference semantics # of system.NimNode: template asgnRef(x, y: untyped) = moveConst(x, y) proc copyValue(src: PNode): PNode = if src == nil or nfIsRef in src.flags: return src result = newNode(src.kind) result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags result.comment = src.comment when defined(useNodeIds): if result.id == nodeIdToDebug: echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal else: newSeq(result.sons, src.len) for i in 0..pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#
#
#           The Nim Compiler
#        (c) Copyright 2015 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Implements the new configuration system for Nim. Uses Nim as a scripting
## language.

import
  ast, modules, passes, passaux, condsyms,
  options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
  os, times, osproc, wordrecg, strtabs

# we support 'cmpIgnoreStyle' natively for efficiency:
from strutils import cmpIgnoreStyle, contains

proc listDirs(a: VmArgs, filter: set[PathComponent]) =
  let dir = getString(a, 0)
  var result: seq[string] = @[]
  for kind, path in walkDir(dir):
    if kind in filter: result.add path
  setResult(a, result)

proc setupVM*(module: PSym; scriptName: string): PEvalContext =
  # For Nimble we need to export 'setupVM'.
  result = newCtx(module)
  result.mode = emRepl
  registerAdditionalOps(result)

  # captured vars:
  var errorMsg: string
  var vthisDir = scriptName.splitFile.dir

  template cbconf(name, body) {.dirty.} =
    result.registerCallback "stdlib.system." & astToStr(name),
      proc (a: VmArgs) =
        body

  template cbos(name, body) {.dirty.} =
    result.registerCallback "stdlib.system." & astToStr(name),
      proc (a: VmArgs) =
        try:
          body
        except OSError:
          errorMsg = getCurrentExceptionMsg()

  # Idea: Treat link to file as a file, but ignore link to directory to prevent
  # endless recursions out of the box.
  cbos listFiles:
    listDirs(a, {pcFile, pcLinkToFile})
  cbos listDirs:
    listDirs(a, {pcDir})
  cbos removeDir:
    os.removeDir getString(a, 0)
  cbos removeFile:
    os.removeFile getString(a, 0)
  cbos createDir:
    os.createDir getString(a, 0)
  cbos getOsError:
    setResult(a, errorMsg)
  cbos setCurrentDir:
    os.setCurrentDir getString(a, 0)
  cbos getCurrentDir:
    setResult(a, os.getCurrentDir())
  cbos moveFile:
    os.moveFile(getString(a, 0), getString(a, 1))
  cbos copyFile:
    os.copyFile(getString(a, 0), getString(a, 1))
  cbos getLastModificationTime:
    setResult(a, toSeconds(getLastModificationTime(getString(a, 0))))

  cbos rawExec:
    setResult(a, osproc.execCmd getString(a, 0))

  cbconf getEnv:
    setResult(a, os.getEnv(a.getString 0))
  cbconf existsEnv:
    setResult(a, os.existsEnv(a.getString 0))
  cbconf dirExists:
    setResult(a, os.dirExists(a.getString 0))
  cbconf fileExists:
    setResult(a, os.fileExists(a.getString 0))

  cbconf thisDir:
    setResult(a, vthisDir)
  cbconf put:
    options.setConfigVar(getString(a, 0), getString(a, 1))
  cbconf get:
    setResult(a, options.getConfigVar(a.getString 0))
  cbconf exists:
    setResult(a, options.existsConfigVar(a.getString 0))
  cbconf nimcacheDir:
    setResult(a, options.getNimcacheDir())
  cbconf paramStr:
    setResult(a, os.paramStr(int a.getInt 0))
  cbconf paramCount:
    setResult(a, os.paramCount())
  cbconf cmpIgnoreStyle:
    setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1))
  cbconf cmpIgnoreCase:
    setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
  cbconf setCommand:
    options.command = a.getString 0
    let arg = a.getString 1
    if arg.len > 0:
      gProjectName = arg
      try:
        gProjectFull = canonicalizePath(gProjectPath / gProjectName)
      except OSError:
        gProjectFull = gProjectName
  cbconf getCommand:
    setResult(a, options.command)
  cbconf switch:
    processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
  cbconf hintImpl:
    processSpecificNote(a.getString 0, wHint, passPP, unknownLineInfo(),
      a.getString 1)
  cbconf warningImpl:
    processSpecificNote(a.getString 0, wWarning, passPP, unknownLineInfo(),
      a.getString 1)
  cbconf patchFile:
    let key = a.getString(0) & "_" & a.getString(1)
    var val = a.getString(2).addFileExt(NimExt)
    if {'$', '~'} in val:
      val = pathSubs(val, vthisDir)
    elif not isAbsolute(val):
      val = vthisDir / val
    gModuleOverrides[key] = val
  cbconf selfExe:
    setResult(a, os.getAppFilename())

proc runNimScript*(scriptName: string; freshDefines=true) =
  passes.gIncludeFile = includeModule
  passes.gImportModule = importModule
  if freshDefines: initDefines()

  defineSymbol("nimscript")
  defineSymbol("nimconfig")
  registerPass(semPass)
  registerPass(evalPass)

  appendStr(searchPaths, options.libpath)

  var m = makeModule(scriptName)
  incl(m.flags, sfMainModule)
  vm.globalCtx = setupVM(m, scriptName)

  compileSystemModule()
  processModule(m, llStreamOpen(scriptName, fmRead), nil)

  # ensure we load 'system.nim' again for the real non-config stuff!
  resetAllModulesHard()
  vm.globalCtx = nil
  # do not remove the defined symbols
  #initDefines()
  undefSymbol("nimscript")
  undefSymbol("nimconfig")
the `finally` block ends. savedPC = -1 pc = jumpTo.where - 1 if tos != frame: tos = frame updateRegsAlias of ExceptionGotoFinally: # Jump to the `finally` block first then re-jump here to continue the # traversal of the exception chain savedPC = pc savedFrame = tos pc = jumpTo.where - 1 if tos != frame: tos = frame updateRegsAlias of ExceptionGotoUnhandled: # Nobody handled this exception, error out. bailOut(c, tos) of opcNew: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] 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] inc pc ensureKind(rkNode) let instr2 = c.code[pc] let count = regs[instr2.regA].intVal.int regs[ra].node = newNodeI(nkBracket, c.debug[pc]) regs[ra].node.typ = typ newSeq(regs[ra].node.sons, count) for i in 0.. The codegen has to deal with it # via 'genAsgnPatch'. of opcLdNullReg: let typ = c.types[instr.regBx - wordExcess] if typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}).kind in { tyFloat..tyFloat128}: ensureKind(rkFloat) regs[ra].floatVal = 0.0 else: ensureKind(rkInt) regs[ra].intVal = 0 of opcLdConst: let rb = instr.regBx - wordExcess let cnst = c.constants[rb] if fitsRegister(cnst.typ): reset(regs[ra]) putIntoReg(regs[ra], cnst) else: ensureKind(rkNode) regs[ra].node = cnst of opcAsgnConst: let rb = instr.regBx - wordExcess let cnst = c.constants[rb] if fitsRegister(cnst.typ): putIntoReg(regs[ra], cnst) else: ensureKind(rkNode) regs[ra].node = cnst.copyTree of opcLdGlobal: let rb = instr.regBx - wordExcess - 1 ensureKind(rkNode) regs[ra].node = c.globals[rb] of opcLdGlobalDerefFFI: let rb = instr.regBx - wordExcess - 1 let node = c.globals[rb] let typ = node.typ doAssert node.kind == nkIntLit, $(node.kind) if typ.kind == tyPtr: ensureKind(rkNode) # use nkPtrLit once this is added let node2 = newNodeIT(nkIntLit, c.debug[pc], typ) node2.intVal = cast[ptr int](node.intVal)[] node2.flags.incl nfIsPtr regs[ra].node = node2 elif not derefPtrToReg(node.intVal, typ, regs[ra], isAssign = false): stackTrace(c, tos, pc, "opcLdDeref unsupported type: " & $(typeToString(typ), typ[0].kind)) of opcLdGlobalAddrDerefFFI: let rb = instr.regBx - wordExcess - 1 let node = c.globals[rb] let typ = node.typ var node2 = newNodeIT(nkIntLit, node.info, typ) node2.intVal = node.intVal node2.flags.incl nfIsPtr ensureKind(rkNode) regs[ra].node = node2 of opcLdGlobalAddr: let rb = instr.regBx - wordExcess - 1 ensureKind(rkNodeAddr) regs[ra].nodeAddr = addr(c.globals[rb]) of opcRepr: decodeB(rkNode) createStr regs[ra] regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments}) of opcQuit: if c.mode in {emRepl, emStaticExpr, emStaticStmt}: message(c.config, c.debug[pc], hintQuitCalled) msgQuit(int8(toInt(getOrdValue(regs[ra].regToNode, onError = toInt128(1))))) else: return TFullReg(kind: rkNone) of opcInvalidField: stackTrace(c, tos, pc, errFieldXNotFound & regs[ra].node.strVal) of opcSetLenStr: decodeB(rkNode) #createStrKeepNode regs[ra] regs[ra].node.strVal.setLen(regs[rb].intVal.int) of opcOf: decodeBC(rkInt) let typ = c.types[regs[rc].intVal.int] regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) <= 0) of opcIs: decodeBC(rkInt) let t1 = regs[rb].node.typ.skipTypes({tyTypeDesc}) let t2 = c.types[regs[rc].intVal.int] # XXX: This should use the standard isOpImpl let match = if t2.kind == tyUserTypeClass: true else: sameType(t1, t2) regs[ra].intVal = ord(match) of opcSetLenSeq: decodeB(rkNode) let newLen = regs[rb].intVal.int if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc]) 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, "unhandled exception: value out of range") of opcNarrowU: decodeB(rkInt) regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1) of opcSignExtend: # like opcNarrowS, but no out of range possible decodeB(rkInt) let imm = 64 - rb regs[ra].intVal = ashr(regs[ra].intVal shl imm, imm) of opcIsNil: decodeB(rkInt) let node = regs[rb].node 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[0].kind == nkNilLit and node[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[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[ptr UncheckedArray[TFullReg]](addr regs[0]), currentException: c.currentExceptionA, currentLineInfo: c.debug[pc]) callback(args) regs[ra].node.flags.incl nfIsRef of opcNChild: decodeBC(rkNode) let idx = regs[rc].intVal.int let src = regs[rb].node if src.kind in {nkEmpty..nkNilLit}: stackTrace(c, tos, pc, "cannot get child of node kind: n" & $src.kind) elif idx >=% src.len: stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1)) else: regs[ra].node = src[idx] of opcNSetChild: decodeBC(rkNode) let idx = regs[rb].intVal.int var dest = regs[ra].node if nfSem in dest.flags and allowSemcheckedAstModification notin c.config.legacyFeatures: stackTrace(c, tos, pc, "typechecked nodes may not be modified") elif dest.kind in {nkEmpty..nkNilLit}: stackTrace(c, tos, pc, "cannot set child of node kind: n" & $dest.kind) elif idx >=% dest.len: stackTrace(c, tos, pc, formatErrorIndexBound(idx, dest.len-1)) else: dest[idx] = regs[rc].node of opcNAdd: decodeBC(rkNode) var u = regs[rb].node if nfSem in u.flags and allowSemcheckedAstModification notin c.config.legacyFeatures: stackTrace(c, tos, pc, "typechecked nodes may not be modified") elif u.kind in {nkEmpty..nkNilLit}: stackTrace(c, tos, pc, "cannot add to node kind: n" & $u.kind) else: u.add(regs[rc].node) regs[ra].node = u of opcNAddMultiple: decodeBC(rkNode) let x = regs[rc].node var u = regs[rb].node if nfSem in u.flags and allowSemcheckedAstModification notin c.config.legacyFeatures: stackTrace(c, tos, pc, "typechecked nodes may not be modified") elif u.kind in {nkEmpty..nkNilLit}: stackTrace(c, tos, pc, "cannot add to node kind: n" & $u.kind) else: for i in 0.. 0: c.errorFlag = error elif ast.len != 1: c.errorFlag = formatMsg(c.config, c.debug[pc], errGenerated, "expected expression, but got multiple statements") else: regs[ra].node = ast[0] of opcParseStmtToAst: decodeB(rkNode) var error: string 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) {.nosinks.} = 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 of opcQueryErrorFlag: createStr regs[ra] regs[ra].node.strVal = c.errorFlag c.errorFlag.setLen 0 of opcCallSite: ensureKind(rkNode) if c.callsite != nil: regs[ra].node = c.callsite else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite") of opcNGetLineInfo: decodeBImm(rkNode) let n = regs[rb].node 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 opcNSetLineInfo: decodeB(rkNode) regs[ra].node.info = regs[rb].node.info of opcEqIdent: decodeBC(rkInt) # aliases for shorter and easier to understand code below var aNode = regs[rb].node var bNode = regs[rc].node # Skipping both, `nkPostfix` and `nkAccQuoted` for both # arguments. `nkPostfix` exists only to tag exported symbols # and therefor it can be safely skipped. Nim has no postfix # operator. `nkAccQuoted` is used to quote an identifier that # wouldn't be allowed to use in an unquoted context. if aNode.kind == nkPostfix: aNode = aNode[1] if aNode.kind == nkAccQuoted: aNode = aNode[0] if bNode.kind == nkPostfix: bNode = bNode[1] if bNode.kind == nkAccQuoted: bNode = bNode[0] # These vars are of type `cstring` to prevent unnecessary string copy. 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: 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 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") else: regs[ra].node = newNodeI(nkIdent, c.debug[pc]) regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal) regs[ra].node.flags.incl nfIsRef of opcSetType: let typ = c.types[instr.regBx - wordExcess] if regs[ra].kind != rkNode: let temp = regToNode(regs[ra]) ensureKind(rkNode) regs[ra].node = temp regs[ra].node.info = c.debug[pc] regs[ra].node.typ = typ of opcConv: let rb = instr.regB inc pc let desttyp = c.types[c.code[pc].regBx - wordExcess] inc pc let srctyp = c.types[c.code[pc].regBx - wordExcess] if opConv(c, regs[ra], regs[rb], desttyp, srctyp): stackTrace(c, tos, pc, errIllegalConvFromXtoY % [ typeToString(srctyp), typeToString(desttyp)]) of opcCast: let rb = instr.regB inc pc let desttyp = c.types[c.code[pc].regBx - wordExcess] inc pc let srctyp = c.types[c.code[pc].regBx - wordExcess] when hasFFI: let dest = fficast(c.config, regs[rb].node, desttyp) # todo: check whether this is correct # asgnRef(regs[ra], dest) putIntoReg(regs[ra], dest) else: globalError(c.config, c.debug[pc], "cannot evaluate cast") of opcNSetIntVal: decodeB(rkNode) var dest = regs[ra].node if dest.kind in {nkCharLit..nkUInt64Lit} and regs[rb].kind in {rkInt}: dest.intVal = regs[rb].intVal elif dest.kind == nkSym and dest.sym.kind == skEnumField: stackTrace(c, tos, pc, "`intVal` cannot be changed for an enum symbol.") else: stackTrace(c, tos, pc, errFieldXNotFound & "intVal") of opcNSetFloatVal: decodeB(rkNode) var dest = regs[ra].node if dest.kind in {nkFloatLit..nkFloat64Lit} and regs[rb].kind in {rkFloat}: dest.floatVal = regs[rb].floatVal else: 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") 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") of opcNSetType: decodeB(rkNode) let b = regs[rb].node 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) var dest = regs[ra].node if dest.kind in {nkStrLit..nkTripleStrLit} and regs[rb].kind in {rkNode}: dest.strVal = regs[rb].node.strVal elif dest.kind == nkCommentStmt and regs[rb].kind in {rkNode}: dest.comment = regs[rb].node.strVal else: 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.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 != 0'u16: c.comesFromHeuristic elif c.callsite != nil and c.callsite.safeLen > 1: c.callsite[1].info else: c.debug[pc]) x.flags.incl nfIsRef # prevent crashes in the compiler resulting from wrong macros: if x.kind == nkIdent: x.ident = c.cache.emptyIdent regs[ra].node = x of opcNCopyNimNode: decodeB(rkNode) regs[ra].node = copyNode(regs[rb].node) of opcNCopyNimTree: decodeB(rkNode) regs[ra].node = copyTree(regs[rb].node) of opcNDel: decodeBC(rkNode) let bb = regs[rb].intVal.int for i in 0.. ord(high(TSymKind)): internalError(c.config, c.debug[pc], "request to create symbol of invalid kind") var sym = newSym(k.TSymKind, getIdent(c.cache, name), nextSymId c.idgen, 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 declBC() let destKey = regs[rb].node.strVal let by = regs[rc].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 declBC() let destKey = regs[rb].node.strVal let val = regs[rc].node if not contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey] = newTree(nkStmtList, val) else: g.cacheSeqs[destKey].add val recordAdd(c, c.debug[pc], destKey, val) of opcNcsIncl: let g = c.graph declBC() let destKey = regs[rb].node.strVal let val = regs[rc].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, formatErrorIndexBound(idx, g.cacheSeqs[destKey].len-1)) 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 c.config, typ != nil while typ.kind == tyTypeDesc and typ.len > 0: typ = typ[0] createStr regs[ra] regs[ra].node.strVal = typ.typeToString(preferExported) of opcMarshalLoad: let ra = instr.regA let rb = instr.regB inc pc let typ = c.types[c.code[pc].regBx - wordExcess] putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config, c.idgen)) of opcMarshalStore: decodeB(rkNode) inc pc let typ = c.types[c.code[pc].regBx - wordExcess] createStrKeepNode(regs[ra]) storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config) c.profiler.leave(c) inc pc proc execute(c: PCtx, start: int): PNode = var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) result = rawExecute(c, start, tos).regToNode proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = c.loopIterations = c.config.maxLoopIterationsVM if sym.kind in routineKinds: if sym.typ.len-1 != args.len: localError(c.config, sym.info, "NimScript: expected $# arguments, but got $#" % [ $(sym.typ.len-1), $args.len]) else: let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) let maxSlots = sym.offset newSeq(tos.slots, maxSlots) # setup parameters: if not isEmptyType(sym.typ[0]) or sym.kind == skMacro: putIntoReg(tos.slots[0], getNullValue(sym.typ[0], sym.info, c.config)) # XXX We could perform some type checking here. for i in 1.. 0: return n let n = transformExpr(g, idgen, module, n) setupGlobalCtx(module, g, idgen) var c = PCtx g.vm let oldMode = c.mode c.mode = mode let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) 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.. 0: return errorNode(idgen, module, n) # XXX globalError() is ugly here, but I don't know a better solution for now 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(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [ n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)]) setupGlobalCtx(module, g, idgen) var c = PCtx g.vm let oldMode = c.mode c.mode = emStaticStmt c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig c.templInstCounter = templInstCounter let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) let maxSlots = sym.offset newSeq(tos.slots, maxSlots) # setup arguments: var L = n.safeLen if L == 0: L = 1 # This is wrong for tests/reject/tind1.nim where the passed 'else' part # doesn't end up in the parameter: #InternalAssert tos.slots.len >= L # return value: tos.slots[0] = TFullReg(kind: rkNode, node: newNodeI(nkEmpty, n.info)) # setup parameters: for i in 1..