Expand description
Plugin architecture and super-global scope.
This module implements the super-global scope - a key architectural feature that provides lazy, dynamic resolution of built-in and plugin-provided objects.
§Super-Global Scope Concept
The super-global scope sits outside the normal lexical environment chain and provides objects that are available globally but resolved on-demand:
Variable Lookup Order:
1. Local scope (function/block)
2. Outer scopes (lexical chain)
3. Global scope
4. Super-global scope ← Built-ins and plugins live here§Key Components
PluginResolver: Trait for providing objects and methods dynamicallySuperGlobalEnvironment: Container holding multiple resolvers with cachingCorePluginResolver: Adapter wrappingBuiltInRegistryas a resolverEvalContext: Execution context with super-global integration
§Resolution Flow
When JavaScript code references a name (e.g., Math):
- Check cache: Has this name been resolved before?
- Query resolvers: Ask each resolver in registration order
- Cache result: Store the resolved value for future lookups
- Return value: Provide the object to JavaScript code
§Method Calls
Method calls (e.g., Math.abs(-5)) are handled specially:
- Direct dispatch: Resolvers can handle method calls directly without materializing the object
- Fallback: If no resolver handles it, fall back to property lookup
This allows efficient built-in method calls without creating intermediate objects.
§Example: Custom Plugin
use just::runner::plugin::resolver::PluginResolver;
use just::runner::plugin::types::EvalContext;
use just::runner::ds::value::{JsValue, JsNumberType};
use just::runner::ds::error::JErrorType;
struct UtilsPlugin;
impl PluginResolver for UtilsPlugin {
fn has_binding(&self, name: &str) -> bool {
name == "Utils"
}
fn resolve(&self, _name: &str, _ctx: &mut EvalContext) -> Result<JsValue, JErrorType> {
Ok(JsValue::Undefined) // Sentinel
}
fn call_method(&self, obj: &str, method: &str, _ctx: &mut EvalContext,
_this: JsValue, args: Vec<JsValue>) -> Option<Result<JsValue, JErrorType>> {
if obj == "Utils" && method == "double" {
let n = match args.first() {
Some(JsValue::Number(JsNumberType::Integer(n))) => *n,
_ => 0,
};
Some(Ok(JsValue::Number(JsNumberType::Integer(n * 2))))
} else {
None
}
}
fn name(&self) -> &str { "utils_plugin" }
}
// Register the plugin
let mut ctx = EvalContext::new();
ctx.add_resolver(Box::new(UtilsPlugin));
// Now Utils.double(21) returns 42§Design Rationale
§Why Not Preload Everything?
Traditional approach:
- Load all built-ins at startup
- High memory usage
- Slow startup time
- Hard to extend
Super-global approach:
- Load on first use (lazy)
- Lower memory footprint
- Fast startup
- Easy to add plugins
§Why Not Use Global Scope?
Keeping built-ins in a separate super-global scope:
- Prevents accidental mutation from JavaScript
- Allows local variables to shadow built-ins
- Maintains clean separation of concerns
- Enables efficient caching and dispatch
Re-exports§
pub use types::BuiltInFn;pub use types::BuiltInObject;pub use types::NativeFn;pub use types::PluginInfo;pub use registry::BuiltInRegistry;pub use config::PluginConfig;pub use resolver::PluginResolver;pub use core_resolver::CorePluginResolver;pub use super_global::SuperGlobalEnvironment;
Modules§
- config
- Plugin configuration file parsing.
- core_
resolver - Core plugin resolver — wraps the existing
BuiltInRegistryas aPluginResolver. - registry
- Built-in registry for managing built-in objects and plugins.
- resolver
- Plugin resolver trait for lazy, dynamic resolution of super-global objects.
- super_
global - Super-global environment — the bottom of the scope chain.
- types
- Core types for the plugin architecture.