Skip to main content

Module plugin

Module plugin 

Source
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

§Resolution Flow

When JavaScript code references a name (e.g., Math):

  1. Check cache: Has this name been resolved before?
  2. Query resolvers: Ask each resolver in registration order
  3. Cache result: Store the resolved value for future lookups
  4. Return value: Provide the object to JavaScript code

§Method Calls

Method calls (e.g., Math.abs(-5)) are handled specially:

  1. Direct dispatch: Resolvers can handle method calls directly without materializing the object
  2. 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 BuiltInRegistry as a PluginResolver.
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.