Native plugins › 07 Advanced metadata

Advanced metadata

Declare interfaces and slots, inspect libraries, and load plugins at run time.

1. Interfaces

A native class may implement language interfaces. The metadata lists their dotted fully-qualified names, and the declaration binds each member:

%native com.example.counter.Task
    String name()
    void run()
static const char* const task_ifaces[] = { "com.example.Runnable", NULL };

static const NativeClassMeta task_meta = {
    "com.example.counter.Task", 0, task_ifaces, NULL,
    NULL, 0, task_methods, 2, NULL, NULL,
};

Binding checks that every interface member declared in .loth is provided, and that the descriptors agree. A native object can then be passed anywhere the interface is expected, including new Thread(task).

2. Generic slots

%native com.example.EqSet<?a>
    void put(?a v)
    bool contains(?a v)
static const char* const eqset_slots[] = { "a", NULL };

static const NativeClassMeta eqset_meta = {
    "com.example.EqSet", 0, NULL, eqset_slots,
    NULL, 0, eqset_methods, 2, eqset_finalize, eqset_scan,
};

3. Class tokens

A &T value is a class token: the class itself, not an instance. Native code can recover the class from a token:

ClassMeta* vm_token_class(Value token);   /* NULL if not a token */

Tokens are ordinary objects; store them in fields and instantiate later with vm_new_instance(vm, cls) plus a constructor call.

4. Inspecting a library

$ loth native-info lib/Thread.so
class: Thread (native)
  method: ___init(LRunnable;)V
  method: start()V
  method: join()V
  method: id()I
  finalize=yes gc_scan=yes
class: ThreadUtil (singleton)
  ...
class: Lock (native)
  ...
class: Cond (native)
  ...

native-info opens the library and prints every registered class with kind, fields, methods and slots — useful when a binding is rejected and you want to see what the library actually declares.

5. Multi-class libraries

One library can register many classes, and binding a single one opens the library and binds every class it declares that is already loaded and unbound. This is how lib/Thread.so provides Thread, ThreadUtil, Lock and Cond, and how the example provides Counter and CounterUtil.

When the file name does not match the class name, the loader scans the library directory for a registration. Naming the file after the class is still the clearest choice.

6. Dynamic loading

int vm_load_module(VM* vm, const char* lothc_path);     /* 0 on success */
int vm_bind_native_file(VM* vm, const char* so_path);   /* 0 on success */

The order is: load the .lothc module first, then bind its library. Both are atomic — any duplicate class, missing dependency or metadata mismatch rejects the whole operation and leaves the VM unchanged.

Load before you start threads.

The module table is not synchronized against concurrent readers, so treat dynamic loading as a startup activity. Everything else — running code, creating objects, calling methods — is thread-safe in the ways the other tracks describe.