Runtime module loading support
This update introduces runtime module import support and extends the bytecode format to handle exported symbols from precompiled modules.
-
Runtime Module Imports: The compiler now supports a function-like
import()statement for dynamically loading modules during execution. Unlikerequire(), imported code follows module semantics (strict mode, no top-level returns, and allowed exports). A new-cmodulecompile flag enables precompiling module files into bytecode. -
Export Symbol Encoding: The bytecode format now includes an export symbol section, allowing the runtime to reconstruct namespace objects when loading precompiled modules.
Together, these changes lay the groundwork for dynamic module loading in ucode.
Am I missing something? import() seems to return an empty object, while I would have expected the export. a.uc:
function X()
{
print( 'in X()\n' );
}
export { X };
b.uc:
const a = import( 'a' );
print( a );
a.X();
$ ucode b.uc
{ }
Type error: left-hand side is not a function
In b.uc, line 3, byte 5:
`a.X();`
^-- Near here
That should work, will check
Should work now
OK, I confirm it works now. And I can also import() a compiled module, and use it. But is it by design that 'import from' doesn't work in a compiled module? ucode complains about the she-bang. (Should it /have/ a she-bang when compiled as module?)
See the latest commit in this branch, it will address this issue by implicitly compiling static imports of precompiled modules into dynamic runtime imports (no code changes required on your end).
OK, thanks. After some tests I come to this truth table:
| import from | import() | require() | |
|---|---|---|---|
| dynamic library | Works | Works | Works |
| script | Works | Works | Only works with 'return { exports };', in which case 'export' is forbidden. |
| compiled script | Compile error on 'export' | Compile error on 'export' | Compile error on 'export', but 'return { exports };' works |
| 'module compiled' script | Works | Works | Works, but compile error on 'return { exports };'. So only the module script body is executed |
Futher, to my surprise I can have more 'export {};' blocks in a script, which get merged. Nice.
I can't execute a script containing an 'export {};', it can't be compiled either. But if I 'module compile' it, it can be both imported, and executed.
In the latter case, is there a way to detect that, and have a way to be both a utility and a library, comparable to nodejs
if (require.main === module) {
// do something
}
?
@jow- Is there anything which is keeping you from merging this?
I was somewhat held up by your request for a way to check whether code is running as module or standalone and I couldn't come up with some simple or clean API.
I am going to merge this for now.
Ah, thanks. It was more a question than a request. Meanwhile I found an answer:
function somefunction( arg )
{
print( arg, '\n' );
}
export { somefunction };
function main()
{
somefunction( 'locally called' );
}
if( 'module,uc' == pop( split( global.SCRIPT_NAME, '/' ) ) )
{
main();
}
The only (?) downside is that you have to know the name of the module inside the module.
You could also do something like:
function runsAsModule() {
try {
die();
}
catch(e) {
return e.stacktrace[1].function === "module";
}
return false;
}