Skip to main content

just_engine/runner/plugin/
core_resolver.rs

1//! Core plugin resolver — wraps the existing `BuiltInRegistry` as a `PluginResolver`.
2//!
3//! This makes all built-in objects (Math, console, String, etc.) available
4//! through the super-global scope's lazy resolution mechanism.
5
6use crate::runner::ds::error::JErrorType;
7use crate::runner::ds::value::JsValue;
8use crate::runner::plugin::registry::BuiltInRegistry;
9use crate::runner::plugin::resolver::PluginResolver;
10use crate::runner::plugin::types::EvalContext;
11
12/// Wraps a `BuiltInRegistry` as a `PluginResolver`.
13///
14/// When the super-global scope queries for a name like `"Math"`,
15/// this resolver checks the registry and materializes a proxy object
16/// whose method calls are dispatched back to the registry.
17pub struct CorePluginResolver {
18    registry: BuiltInRegistry,
19}
20
21impl CorePluginResolver {
22    pub fn new(registry: BuiltInRegistry) -> Self {
23        CorePluginResolver { registry }
24    }
25
26    pub fn registry(&self) -> &BuiltInRegistry {
27        &self.registry
28    }
29}
30
31impl PluginResolver for CorePluginResolver {
32    fn has_binding(&self, name: &str) -> bool {
33        self.registry.has_object(name)
34    }
35
36    fn resolve(&self, name: &str, _ctx: &mut EvalContext) -> Result<JsValue, JErrorType> {
37        if self.registry.has_object(name) {
38            // Return a sentinel object that identifies this built-in.
39            // The actual method dispatch happens via `call_method`.
40            use crate::runner::ds::object::{ObjectType};
41            use crate::runner::ds::object_property::{
42                PropertyDescriptor, PropertyDescriptorData, PropertyKey,
43            };
44            use crate::runner::ds::object::JsObject;
45            use crate::runner::plugin::types::SimpleObject;
46            use std::cell::RefCell;
47            use std::rc::Rc;
48
49            let mut obj = SimpleObject::new();
50
51            // Tag the object with its built-in name so CallMethod can identify it
52            obj.get_object_base_mut().properties.insert(
53                PropertyKey::Str("__builtin_name__".to_string()),
54                PropertyDescriptor::Data(PropertyDescriptorData {
55                    value: JsValue::String(name.to_string()),
56                    writable: false,
57                    enumerable: false,
58                    configurable: false,
59                }),
60            );
61
62            // Also add any static properties from the registry
63            if let Some(builtin_obj) = self.registry.get_object(name) {
64                for (prop_name, prop_value) in &builtin_obj.properties {
65                    obj.get_object_base_mut().properties.insert(
66                        PropertyKey::Str(prop_name.clone()),
67                        PropertyDescriptor::Data(PropertyDescriptorData {
68                            value: prop_value.clone(),
69                            writable: false,
70                            enumerable: true,
71                            configurable: false,
72                        }),
73                    );
74                }
75            }
76
77            Ok(JsValue::Object(Rc::new(RefCell::new(
78                ObjectType::Ordinary(Box::new(obj)),
79            ))))
80        } else {
81            Err(JErrorType::ReferenceError(format!(
82                "{} is not defined",
83                name
84            )))
85        }
86    }
87
88    fn call_method(
89        &self,
90        object_name: &str,
91        method_name: &str,
92        ctx: &mut EvalContext,
93        this: JsValue,
94        args: Vec<JsValue>,
95    ) -> Option<Result<JsValue, JErrorType>> {
96        self.registry
97            .get_method(object_name, method_name)
98            .map(|builtin_fn| builtin_fn.call(ctx, this, args))
99    }
100
101    fn call_constructor(
102        &self,
103        object_name: &str,
104        ctx: &mut EvalContext,
105        args: Vec<JsValue>,
106    ) -> Option<Result<JsValue, JErrorType>> {
107        self.registry
108            .get_constructor(object_name)
109            .map(|ctor_fn| ctor_fn.call(ctx, JsValue::Undefined, args))
110    }
111
112    fn name(&self) -> &str {
113        "core"
114    }
115}