From 325dd44e81b44814986f5d586f1934a814c1b148 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 3 Apr 2014 22:35:10 +0200 Subject: gensym'ed symbols work with lambda-lifting; closures produce objects instead of tuples for easier debugging --- compiler/ccgstmts.nim | 2 +- compiler/lambdalifting.nim | 72 ++++++++++++++++++++++++---------------------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 990153fc7..311149cb3 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -138,7 +138,7 @@ proc genBreakState(p: BProc, n: PNode) = if n.sons[0].kind == nkClosure: # XXX this produces quite inefficient code! initLocExpr(p, n.sons[0].sons[1], a) - lineF(p, cpsStmts, "if (($1->Field0) < 0) break;$n", [rdLoc(a)]) + lineF(p, cpsStmts, "if (((NI*) $1)[0] < 0) break;$n", [rdLoc(a)]) else: initLocExpr(p, n.sons[0], a) # the environment is guaranteed to contain the 'state' field at offset 0: diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 6da156ba6..440468ac4 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -121,14 +121,14 @@ type capturedVars: seq[PSym] # captured variables in this environment deps: seq[TDep] # dependencies up: PEnv - tup: PType + obj: PType - TInnerContext {.final.} = object + TInnerContext = object fn: PSym closureParam: PSym localsToAccess: TIdNodeTable - TOuterContext {.final.} = object + TOuterContext = object fn: PSym # may also be a module! currentEnv: PEnv isIter: bool # first class iterator? @@ -139,8 +139,13 @@ type up: POuterContext closureParam, state, resultSym: PSym # only if isIter - tup: PType # only if isIter + obj: PType # only if isIter +proc createObj(owner: PSym, info: TLineInfo): PType = + result = newType(tyObject, owner) + rawAddSon(result, nil) + incl result.flags, tfFinal + result.n = newNodeI(nkRecList, info) proc getStateType(iter: PSym): PType = var n = newNodeI(nkRange, iter.info) @@ -185,13 +190,14 @@ proc getEnvParam(routine: PSym): PSym = if hidden.kind == nkSym and hidden.sym.name.s == paramName: result = hidden.sym -proc addField(tup: PType, s: PSym) = - var field = newSym(skField, s.name, s.owner, s.info) +proc addField(obj: PType; s: PSym) = + # because of 'gensym' support, we have to mangle the name with its ID. + # This is hacky but the clean solution is much more complex than it looks. + var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info) let t = skipIntLit(s.typ) field.typ = t - field.position = sonsLen(tup) - addSon(tup.n, newSymNode(field)) - rawAddSon(tup, t) + field.position = sonsLen(obj.n) + addSon(obj.n, newSymNode(field)) proc initIterContext(c: POuterContext, iter: PSym) = c.fn = iter @@ -199,25 +205,24 @@ proc initIterContext(c: POuterContext, iter: PSym) = var cp = getEnvParam(iter) if cp == nil: - c.tup = newType(tyTuple, iter) - c.tup.n = newNodeI(nkRecList, iter.info) + c.obj = createObj(iter, iter.info) cp = newSym(skParam, getIdent(paramName), iter, iter.info) incl(cp.flags, sfFromGeneric) cp.typ = newType(tyRef, iter) - rawAddSon(cp.typ, c.tup) + rawAddSon(cp.typ, c.obj) addHiddenParam(iter, cp) c.state = createStateField(iter) - addField(c.tup, c.state) + addField(c.obj, c.state) else: - c.tup = cp.typ.sons[0] - assert c.tup.kind == tyTuple - if c.tup.len > 0: - c.state = c.tup.n[0].sym + c.obj = cp.typ.sons[0] + assert c.obj.kind == tyObject + if c.obj.n.len > 0: + c.state = c.obj.n[0].sym else: c.state = createStateField(iter) - addField(c.tup, c.state) + addField(c.obj, c.state) c.closureParam = cp if iter.typ.sons[0] != nil: @@ -244,8 +249,7 @@ proc newEnv(outerProc: PSym, up: PEnv, n: PNode): PEnv = new(result) result.deps = @[] result.capturedVars = @[] - result.tup = newType(tyTuple, outerProc) - result.tup.n = newNodeI(nkRecList, outerProc.info) + result.obj = createObj(outerProc, outerProc.info) result.up = up result.attachedNode = n @@ -254,28 +258,28 @@ proc addCapturedVar(e: PEnv, v: PSym) = if x == v: return # XXX meh, just add the state field for every closure for now, it's too # hard to figure out if it comes from a closure iterator: - if e.tup.len == 0: addField(e.tup, createStateField(v.owner)) + if e.obj.n.len == 0: addField(e.obj, createStateField(v.owner)) e.capturedVars.add(v) - addField(e.tup, v) + addField(e.obj, v) proc addDep(e, d: PEnv, owner: PSym): PSym = for x, field in items(e.deps): if x == d: return field - var pos = sonsLen(e.tup) + var pos = sonsLen(e.obj.n) result = newSym(skField, getIdent(upName & $pos), owner, owner.info) result.typ = newType(tyRef, owner) result.position = pos - assert d.tup != nil - rawAddSon(result.typ, d.tup) - addField(e.tup, result) + assert d.obj != nil + rawAddSon(result.typ, d.obj) + addField(e.obj, result) e.deps.add((d, result)) proc indirectAccess(a: PNode, b: PSym, info: TLineInfo): PNode = # returns a[].b as a node var deref = newNodeI(nkHiddenDeref, info) deref.typ = a.typ.sons[0] - assert deref.typ.kind == tyTuple - let field = getSymFromList(deref.typ.n, b.name) + assert deref.typ.kind == tyObject + let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id)) assert field != nil, b.name.s addSon(deref, a) result = newNodeI(nkDotExpr, info) @@ -302,11 +306,11 @@ proc addClosureParam(i: PInnerContext, e: PEnv) = cp = newSym(skParam, getIdent(paramName), i.fn, i.fn.info) incl(cp.flags, sfFromGeneric) cp.typ = newType(tyRef, i.fn) - rawAddSon(cp.typ, e.tup) + rawAddSon(cp.typ, e.obj) addHiddenParam(i.fn, cp) else: - e.tup = cp.typ.sons[0] - assert e.tup.kind == tyTuple + e.obj = cp.typ.sons[0] + assert e.obj.kind == tyObject i.closureParam = cp #echo "closure param added for ", i.fn.name.s, " ", i.fn.id @@ -357,7 +361,7 @@ proc captureVar(o: POuterContext, i: PInnerContext, local: PSym, access = indirectAccess(access, addDep(e, it, i.fn), info) access = indirectAccess(access, local, info) if o.isIter: - if not containsOrIncl(o.capturedVars, local.id): addField(o.tup, local) + if not containsOrIncl(o.capturedVars, local.id): addField(o.obj, local) else: incl(o.capturedVars, local.id) idNodeTablePut(i.localsToAccess, local, access) @@ -543,7 +547,7 @@ proc newClosureCreationVar(o: POuterContext; e: PEnv): PSym = result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info) incl(result.flags, sfShadowed) result.typ = newType(tyRef, o.fn) - result.typ.rawAddSon(e.tup) + result.typ.rawAddSon(e.obj) proc getClosureVar(o: POuterContext; e: PEnv): PSym = if e.createdVar == nil: @@ -669,7 +673,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = var local = n.sym if o.isIter and interestingIterVar(local) and o.fn.id == local.owner.id: - if not containsOrIncl(o.capturedVars, local.id): addField(o.tup, local) + if not containsOrIncl(o.capturedVars, local.id): addField(o.obj, local) return indirectAccess(newSymNode(o.closureParam), local, n.info) var closure = PEnv(idTableGet(o.lambdasToEnv, local)) -- cgit 1.4.1-2-gfad0 From 6a055990f90ef943cb8f6f0ae73c2a011009f939 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 4 Apr 2014 08:19:02 +0200 Subject: minor VM bugfix --- compiler/vmgen.nim | 1 + todo.txt | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 85416f7bc..434cb0932 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1034,6 +1034,7 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = proc setSlot(c: PCtx; v: PSym) = # XXX generate type initialization here? if v.position == 0: + if c.prc.maxSlots == 0: c.prc.maxSlots = 1 v.position = c.prc.maxSlots c.prc.slots[v.position] = (inUse: true, kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) diff --git a/todo.txt b/todo.txt index 44b3eac2b..dcdd6e968 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,6 @@ version 0.9.4 ============= -- fix GC issues - Bugs ==== -- cgit 1.4.1-2-gfad0 From 033f2bbbf267cd4fe4f2567aca8aa7483201a737 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 4 Apr 2014 09:06:29 +0200 Subject: new VM: fixes for exception handling --- compiler/vm.nim | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 7355e85fe..b365dba9a 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -222,13 +222,14 @@ proc pushSafePoint(f: PStackFrame; pc: int) = proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop() -proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: seq[TFullReg]): int = +proc cleanUpOnException(c: PCtx; tos: PStackFrame): + tuple[pc: int, f: PStackFrame] = let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: while f.safePoints.isNil or f.safePoints.len == 0: f = f.next - if f.isNil: return -1 + if f.isNil: return (-1, nil) var pc2 = f.safePoints[f.safePoints.high] var nextExceptOrFinally = -1 @@ -244,13 +245,13 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: seq[TFullReg]): int = c.currentExceptionB = c.currentExceptionA c.currentExceptionA = nil # execute the corresponding handler: - return pc2 + return (pc2, f) inc pc2 if nextExceptOrFinally >= 0: pc2 = nextExceptOrFinally if c.code[pc2].opcode == opcFinally: # execute the corresponding handler, but don't quit walking the stack: - return pc2 + return (pc2, f) # not the right one: discard f.safePoints.pop @@ -869,19 +870,27 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcFinallyEnd: if c.currentExceptionA != nil: # we are in a cleanup run: - pc = cleanUpOnException(c, tos, regs)-1 - if pc < 0: + let (newPc, newTos) = cleanUpOnException(c, tos) + if newPc-1 < 0: bailOut(c, tos) return + pc = newPc-1 + if tos != newTos: + tos = newTos + move(regs, tos.slots) of opcRaise: let raised = regs[ra].node c.currentExceptionA = raised c.exceptionInstr = pc + let (newPc, newTos) = cleanUpOnException(c, tos) # -1 because of the following 'inc' - pc = cleanUpOnException(c, tos, regs) - 1 - if pc < 0: + if pc-1 < 0: bailOut(c, tos) return + pc = newPc -1 + if tos != newTos: + tos = newTos + move(regs, tos.slots) of opcNew: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] -- cgit 1.4.1-2-gfad0 From 8d1c4a23dee164ec0aff3dcf6bd3b769bc8b4148 Mon Sep 17 00:00:00 2001 From: Jason Livesay Date: Fri, 4 Apr 2014 16:11:43 -0700 Subject: Allow multi/exec replies so transactions work. --- lib/pure/redis.nim | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim index 8171dc12b..563e522df 100644 --- a/lib/pure/redis.nim +++ b/lib/pure/redis.nim @@ -45,7 +45,7 @@ proc raiseInvalidReply(expected, got: char) = [$expected, $got]) proc raiseNoOK(status: string) = - if status != "OK": + if status != "QUEUED" and status != "OK": raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status) proc parseStatus(r: TRedis): TRedisStatus = @@ -64,6 +64,10 @@ proc parseStatus(r: TRedis): TRedisStatus = proc parseInteger(r: TRedis): TRedisInteger = var line = "" r.socket.readLine(line) + + if line == "+QUEUED": # inside of multi + return -1 + if line == "": raise newException(ERedis, "Server closed connection prematurely") @@ -84,7 +88,10 @@ proc recv(sock: TSocket, size: int): TaintedString = proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = var line = "" r.socket.readLine(line.TaintedString) - + + if line == "+QUEUED" or line == "+OK": # inside of a transaction (multi) + return nil + # Error. if line[0] == '-': raise newException(ERedis, strip(line)) @@ -107,6 +114,9 @@ proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = proc parseMultiBulk(r: TRedis): TRedisList = var line = TaintedString"" r.socket.readLine(line) + + if line == "+QUEUED": # inside of a transaction (multi) + return nil if line.string[0] != '*': raiseInvalidReply('*', line.string[0]) @@ -722,6 +732,7 @@ proc discardMulti*(r: TRedis) = proc exec*(r: TRedis): TRedisList = ## Execute all commands issued after MULTI r.sendCommand("EXEC") + return r.parseMultiBulk() proc multi*(r: TRedis) = -- cgit 1.4.1-2-gfad0 From e6d2490b9c71fcd7b30ed3afaaacb11331a21979 Mon Sep 17 00:00:00 2001 From: Jason Livesay Date: Fri, 4 Apr 2014 23:29:50 -0700 Subject: Support transactions --- lib/pure/redis.nim | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim index 563e522df..f4c45b99c 100644 --- a/lib/pure/redis.nim +++ b/lib/pure/redis.nim @@ -85,13 +85,7 @@ proc recv(sock: TSocket, size: int): TaintedString = if sock.recv(cstring(result), size) != size: raise newException(EInvalidReply, "recv failed") -proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = - var line = "" - r.socket.readLine(line.TaintedString) - - if line == "+QUEUED" or line == "+OK": # inside of a transaction (multi) - return nil - +proc parseSingle(r: TRedis, line:string, allowMBNil = False): TRedisString = # Error. if line[0] == '-': raise newException(ERedis, strip(line)) @@ -101,6 +95,9 @@ proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = if line == "*-1": return RedisNil + if line == "+QUEUED" or line == "+OK" : # inside of a transaction (multi) + return nil + if line[0] != '$': raiseInvalidReply('$', line[0]) @@ -111,6 +108,32 @@ proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = var s = r.socket.recv(numBytes+2) result = strip(s.string) +proc parseMultiLines(r: TRedis, countLine:string): TRedisList = + if countLine.string[0] != '*': + raiseInvalidReply('*', countLine.string[0]) + + var numElems = parseInt(countLine.string.substr(1)) + if numElems == -1: return nil + result = @[] + for i in 1..numElems: + var line = "" + r.socket.readLine(line.TaintedString) + if line[0] == '*': # after exec() may contain more multi-bulk replies + var parsed = r.parseMultiLines(line) + for item in parsed: + result.add(item) + else: + result.add(r.parseSingle(line)) + +proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = + var line = "" + r.socket.readLine(line.TaintedString) + + if line == "+QUEUED" or line == "+OK": # inside of a transaction (multi) + return nil + + return r.parseSingle(line, allowMBNil) + proc parseMultiBulk(r: TRedis): TRedisList = var line = TaintedString"" r.socket.readLine(line) @@ -118,14 +141,8 @@ proc parseMultiBulk(r: TRedis): TRedisList = if line == "+QUEUED": # inside of a transaction (multi) return nil - if line.string[0] != '*': - raiseInvalidReply('*', line.string[0]) - - var numElems = parseInt(line.string.substr(1)) - if numElems == -1: return nil - result = @[] - for i in 1..numElems: - result.add(r.parseBulk()) + return r.parseMultiLines(line) + proc sendCommand(r: TRedis, cmd: string, args: varargs[string]) = var request = "*" & $(1 + args.len()) & "\c\L" -- cgit 1.4.1-2-gfad0 From 4835199125415dbd40d863ea945a71bab9b6962b Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 5 Apr 2014 21:05:07 +0200 Subject: new VM: proper shield against endless recursions --- compiler/vm.nim | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index b365dba9a..fb8749250 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -48,9 +48,17 @@ type # XXX 'break' should perform cleanup actions # What does the C backend do for it? -proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int) = +proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = if x != nil: - stackTraceAux(c, x.next, x.comesFrom) + if recursionLimit == 0: + var calls = 0 + var x = x + while x != nil: + inc calls + x = x.next + msgWriteln($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 = toFilename(info) -- cgit 1.4.1-2-gfad0