.. _aot: .. _embedding_advanced: .. index:: single: Embedding; AOT single: Embedding; Advanced Topics single: Embedding; Class Adapters single: Embedding; Coroutines single: Embedding; Standalone Contexts ========================================= Advanced Topics ========================================= This page covers advanced embedding techniques: ahead-of-time compilation, class adapters, coroutines, dynamic script generation, and standalone contexts. .. contents:: :local: :depth: 2 AOT compilation =============== daslang can compile scripts ahead-of-time into C++ source files that, when built alongside the host, replace the interpreter with direct native calls. This provides significant performance improvements for hot paths. AOT pipeline overview --------------------- 1. **Compile** the ``.das`` file as normal. 2. **Run** the AOT tool: ``daslang -aot script.das output.cpp`` 3. **Build** the generated ``.cpp`` alongside your application and link ``libDaScript``. 4. At runtime, ``simulate()`` detects AOT-compiled functions and uses them instead of the interpreter. See :ref:`tutorial_integration_cpp_aot` for a step-by-step walkthrough. AOT annotations --------------- ``[no_aot]`` Prevents AOT compilation for a specific function. ``[hybrid]`` AOT function uses full ABI — slower calls, but no semantic hash dependency. Required when the module may be compiled separately from the script. AOT template functions ---------------------- By default, AOT-generated functions expect blocks to be passed as the C++ ``TBlock`` class (see :ref:`blocks`). If the block is small enough, AOT can replace it with a direct call. ``setAotTemplate`` enables this: .. code-block:: cpp addExtern(*this, lib, "my_func", SideEffects::invoke, "my_func") ->setAotTemplate(); This generates inlined block evaluation instead of ``das_invoke``. AOT type customization ----------------------- Handled types can customize their AOT generation through ``TypeAnnotation`` virtual methods: ``aotPreVisitGetField(writer, fieldName)`` Emits code *before* field access (e.g., dereference operator). ``aotVisitGetField(writer, fieldName)`` Emits the field access itself. ``aotPreVisitGetFieldPtr(writer, fieldName)`` Same as above, for pointer field access. ``aotVisitGetFieldPtr(writer, fieldName)`` Emits pointer field access. **Index operations** are covered by ``das_index`` and ``das_iterator`` C++ templates. Override these templates to provide AOT-compatible indexing and iteration for custom types. ``das_index`` Used for ``ExprAt`` (``array[index]``) and ``ExprSafeAt`` (``array?[index]``). Must provide ``at(value, index, context)`` and ``at(value, index, count, context)`` methods. ``das_iterator`` Used for ``for`` loop iteration. Must provide ``main_loop`` and associated helper methods. Class adapters ============== Class adapters bridge C++ virtual method dispatch to daslang class method implementations. This allows daslang scripts to define classes that a C++ host can polymorphically call through base class pointers. Pattern overview ---------------- 1. Define a C++ abstract base class with virtual methods. 2. Generate a "tutorial adapter" class that maps each virtual method to a ``das_invoke_function`` call. 3. Create a dual-inheritance adapter: ``struct Adapter : BaseClass, TutorialAdapter``. 4. In the daslang script, derive a class from the adapter type and override methods. 5. The C++ host calls virtual methods on base class pointers — dispatch reaches daslang automatically. Key C++ APIs: * ``das_invoke_function::invoke(ctx, at, fn, args...)`` — calls a daslang function pointer from C++ * ``getDasClassMethod(classPtr, offset)`` — retrieves the daslang function pointer for a virtual method * ``compileBuiltinModule`` — compiles an embedded ``.das`` module as a built-in * ``class_info(*classPtr)`` — gets ``StructInfo`` for RTTI bridging The adapter class is typically generated by ``log_cpp_class_adapter`` from ``daslib/cpp_bind``, which produces the boilerplate ``get_`` / ``invoke_`` static helpers. See :ref:`tutorial_integration_cpp_class_adapters` for a complete, compilable example. Coroutines (generators) ======================= daslang generators (``generator``) can be consumed from C++ through the ``Sequence`` type. This enables coroutine-style patterns where the host drives execution step by step. Consuming a generator --------------------- 1. Call the generator-returning function with ``evalWithCatch``, passing a ``Sequence *`` as the third argument: .. code-block:: cpp Sequence it; ctx.evalWithCatch(fnTest, nullptr, &it); 2. Step through values with ``builtin_iterator_iterate``: .. code-block:: cpp int32_t value = 0; while (builtin_iterator_iterate(it, &value, &ctx)) { // process value } 3. Clean up with ``builtin_iterator_close``: .. code-block:: cpp builtin_iterator_close(it, &value, &ctx); Even if the generator has finished, always call ``close`` to release resources. If you break early, ``close`` is essential. See :ref:`tutorial_integration_cpp_coroutines`. Dynamic script generation ========================== Scripts can be constructed as strings and compiled without writing to disk, using virtual files: .. code-block:: cpp TextWriter ss; ss << "options gen2\n"; ss << "var x = 0.0f\n"; ss << "var y = 0.0f\n"; ss << "[export] def eval() : float { return x*x + y*y; }\n"; auto fAccess = make_smart(); auto fileInfo = make_unique( ss.str().c_str(), uint32_t(ss.str().length()), false); fAccess->setFileInfo("virtual.das", das::move(fileInfo)); auto program = compileDaScript("virtual.das", fAccess, tout, dummyLibGroup); After simulation, global variables can be located and written to directly through context memory: .. code-block:: cpp int idx = ctx->findVariable("x"); float * xPtr = (float *)ctx->getVariable(idx); *xPtr = 3.0f; // set x = 3.0 This pattern is useful for expression evaluators, configuration systems, and dynamic code generation. See :ref:`tutorial_integration_cpp_dynamic_scripts`. Context variables ----------------- ``ctx.findVariable(name)`` Returns the index of a global variable, or -1 if not found. ``ctx.getVariable(index)`` Returns a raw pointer into the context's global data segment. ``ctx.getTotalVariables()`` Returns the number of global variables. ``ctx.getVariableName(index)`` Returns the variable's name. ``ctx.getVariableSize(index)`` Returns the variable's size in bytes. See :ref:`tutorial_integration_cpp_context_variables`. Standalone contexts =================== A standalone context pre-compiles a daslang program into C++ source files (``.das.h`` and ``.das.cpp``) that embed the entire program state — function tables, variable layouts, and AOT implementations — without requiring runtime compilation. Benefits: * **Zero compilation overhead** at runtime — no parsing, no type inference, no simulation. * **Direct native calls** — exported functions become C++ methods on a ``Standalone`` class. * **Deterministic startup** — all code is linked at build time. Pipeline: 1. Compile-time: ``daslang utils/aot/main.das -- -ctx script.das output_dir/`` 2. This generates ``script.das.h`` and ``script.das.cpp``. 3. Build both alongside your host application. 4. At runtime, create the ``Standalone`` context and call functions directly. .. code-block:: cpp #include "standalone_ctx_generated/script.das.h" das::standalone::Standalone ctx; ctx.test(); // direct call, no findFunction needed See :ref:`tutorial_integration_cpp_standalone_contexts` for a complete example. Serialization ============= Compiled programs can be serialized to a binary blob, skipping compilation entirely on subsequent loads: .. code-block:: cpp // Save AstSerializer ser; program->serialize(ser); // ser.buffer contains the binary data // Load AstSerializer deser; deser.buffer = saved_data; auto restored = Program::deserialize(deser); // simulate and run as normal This is faster than recompilation (skips parsing and type inference) but slower than standalone contexts (still requires simulation). See :ref:`tutorial_integration_cpp_serialization` and :ref:`tutorial_integration_c_serialization`. Sandboxing ========== ``CodeOfPolicies`` controls what scripts are allowed to do: * ``no_unsafe = true`` — forbid ``unsafe`` blocks * ``no_global_variables = true`` — forbid module-level variables * ``no_global_heap = true`` — forbid heap allocations for globals * ``no_init = true`` — forbid ``[init]`` functions * ``threadlock_context = true`` — enable context mutex ``FsFileAccess`` can be locked to restrict file access: 1. Pre-introduce allowed files with ``das_fileaccess_introduce_file``. 2. Lock with ``das_fileaccess_lock`` to prevent filesystem reads. See :ref:`tutorial_integration_cpp_sandbox` and :ref:`tutorial_integration_c_sandbox`. File access customization ========================= Override ``FsFileAccess`` methods for custom file resolution: ``getNewFileInfo(fname)`` Resolves file paths and returns file content. ``getModuleInfo(req, from)`` Resolves ``require`` statements to file paths. ``setFileInfo(name, info)`` Registers a virtual file from a string — used for dynamic script generation and embedded modules. For project-level file resolution, implement ``module_get`` as a function macro (.das-side): .. code-block:: das options gen2 require daslib/ast_boost public [function_macro(name="module_get")] class ModuleGetMacro : AstModuleGetMacro { def override getModule(req, from : string) : ModuleInfo { // resolve require paths here } } See :ref:`tutorial_integration_cpp_custom_modules`.