fixes #7827, bindSym enhancement (#8499)

* bindSym power up, working prototype

* update bindSym doc

* add bindSym test

* fix some typo

* fix bindSym doc

* get rid of specialops field from vm

* add experimental: dynamicBindSym
This commit is contained in:
andri lim
2018-08-02 17:56:44 +07:00
committed by Andreas Rumpf
parent 674bd7bfad
commit 78c0ac5407
9 changed files with 183 additions and 13 deletions

View File

@@ -196,4 +196,9 @@
- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
for more info.
- macros.bindSym now capable to accepts not only literal string or string constant expression.
bindSym enhancement make it also can accepts computed string or ident node inside macros /
compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch
### Bugfixes

View File

@@ -116,7 +116,8 @@ type
callOperator,
parallel,
destructor,
notnil
notnil,
dynamicBindSym
SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf

View File

@@ -207,6 +207,67 @@ proc semBindSym(c: PContext, n: PNode): PNode =
else:
errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode =
if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}:
localError(c.config, info.info, errStringOrIdentNodeExpected)
return errorNode(c, n)
if isMixin < 0 or isMixin > high(TSymChoiceRule).int:
localError(c.config, info.info, errConstExprExpected)
return errorNode(c, n)
let id = if n.kind == nkIdent: n
else: newIdentNode(getIdent(c.cache, n.strVal), info.info)
let tmpScope = c.currentScope
c.currentScope = scope
let s = qualifiedLookUp(c, id, {checkUndeclared})
if s != nil:
# we need to mark all symbols:
result = symChoice(c, id, s, TSymChoiceRule(isMixin))
else:
errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s
else: n.strVal)
c.currentScope = tmpScope
proc semDynamicBindSym(c: PContext, n: PNode): PNode =
# inside regular code, bindSym resolves to the sym-choice
# nodes (see tinspectsymbol)
if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc):
return semBindSym(c, n)
if c.graph.vm.isNil:
setupGlobalCtx(c.module, c.graph)
let
vm = PCtx c.graph.vm
# cache the current scope to
# prevent it lost into oblivion
scope = c.currentScope
# cannot use this
# vm.config.features.incl dynamicBindSym
proc bindSymWrapper(a: VmArgs) =
# capture PContext and currentScope
# param description:
# 0. ident, a string literal / computed string / or ident node
# 1. bindSym rule
# 2. info node
a.setResult opBindSym(c, scope, a.getNode(0), a.getInt(1).int, a.getNode(2))
let
# altough we use VM callback here, it is not
# executed like 'normal' VM callback
idx = vm.registerCallback("bindSymImpl", bindSymWrapper)
# dummy node to carry idx information to VM
idxNode = newIntTypeNode(nkIntLit, idx, c.graph.getSysType(TLineInfo(), tyInt))
result = copyNode(n)
for x in n: result.add x
result.add n # info node
result.add idxNode
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semOf(c: PContext, n: PNode): PNode =
@@ -270,7 +331,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mOf: result = semOf(c, n)
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
of mShallowCopy: result = semShallowCopy(c, n, flags)
of mNBindSym: result = semBindSym(c, n)
of mNBindSym:
if dynamicBindSym notin c.features:
result = semBindSym(c, n)
else:
result = semDynamicBindSym(c, n)
of mProcCall:
result = n
result.typ = n[1].typ

View File

@@ -11,6 +11,7 @@
# included from sem.nim
const
errStringOrIdentNodeExpected = "string or ident node expected"
errStringLiteralExpected = "string literal expected"
errIntLiteralExpected = "integer literal expected"
errWrongNumberOfVariables = "wrong number of variables"

View File

@@ -1222,9 +1222,25 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
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
@@ -1773,7 +1789,7 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
include vmops
proc setupGlobalCtx(module: PSym; graph: ModuleGraph) =
proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) =
if graph.vm.isNil:
graph.vm = newCtx(module, graph.cache, graph)
registerAdditionalOps(PCtx graph.vm)

View File

@@ -136,7 +136,7 @@ type
opcLdGlobalAddr, # dest = addr(globals[Bx])
opcLdImmInt, # dest = immediate value
opcNBindSym,
opcNBindSym, opcNDynBindSym,
opcSetType, # dest.typ = types[Bx]
opcTypeTrait,
opcMarshalLoad, opcMarshalStore,
@@ -229,7 +229,8 @@ proc refresh*(c: PCtx, module: PSym) =
c.prc = PProc(blocks: @[])
c.loopIterations = MaxLoopIterations
proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} =
result = c.callbacks.len
c.callbacks.add((name, callback))
const

View File

@@ -817,6 +817,43 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
c.freeTemp(tmp2)
c.freeTemp(tmp3)
proc genBindSym(c: PCtx; n: PNode; dest: var TDest) =
# nah, cannot use c.config.features because sempass context
# can have local experimental switch
# if dynamicBindSym notin c.config.features:
if n.len == 2: # hmm, reliable?
# bindSym with static input
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
let idx = c.genLiteral(n[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcNBindSym, dest, idx)
else:
localError(c.config, n.info, "invalid bindSym usage")
else:
# experimental bindSym
if dest < 0: dest = c.getTemp(n.typ)
let x = c.getTempRange(n.len, slotTempUnknown)
# callee symbol
var tmp0 = TDest(x)
c.genLit(n.sons[0], tmp0)
# original parameters
for i in 1..<n.len-2:
var r = TRegister(x+i)
c.gen(n.sons[i], r)
# info node
var tmp1 = TDest(x+n.len-2)
c.genLit(n.sons[^2], tmp1)
# payload idx
var tmp2 = TDest(x+n.len-1)
c.genLit(n.sons[^1], tmp2)
c.gABC(n, opcNDynBindSym, dest, x, n.len)
c.freeTempRange(x, n.len)
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
case m
of mAnd: c.genAndOr(n, opcFJmp, dest)
@@ -1137,13 +1174,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree)
of mNBindSym:
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
let idx = c.genLiteral(n[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcNBindSym, dest, idx)
else:
localError(c.config, n.info, "invalid bindSym usage")
of mNBindSym: genBindSym(c, n, dest)
of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)

View File

@@ -382,16 +382,24 @@ type
{.deprecated: [TBindSymRule: BindSymRule].}
proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
magic: "NBindSym", noSideEffect.}
## creates a node that binds `ident` to a symbol node. The bound symbol
## may be an overloaded symbol.
## if `ident` is a NimNode, it must have nkIdent kind.
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
## returned even if the symbol is not ambiguous.
##
## experimental feature:
## use {.experimental: "dynamicBindSym".} to activate it
## if called from template / regular code, `ident` and `rule` must be
## constant expression / literal value.
## if called from macros / compile time procs / static blocks,
## `ident` and `rule` can be VM computed value.
proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
magic: "NGenSym", noSideEffect.}

View File

@@ -1,4 +1,9 @@
discard """
msg: '''initApple
deinitApple
Coral
enum
redCoral, blackCoral'''
output: '''TFoo
TBar'''
"""
@@ -23,3 +28,40 @@ macro test: untyped =
bindSym("TBar"))
test()
# issue 7827, bindSym power up
{.experimental: "dynamicBindSym".}
type
Apple = ref object
name: string
color: int
weight: int
proc initApple(name: string): Apple =
discard
proc deinitApple(x: Apple) =
discard
macro wrapObject(obj: typed, n: varargs[untyped]): untyped =
let m = n[0]
for x in m:
var z = bindSym x
echo z.repr
wrapObject(Apple):
initApple
deinitApple
type
Coral = enum
redCoral
blackCoral
macro mixer(): untyped =
let m = "Co" & "ral"
let x = bindSym(m)
echo x.repr
echo getType(x).repr
mixer()