This document describes the comprehensive dynamic library architecture implemented for O²L, enabling native C++ libraries to be loaded and used from O²L code.
The dynamic library system consists of several key components:
NativeLibrary- Abstract base class that all native libraries must inherit fromDynamicLibraryManager- Manages loading, unloading, and lifecycle of native librariesNativeMethodRegistry- Registry for mapping method names to native function implementations- Enhanced
@externalsyntax - Support for native method bindings
class NativeLibrary {
public:
virtual std::string getName() const = 0;
virtual std::string getVersion() const = 0;
virtual void initialize(Context& context) = 0;
virtual void registerMethods(ObjectInstance* obj) = 0;
virtual void cleanup() = 0;
virtual std::map<std::string, std::string> getMetadata() const;
};Every native library must export these C-style functions:
extern "C" {
NativeLibrary* create_library();
void destroy_library(NativeLibrary* lib);
const char* get_abi_version(); // Optional
}// MathLibrary.cpp
#include <o2l/NativeLibrary.hpp>
class MathLibrary : public o2l::NativeLibrary {
public:
std::string getName() const override { return "Math"; }
std::string getVersion() const override { return "1.0.0"; }
void initialize(Context& context) override {
// One-time initialization
}
void registerMethods(ObjectInstance* obj) override {
// Register sqrt method
obj->addMethod("sqrt", [](const std::vector<Value>& args, Context& ctx) -> Value {
if (args.size() != 1) throw EvaluationError("sqrt() expects 1 argument");
double val = std::get<Double>(args[0]);
return Double(std::sqrt(val));
}, true); // true = external
// Register pow method
obj->addMethod("pow", [](const std::vector<Value>& args, Context& ctx) -> Value {
if (args.size() != 2) throw EvaluationError("pow() expects 2 arguments");
double base = std::get<Double>(args[0]);
double exp = std::get<Double>(args[1]);
return Double(std::pow(base, exp));
}, true);
}
void cleanup() override {
// Cleanup resources
}
};
extern "C" {
o2l::NativeLibrary* create_library() {
return new MathLibrary();
}
void destroy_library(o2l::NativeLibrary* lib) {
delete lib;
}
}# Load native library (implicit)
import native.math
Object Main {
method main(): Text {
# Create native object instance
math: Math = new Math()
# Call native methods
result: Double = math.sqrt(16.0)
power: Double = math.pow(2.0, 3.0)
io.print("sqrt(16) = %f", result) # 4.0
io.print("pow(2,3) = %f", power) # 8.0
"Native library demo complete"
}
}
Object MathWrapper {
# Direct native binding
@external(native="libmath.so", function="o2l_sqrt")
method sqrt(x: Double): Double
# Pure O²L external method
@external
method double(x: Double): Double {
x * 2.0
}
}
# Add native library
o2l-pkg add-native math-native 1.0.0
# Create native library template
o2l-pkg create-native com.mycompany.math
# Build native library
o2l-pkg build-native com.mycompany.math --releaseproject/
├── .o2l/
│ └── lib/
│ └── native/ # Native libraries
│ ├── libmath.so # Math library
│ ├── libcollections.so
│ └── libnetwork.so
├── src/
│ └── main.obq
└── o2l.toml
- Linux:
.so(shared object) - macOS:
.dylib(dynamic library) - Windows:
.dll(dynamic link library)
- Unix-like:
libname.ext(e.g.,libmath.so) - Windows:
name.dll(e.g.,math.dll)
.o2l/lib/native/(project-local)/usr/local/lib/o2l/(system-wide Unix)$PROGRAMFILES/O2L/lib/(system-wide Windows)
- Missing library file
- Invalid library format
- Missing entry points (
create_library,destroy_library) - ABI version mismatch
- Initialization failures
- Method not found
- Invalid argument types/count
- Native function exceptions
- Library cleanup failures
Libraries can export get_abi_version() for compatibility:
extern "C" const char* get_abi_version() {
return "1.0.0"; // Must match O²L runtime ABI
}Major ABI changes will increment the version, requiring library recompilation.
- Native libraries run with full system privileges
- Consider runtime permission system for sensitive operations
- Validate all inputs from O²L code
- Proper exception handling in native code
- Avoid raw pointers in public interfaces
- RAII principles for resource management
- Libraries loaded on first use (lazy loading)
- Cached for subsequent object instantiations
- Unloaded on program exit or explicit cleanup
- Direct function pointer calls (minimal overhead)
- Value conversion between O²L and native types
- Exception translation layer
# Template for native libraries
add_library(math SHARED MathLibrary.cpp)
target_link_libraries(math o2l_runtime)
set_target_properties(math PROPERTIES PREFIX "lib")- GDB/LLDB support for mixed O²L/native debugging
- Stack trace integration across language boundaries
- Memory profiling for native components
- Hot Reloading - Update libraries without restarting
- Dependency Management - Native library dependencies
- Code Generation - Auto-generate bindings from headers
- WebAssembly Support - Compile native libraries to WASM
- Reflection API - Runtime inspection of native methods
- IDE tooling for native library development
- Package registry for sharing native libraries
- Cross-compilation support for multiple platforms
- Automated testing frameworks for native code
The core architecture is implemented and ready for:
- Native library development
- Integration with o2l-pkg
- Testing with example libraries
- Production use cases
The dynamic library system provides a robust foundation for extending O²L with high-performance native code while maintaining the language's object-oriented philosophy and safety guarantees.