Before reading this part of the documentation, it is recommended to have already read the C FFI Documentation or to have a good knowledge of it.
The Neko Virtual Machine is one binary called neko
included in the Neko distribution. You can call it anytime using neko (file)
in order to execute the specified bytecode file.
Bytecode files are precompiled Neko sources and have the .n
extension. They are searched in local directories, but also using the NEKOPATH
environment variable which can list several search paths separated by :
. Each bytecode .n
file is called a Module.
Each .ndll
file is a Neko library. It's just a shared library (.so
or .dll
) linked with the Neko Library (libneko.so
or neko.lib
). Each Neko Library can contain several primitives that can then be used from a Neko program. Neko libraries are also searched the same way as Modules, using NEKOPATH
.
Each Neko module has a global object named $exports
. The module can set fields in this object in order to export values that will be usable from other modules :
$exports.log = function() { $print("log test") };
Each Neko module has a loader, which is an object that can be used to load other Neko modules and C primitives. The loader is accessible using the $loader
builtin.
In order to load a Module, you can simply call the loadmodule
method, which takes two parameters. The first parameter is the name of the module and the second parameter is the loader that this module will use. If found, the module is loaded, executed, and then its $exports
table is returned. An exception is thrown if not found.
var m = $loader.loadmodule("log",$loader);
m.log();
You can also load C primitives using the loader. See the C FFI API for help on how to write such primitives. A primitive is loaded using the loadprim
method, using the name of the library and the name of the primitive separated by an at symbol (@), followed by the number of arguments. If succeeded, a Neko function is returned that is used to call the primitive, otherwise an exception is thrown.
var p = $loader.loadprim("std@test",0);
p();
It's possible to define your custom loader that will filter or secure the modules and primitives loaded. The only thing needed is to implement the two methods loadmodule
and loadprim
and use your loader as the second parameter of the loadmodule
method when loading another module.
The Neko Virtual Machine and its C FFI are packaged into a single shared library (libneko.so
on Unix systems and neko.dll
on Windows). With the garbage collector library (libgc
on Unix and gc.dll
on Windows), this is all you need to add to your application in order to be able to run a Neko Program.
Here's a small code snippet that initializes a NekoVM, runs a Neko module inside of it, then accesses some data :
#include <stdio.h>
#include <neko_vm.h>
value load( char *file ) {
value loader;
value args[2];
value exc = NULL;
value ret;
loader = neko_default_loader(NULL,0);
args[0] = alloc_string(file);
args[1] = loader;
ret = val_callEx(loader,val_field(loader,val_id("loadmodule")),args,2,&exc);
if( exc != NULL ) {
buffer b = alloc_buffer(NULL);
val_buffer(b,exc);
printf("Uncaught exception - %s\n",val_string(buffer_to_string(b)));
return NULL;
}
return ret;
}
void execute( value module ) {
value x = val_field(module,val_id("x"));
value f = val_field(module,val_id("f"));
value ret;
if( !val_is_int(x) )
return;
printf("x = %d\n",val_int(x));
if( !val_is_function(f) || val_fun_nargs(f) != 1 )
return;
ret = val_call1(f,x);
if( !val_is_int(ret) )
return;
printf("f(x) = %d\n",val_int(ret));
}
int main( int argc, char *argv[] ) {
neko_vm *vm;
value module;
neko_global_init(NULL);
vm = neko_vm_alloc(NULL);
neko_vm_select(vm);
module = load("mymodule.n");
if( module == NULL ) {
printf("Failed to load module !\n");
return -1;
}
execute(module);
neko_global_free();
return 0;
}
You can use it to load the following mymodule.neko
file after compilation :
$exports.x = 33;
$exports.f = function(x) { return x * 2 + 1; }
The NekoVM API is declared in the neko_vm.h
file. It will be fully documented later. There is also the neko_mod.h
file for low-level module access.
Each thread can run several NekoVMs, however, the same VM should not run at the same time in different threads. When changing virtual machines or after allocating one, you must call the neko_vm_select
API function that will select the given virtual machine for the current thread.