should the {.exportc.} pragma add the symbol to a module.exports so that it can be used in other nodejs programs?
For example,
foo.nim
proc fib(a: cint): cint {.exportc.} =
if a <= 2:
result = 1
else:
result = fib(a - 1) + fib(a - 2)
When compiled to target node, nim js -d:nodejs foo.nim will not export a compatible function to be used in other node programs (AFAIK, if this issue is noise and there is a way to do this - apologies). To me,
the.js
let fib = require("./foo.js").fib;
console.log(fib(10));
should work. It seems the way to do this would be for jsgen to track exports if the symbol is directed to be exported. I am new to nim's codegen, but would the general fix be to add a proc genExportc(p: PProc; procname, filename: Rope): Rope that extends the file to include module.exports.$1 = $1 on the procname if exportc is present in the procdef? I am happy to do this unless this is misguided, undesirable, etc..
Not doing this
The question prompting the issue was here
I don't think this should be done by the codegen. The idea of {.exportc.} is that the symbol is not mangled by the compiler. This doesn't necessary mean that the symbol will be exported cross-module (similar to how you need both {.exportc, dynlib.} to export a Nim function to a DLL).
We can write a simple {.exportNodeJs.} macro to solve this though:
import macros
when defined(nodejs):
import jsffi
var module {.importc, nodecl.}: JsObject
macro exportNodeJs(body: typed): untyped =
when defined(nodejs):
expectKind body, RoutineNodes
result = newStmtList()
result.add body
let
exportSym = body[0]
exportName = ident exportSym.strVal
exports = newCall(bindSym".", bindSym"module", ident"exports")
result.add newCall(bindSym".=", exports, exportName, exportSym)
else:
result = newStmtList().add body
proc fib(a: cint): cint {.exportNodeJs.} =
if a <= 2:
result = 1
else:
result = fib(a - 1) + fib(a - 2)
And here's the generated JS:
function fib_2651445(a_2651447) {
var result_2651448 = 0;
var F={procname:"test.fib",prev:framePtr,filename:"test.nim",line:0};
framePtr = F;
if ((a_2651447 <= 2)) {
F.line = 26;
result_2651448 = 1;
}
else {
F.line = 28;
result_2651448 = addInt(fib_2651445(subInt(a_2651447, 1)), fib_2651445(subInt(a_2651447, 2)));
}
framePtr = F.prev;
return result_2651448;
}
var F={procname:"module test",prev:framePtr,filename:"test.nim",line:0};
framePtr = F;
F.line = 24;
module.exports.fib = fib_2651445;
framePtr = F.prev;
We can add this to jsffi, probably.
Most helpful comment
I don't think this should be done by the codegen. The idea of
{.exportc.}is that the symbol is not mangled by the compiler. This doesn't necessary mean that the symbol will be exported cross-module (similar to how you need both{.exportc, dynlib.}to export a Nim function to a DLL).We can write a simple
{.exportNodeJs.}macro to solve this though:And here's the generated JS:
We can add this to
jsffi, probably.