# Extended operating mechanism ## Function The extension mechanism separates the API that the script can see and how the extension is executed internally. The script only calls the extension through the entries, objects and methods declared in `bindings.json`; the BT host is responsible for loading the `.bts` package, verifying the description file, establishing the registry, and distributing the calls to the corresponding Runner. ## Syntax The extension entry call syntax is the same as that of ordinary functions: ```bt object = calc(1) value = object.add(2).value() // Output: 3 print value ``` `calc` comes from the public entry declaration of `bindings.json`, `add` and `value` come from the object method declaration. ## Parameters extension call are determined by bindings. The host will process the value passed in by the script according to the number, type and role of bindings parameters, and then hand the call to the corresponding Runner. ## Return Value The extension call can return the original value or the extended object handle. The object handle only represents "an object in an extension module", and the real state is saved by the extension backend. The script can use `type(object)` to view the object type name of the extended object declared in `bindings.json`: ```bt object = calc(1) // Output: Calc print type(object) ``` ## Loading process When the project starts, BT will process the extension according to the following process: 1. Find `extensions/` in the project root directory. 2. Read the `.bts` files in the directory in file name order. 3. Verify the `.bts` suffix, zip entry path, number of entries, single file size and total decompression size. 4. Read and verify `manifest.json`. 5. Read and verify `bindings.json`. 6. Initialize a pure BT Runner, thread-local WASM Runner, or project-level ExtensionService based on `manifest.kind` and `manifest.runtime.mode`. 7. Create an extended registry and check whether the public entry names conflict. 8. Inject the public entry into the user global environment of the current VM. When loading fails, the project startup will fail and a Chinese error will be output; BT will not load a semi-successful expansion pack. ## Calling process When the script calls the extension entry: The host will find the function declaration in bindings according to the entry name `calc`, check the number and type of parameters, and then hand the call to the Runner or shared service to which the extension belongs. If the return value is a normal value, it is returned directly to the script; if it is an extended object, the script gets an object handle managed by the host. When a script calls an object method, the host will first find the extension module and object type based on the object handle, and then find the method name. When the WASM method is called, the receiver object handle will be passed to WASM as the first parameter; when the pure BT method is called, the host will call the method on the corresponding object in the internal VM. The WASM extension of `runtime.mode=shared` will first encode the parameters into BtValueBinary bytes, and then deliver them to the bounded worker queue of the project-level ExtensionService. The worker thread only receives the call ID, return type, call label, parameter bytes and reply channel. After the result bytes are returned, they are decoded by the calling thread to avoid passing the VM internal `Value` across threads. When the shared extension returns an object, the worker returns the local object ID. The host will register it as `host_object_id` visible to the script and record `host_object_id -> worker_id + local_object_id + type_id`. Subsequent object method calls will first check this table, change the recipient back to the worker's local object ID, and deliver it to the worker who created the object. After the `lifecycle=dispose` method returns successfully, the host will remove this route. shared worker uses Wasmtime epoch interrupt to handle `call_timeout_ms`. When calling a timeout, the calling thread will mark the target worker and advance the epoch of the module Engine; only the marked worker will return a timeout trap in the epoch callback. The worker then cleans up the timeout mark, discards the old Store/Instance and rebuilds the running state, and other workers continue to execute. If the WASM module exports `bts_init`, `bts_shutdown`, or `bts_stats`, the Runner calls them during worker initialization, normal exit, or the statistics read path; older extensions that do not export these functions remain compatible. ## kind and abi `kind` are backend types, which determine who executes the entry file. `abi` is the calling protocol version that determines how the host and backend exchange parameters, return values, and object handles. | kind | abi | Runner | | ------ | ------ | ------ | | `bt` | `bts-bt-1` | Pure BT Runner, reusing BT Parser, Compiler and VM. | | `wasm` | `bts-wasi-1` | Thread-local WASM Runner or shared ExtensionService, using WASI P1 and BtValueBinary. | These two fields cannot be combined arbitrarily. `kind=bt` must use `bts-bt-1`, and `kind=wasm` must use `bts-wasi-1`. ## Code Examples The same `bindings.json` style can correspond to different backends. The script-side calls remain consistent: ```bt value = calc(4).add(6).value() // Output: 10 print value ``` The difference is only within the extension package: the pure BT extension is implemented by `src/lib.bt`; the WASM extension is implemented by `module.wasm`. ## Notes - Public entries will become global constants and cannot be reassigned by scripts. - `env('calc')` can read the extension entry, `has_env('calc')` returns `true`. - `envs('calc')` and `has_envs('calc')` only represent system functions and system constants, and do not regard extension entries as system capabilities. - The extension entry name cannot conflict with the BT system environment name, nor can it be repeated with other extension entries. - `type()` returns the script-visible type name of the extension object, such as `Calc`; the actual method routing is still done by the host based on the module ID, type ID, and object ID. - Extended objects should not cross boundaries that do not support object handles, such as `task()` snapshots. - shared WASM uses the host-level object handle table to route object methods; old or released handles return an expired error. - shared WASM timeout interrupt only guarantees that workers will not permanently occupy service capacity; if WASM is executing a long-blocking hostcall, subsequent host import itself still needs to provide cancellation boundaries.