Embedding › 04 Running code
Start entry classes, call individual methods, pass arguments, and read results and exit codes.
vm_runClassMeta* entry = loth_engine_entry(e, "com.example.App");
if (!entry) { /* not found, or does not implement Main */ }
int rc = vm_run(vm, entry, argc, argv);
loth_engine_entry looks up a class by fully-qualified name and
verifies that it implements _LANG.Main; pass
NULL to take the first entry it finds.vm_run constructs the entry singleton and calls
run(argc, argv).run is returned; an uncaught exception or
a panic produces a non-zero value and a message on stderr.vm_callNot every component has a Main. To call a method on a class,
obtain the singleton (or a new instance) and call it:
Value svc = vm_get_singleton(vm, "com.example.Service");
Value out = NIL_VAL;
int r = vm_call(vm, svc, "handle", args, argc, NULL, NULL, &out);
if (r == 1) {
/* the method threw; the exception object is in out */
} else if (r == -1) {
/* no such method, or the receiver is not an object */
}
vm_get_singleton constructs the singleton on first use and
returns the same object afterwards.NULL for arg_types lets the VM fill in
the callee's parameter types; pass explicit type descriptors when calling
variadic or overloaded native methods.0 success, 1 exception
(out holds it), -1 method or receiver problem.out is not rooted after the call returns. If you keep it
across further allocations, root it yourself (chapter 5).The host decides what to pass. The example passes none. A host that forwards its own arguments can do this:
int rc = vm_run(vm, entry, argc - 1, argv + 1);
return rc; // the language program controls the exit code
By convention run returns 0 for success. The CLI treats a usage
error as exit code 2 and an uncaught exception as 1, and hosts are encouraged to
follow the same convention.
Std.print writes through a process-wide channel. By default it
goes to stdout under a lock, so a single print call never interleaves with
another. A host can take over per VM:
static void collect(void* user, const char* data, size_t len) {
// append data[0..len) to your own buffer
}
vm->out_fn = collect;
vm->out_user = my_buffer;
The callback may be invoked from any thread, so it must be thread-safe. This is how the multi-VM example keeps each VM's output separate.
Values cross the boundary as Value, an untagged union. The host
must know the type: a method that returns int puts it in
out.i; a reference in out.o. For strings and objects,
the same allocation, string and root APIs the native track describes are
available to hosts.