Skip to main content

just_engine/runner/plugin/
types.rs

1//! Core types for the plugin architecture.
2
3use crate::runner::ds::env_record::{
4    new_declarative_environment, new_global_environment, EnvironmentRecord, EnvironmentRecordType,
5};
6use crate::runner::ds::error::JErrorType;
7use crate::runner::ds::execution_context::ExecutionContextStack;
8use crate::runner::ds::heap::{Heap, HeapConfig};
9use crate::runner::ds::lex_env::JsLexEnvironmentType;
10use crate::runner::ds::object::{JsObject, JsObjectType, ObjectBase, ObjectType};
11use crate::runner::ds::value::JsValue;
12use crate::parser::ast::FunctionData;
13use super::super_global::SuperGlobalEnvironment;
14use std::cell::RefCell;
15use std::collections::HashMap;
16use std::rc::Rc;
17
18/// Shared heap type for use across contexts.
19pub type SharedHeap = Rc<RefCell<Heap>>;
20
21/// Shared super-global type for use across contexts.
22pub type SharedSuperGlobal = Rc<RefCell<SuperGlobalEnvironment>>;
23
24/// Execution context for JavaScript evaluation.
25///
26/// Contains the lexical environment chain, heap, and super-global scope.
27/// This is the main context object passed through the interpreter and VMs.
28///
29/// # Examples
30///
31/// ```
32/// use just::parser::JsParser;
33/// use just::runner::plugin::types::EvalContext;
34/// use just::runner::plugin::registry::BuiltInRegistry;
35/// use just::runner::eval::statement::execute_statement;
36///
37/// // Create context with built-ins
38/// let mut ctx = EvalContext::new();
39/// ctx.install_core_builtins(BuiltInRegistry::with_core());
40///
41/// // Parse and execute code
42/// let code = "var x = Math.abs(-42);";
43/// let ast = JsParser::parse_to_ast_from_str(code).unwrap();
44/// for stmt in &ast.body {
45///     execute_statement(stmt, &mut ctx).unwrap();
46/// }
47///
48/// // Access variables
49/// let x = ctx.get_binding("x").unwrap();
50/// ```
51pub struct EvalContext {
52    /// The global `this` value.
53    pub global_this: Option<JsValue>,
54    /// Shared heap for memory allocation tracking.
55    pub heap: SharedHeap,
56    /// Current lexical environment (for let/const and block scoping).
57    pub lex_env: JsLexEnvironmentType,
58    /// Current variable environment (for var declarations).
59    pub var_env: JsLexEnvironmentType,
60    /// Execution context stack for tracking function calls.
61    pub ctx_stack: ExecutionContextStack,
62    /// Whether we're in strict mode.
63    pub strict: bool,
64    /// Lexical environment version for cache invalidation.
65    pub lex_env_version: u64,
66    /// Super-global scope for lazy resolution of built-in and plugin objects.
67    pub super_global: SharedSuperGlobal,
68}
69
70impl EvalContext {
71    /// Create a new evaluation context with default heap configuration.
72    ///
73    /// Creates a fresh context with:
74    /// - Empty super-global scope (call `install_core_builtins` to add built-ins)
75    /// - Default heap with no memory limits
76    /// - Global lexical environment
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// use just::runner::plugin::types::EvalContext;
82    ///
83    /// let mut ctx = EvalContext::new();
84    /// // Context is ready to use, but has no built-ins yet
85    /// ```
86    pub fn new() -> Self {
87        // Create a simple global object
88        let global_obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(
89            SimpleObject::new(),
90        ))));
91        let global_env = new_global_environment(global_obj.clone());
92
93        EvalContext {
94            global_this: Some(JsValue::Object(global_obj)),
95            heap: Rc::new(RefCell::new(Heap::default())),
96            lex_env: global_env.clone(),
97            var_env: global_env,
98            ctx_stack: ExecutionContextStack::new(),
99            strict: false,
100            lex_env_version: 0,
101            super_global: Rc::new(RefCell::new(SuperGlobalEnvironment::new())),
102        }
103    }
104
105    /// Create a new evaluation context with a specific heap configuration.
106    ///
107    /// Use this to set memory limits or custom heap behavior.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use just::runner::plugin::types::EvalContext;
113    /// use just::runner::ds::heap::HeapConfig;
114    ///
115    /// let config = HeapConfig {
116    ///     max_bytes: Some(10 * 1024 * 1024), // 10MB limit
117    ///     gc_threshold: 1024 * 1024,
118    /// };
119    /// let mut ctx = EvalContext::with_heap_config(config);
120    /// ```
121    pub fn with_heap_config(config: HeapConfig) -> Self {
122        let global_obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(
123            SimpleObject::new(),
124        ))));
125        let global_env = new_global_environment(global_obj.clone());
126
127        EvalContext {
128            global_this: Some(JsValue::Object(global_obj)),
129            heap: Rc::new(RefCell::new(Heap::new(config))),
130            lex_env: global_env.clone(),
131            var_env: global_env,
132            ctx_stack: ExecutionContextStack::new(),
133            strict: false,
134            lex_env_version: 0,
135            super_global: Rc::new(RefCell::new(SuperGlobalEnvironment::new())),
136        }
137    }
138
139    /// Current lexical environment version.
140    pub fn current_lex_env_version(&self) -> u64 {
141        self.lex_env_version
142    }
143
144    /// Install a plugin resolver into the super-global scope.
145    ///
146    /// Resolvers are queried in registration order. The first resolver
147    /// that claims a name wins.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use just::runner::plugin::types::EvalContext;
153    /// use just::runner::plugin::resolver::PluginResolver;
154    /// use just::runner::ds::value::JsValue;
155    /// use just::runner::ds::error::JErrorType;
156    ///
157    /// struct MyPlugin;
158    ///
159    /// impl PluginResolver for MyPlugin {
160    ///     fn has_binding(&self, name: &str) -> bool {
161    ///         name == "MyObject"
162    ///     }
163    ///     
164    ///     fn resolve(&self, _name: &str, _ctx: &mut EvalContext) -> Result<JsValue, JErrorType> {
165    ///         Ok(JsValue::Undefined)
166    ///     }
167    ///     
168    ///     fn call_method(&self, _obj: &str, _method: &str, _ctx: &mut EvalContext,
169    ///                    _this: JsValue, _args: Vec<JsValue>) -> Option<Result<JsValue, JErrorType>> {
170    ///         None
171    ///     }
172    ///     
173    ///     fn name(&self) -> &str { "my_plugin" }
174    /// }
175    ///
176    /// let mut ctx = EvalContext::new();
177    /// ctx.add_resolver(Box::new(MyPlugin));
178    /// ```
179    pub fn add_resolver(&mut self, resolver: Box<dyn super::resolver::PluginResolver>) {
180        self.super_global.borrow_mut().add_resolver(resolver);
181    }
182
183    /// Install the core built-in objects (Math, console, JSON, etc.).
184    ///
185    /// This is a convenience method that wraps the `BuiltInRegistry`
186    /// in a `CorePluginResolver` and adds it to the super-global scope.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use just::runner::plugin::types::EvalContext;
192    /// use just::runner::plugin::registry::BuiltInRegistry;
193    ///
194    /// let mut ctx = EvalContext::new();
195    /// ctx.install_core_builtins(BuiltInRegistry::with_core());
196    ///
197    /// // Now Math, console, etc. are available
198    /// ```
199    pub fn install_core_builtins(&mut self, registry: super::registry::BuiltInRegistry) {
200        self.add_resolver(Box::new(super::core_resolver::CorePluginResolver::new(registry)));
201    }
202
203    /// Track a heap allocation.
204    pub fn allocate(&self, bytes: usize) -> Result<(), JErrorType> {
205        self.heap.borrow_mut().allocate(bytes)
206    }
207
208    /// Track a heap deallocation.
209    pub fn deallocate(&self, bytes: usize) {
210        self.heap.borrow_mut().deallocate(bytes)
211    }
212
213    /// Get the current heap usage in bytes.
214    pub fn heap_usage(&self) -> usize {
215        self.heap.borrow().get_allocated()
216    }
217
218    /// Create a tracked SimpleObject that participates in heap accounting.
219    pub fn new_tracked_object(&self) -> Result<SimpleObject, JErrorType> {
220        SimpleObject::new_tracked(self.heap.clone())
221    }
222
223    /// Look up a binding in the environment chain.
224    pub fn get_binding(&mut self, name: &str) -> Result<JsValue, JErrorType> {
225        let name_string = name.to_string();
226        self.resolve_binding(&name_string)
227    }
228
229    /// Look up a binding and return the environment where it was found.
230    pub fn get_binding_with_env(
231        &mut self,
232        name: &str,
233    ) -> Result<(JsValue, JsLexEnvironmentType), JErrorType> {
234        let name_string = name.to_string();
235        let mut current_env = Some(self.lex_env.clone());
236        let mut last_env: Option<JsLexEnvironmentType> = None;
237
238        while let Some(env) = current_env {
239            let env_borrowed = env.borrow();
240            if env_borrowed.inner.as_env_record().has_binding(&name_string) {
241                drop(env_borrowed);
242                let value = env
243                    .borrow()
244                    .inner
245                    .as_env_record()
246                    .get_binding_value(&mut self.ctx_stack, &name_string)?;
247                return Ok((value, env));
248            }
249            last_env = Some(env.clone());
250            current_env = env_borrowed.outer.clone();
251        }
252
253        // Fall back to super-global scope.
254        // Return the global env as the "owning" env for cache purposes.
255        let sg = self.super_global.clone();
256        let result = sg.borrow_mut().resolve_binding(&name_string, self);
257        let value = result?;
258        let env = last_env.unwrap_or_else(|| self.lex_env.clone());
259        Ok((value, env))
260    }
261
262    /// Get a binding from a specific environment (used by inline caches).
263    pub fn get_binding_in_env(
264        &mut self,
265        env: &JsLexEnvironmentType,
266        name: &str,
267    ) -> Result<JsValue, JErrorType> {
268        let name_string = name.to_string();
269        env.borrow()
270            .inner
271            .as_env_record()
272            .get_binding_value(&mut self.ctx_stack, &name_string)
273    }
274
275    /// Resolve a binding by walking up the environment chain,
276    /// falling back to the super-global scope if not found.
277    pub fn resolve_binding(&mut self, name: &String) -> Result<JsValue, JErrorType> {
278        let mut current_env = Some(self.lex_env.clone());
279
280        while let Some(env) = current_env {
281            let env_borrowed = env.borrow();
282            if env_borrowed.inner.as_env_record().has_binding(name) {
283                drop(env_borrowed);
284                return env
285                    .borrow()
286                    .inner
287                    .as_env_record()
288                    .get_binding_value(&mut self.ctx_stack, name);
289            }
290            current_env = env_borrowed.outer.clone();
291        }
292
293        // Fall back to super-global scope
294        let sg = self.super_global.clone();
295        let result = sg.borrow_mut().resolve_binding(name, self);
296        result
297    }
298
299    /// Set a binding in the environment chain.
300    pub fn set_binding(&mut self, name: &str, value: JsValue) -> Result<(), JErrorType> {
301        let name_string = name.to_string();
302        self.resolve_and_set_binding(&name_string, value)
303    }
304
305    /// Set a binding and return the environment where it was set.
306    pub fn set_binding_with_env(
307        &mut self,
308        name: &str,
309        value: JsValue,
310    ) -> Result<JsLexEnvironmentType, JErrorType> {
311        let name_string = name.to_string();
312        let mut current_env = Some(self.lex_env.clone());
313
314        while let Some(env) = current_env.clone() {
315            let has_binding = env.borrow().inner.as_env_record().has_binding(&name_string);
316            if has_binding {
317                self.set_binding_in_env(&env, &name_string, value)?;
318                return Ok(env);
319            }
320            current_env = env.borrow().outer.clone();
321        }
322
323        if !self.strict {
324            let env = self.var_env.clone();
325            self.set_binding_in_env(&env, &name_string, value)?;
326            return Ok(env);
327        }
328
329        Err(JErrorType::ReferenceError(format!("{} is not defined", name)))
330    }
331
332    /// Set a binding in a specific environment (inline cache fast-path).
333    pub fn set_binding_in_env_cached(
334        &mut self,
335        env: &JsLexEnvironmentType,
336        name: &str,
337        value: JsValue,
338    ) -> Result<(), JErrorType> {
339        let name_string = name.to_string();
340        self.set_binding_in_env(env, &name_string, value)
341    }
342
343    /// Resolve and set a binding by walking up the environment chain.
344    fn resolve_and_set_binding(&mut self, name: &String, value: JsValue) -> Result<(), JErrorType> {
345        let mut current_env = Some(self.lex_env.clone());
346
347        while let Some(env) = current_env.clone() {
348            let has_binding = env.borrow().inner.as_env_record().has_binding(name);
349            if has_binding {
350                return self.set_binding_in_env(&env, name, value);
351            }
352            current_env = env.borrow().outer.clone();
353        }
354
355        // If not found and not strict, create in global (var behavior)
356        if !self.strict {
357            return self.set_binding_in_env(&self.var_env.clone(), name, value);
358        }
359
360        Err(JErrorType::ReferenceError(format!("{} is not defined", name)))
361    }
362
363    /// Set a binding in a specific environment.
364    fn set_binding_in_env(
365        &mut self,
366        env: &JsLexEnvironmentType,
367        name: &String,
368        value: JsValue,
369    ) -> Result<(), JErrorType> {
370        let mut env_borrowed = env.borrow_mut();
371        match env_borrowed.inner.as_mut() {
372            EnvironmentRecordType::Declarative(rec) => {
373                rec.set_mutable_binding(&mut self.ctx_stack, name.clone(), value)
374            }
375            EnvironmentRecordType::Function(rec) => {
376                rec.set_mutable_binding(&mut self.ctx_stack, name.clone(), value)
377            }
378            EnvironmentRecordType::Global(rec) => {
379                rec.set_mutable_binding(&mut self.ctx_stack, name.clone(), value)
380            }
381            EnvironmentRecordType::Object(rec) => {
382                rec.set_mutable_binding(&mut self.ctx_stack, name.clone(), value)
383            }
384        }
385    }
386
387    /// Create a new binding in the current lexical environment.
388    pub fn create_binding(&mut self, name: &str, is_const: bool) -> Result<(), JErrorType> {
389        let mut env = self.lex_env.borrow_mut();
390        let name_string = name.to_string();
391        match env.inner.as_mut() {
392            EnvironmentRecordType::Declarative(rec) => {
393                if is_const {
394                    rec.create_immutable_binding(name_string)?
395                } else {
396                    rec.create_mutable_binding(name_string, false)?
397                }
398            }
399            EnvironmentRecordType::Function(rec) => {
400                if is_const {
401                    rec.create_immutable_binding(name_string)?
402                } else {
403                    rec.create_mutable_binding(name_string, false)?
404                }
405            }
406            EnvironmentRecordType::Global(rec) => {
407                if is_const {
408                    rec.create_immutable_binding(name_string)?
409                } else {
410                    rec.create_mutable_binding(name_string, false)?
411                }
412            }
413            EnvironmentRecordType::Object(rec) => {
414                rec.create_mutable_binding(name_string, false)?
415            }
416        }
417        self.lex_env_version = self.lex_env_version.wrapping_add(1);
418        Ok(())
419    }
420
421    /// Create a var binding in the variable environment.
422    pub fn create_var_binding(&mut self, name: &str) -> Result<(), JErrorType> {
423        let mut env = self.var_env.borrow_mut();
424        let name_string = name.to_string();
425        match env.inner.as_mut() {
426            EnvironmentRecordType::Declarative(rec) => {
427                rec.create_mutable_binding(name_string, true)?
428            }
429            EnvironmentRecordType::Function(rec) => rec.create_mutable_binding(name_string, true)?,
430            EnvironmentRecordType::Global(rec) => rec.create_mutable_binding(name_string, true)?,
431            EnvironmentRecordType::Object(rec) => rec.create_mutable_binding(name_string, true)?,
432        }
433        self.lex_env_version = self.lex_env_version.wrapping_add(1);
434        Ok(())
435    }
436
437    /// Initialize a binding with a value.
438    pub fn initialize_binding(&mut self, name: &str, value: JsValue) -> Result<(), JErrorType> {
439        let name_string = name.to_string();
440        let mut env = self.lex_env.borrow_mut();
441        match env.inner.as_mut() {
442            EnvironmentRecordType::Declarative(rec) => {
443                rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
444                Ok(())
445            }
446            EnvironmentRecordType::Function(rec) => {
447                rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
448                Ok(())
449            }
450            EnvironmentRecordType::Global(rec) => {
451                rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
452                Ok(())
453            }
454            EnvironmentRecordType::Object(rec) => {
455                rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
456                Ok(())
457            }
458        }
459    }
460
461    /// Initialize a var binding with a value.
462    pub fn initialize_var_binding(&mut self, name: &str, value: JsValue) -> Result<(), JErrorType> {
463        let name_string = name.to_string();
464        let mut env = self.var_env.borrow_mut();
465        match env.inner.as_mut() {
466            EnvironmentRecordType::Declarative(rec) => {
467                rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
468                Ok(())
469            }
470            EnvironmentRecordType::Function(rec) => {
471                rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
472                Ok(())
473            }
474            EnvironmentRecordType::Global(rec) => {
475                rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
476                Ok(())
477            }
478            EnvironmentRecordType::Object(rec) => {
479                rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
480                Ok(())
481            }
482        }
483    }
484
485    /// Check if a var binding exists.
486    pub fn has_var_binding(&self, name: &str) -> bool {
487        let env = self.var_env.borrow();
488        env.inner.as_env_record().has_binding(&name.to_string())
489    }
490
491    /// Set a var binding's value (for re-declaration).
492    pub fn set_var_binding(&mut self, name: &str, value: JsValue) -> Result<(), JErrorType> {
493        let name_string = name.to_string();
494        let mut env = self.var_env.borrow_mut();
495        match env.inner.as_mut() {
496            EnvironmentRecordType::Declarative(rec) => {
497                rec.set_mutable_binding(&mut self.ctx_stack, name_string, value)
498            }
499            EnvironmentRecordType::Function(rec) => {
500                rec.set_mutable_binding(&mut self.ctx_stack, name_string, value)
501            }
502            EnvironmentRecordType::Global(rec) => {
503                rec.set_mutable_binding(&mut self.ctx_stack, name_string, value)
504            }
505            EnvironmentRecordType::Object(rec) => {
506                rec.set_mutable_binding(&mut self.ctx_stack, name_string, value)
507            }
508        }
509    }
510
511    /// Push a new block scope (for let/const).
512    pub fn push_block_scope(&mut self) {
513        let new_env = new_declarative_environment(Some(self.lex_env.clone()));
514        self.lex_env = new_env;
515        self.lex_env_version = self.lex_env_version.wrapping_add(1);
516    }
517
518    /// Pop a block scope.
519    pub fn pop_block_scope(&mut self) {
520        let outer = self.lex_env.borrow().outer.clone();
521        if let Some(outer_env) = outer {
522            self.lex_env = outer_env;
523        }
524        self.lex_env_version = self.lex_env_version.wrapping_add(1);
525    }
526
527    /// Check if a binding exists in the current environment chain
528    /// or in the super-global scope.
529    pub fn has_binding(&self, name: &str) -> bool {
530        let name_string = name.to_string();
531        let mut current_env = Some(self.lex_env.clone());
532
533        while let Some(env) = current_env {
534            let env_borrowed = env.borrow();
535            if env_borrowed.inner.as_env_record().has_binding(&name_string) {
536                return true;
537            }
538            current_env = env_borrowed.outer.clone();
539        }
540
541        // Check super-global scope
542        self.super_global.borrow().has_name(name)
543    }
544}
545
546impl Default for EvalContext {
547    fn default() -> Self {
548        Self::new()
549    }
550}
551
552/// Simple object for use as global object.
553pub struct SimpleObject {
554    base: ObjectBase,
555    heap: Option<SharedHeap>,
556    allocated_bytes: usize,
557}
558
559impl SimpleObject {
560    pub fn new() -> Self {
561        SimpleObject {
562            base: ObjectBase::new(),
563            heap: None,
564            allocated_bytes: 0,
565        }
566    }
567
568    pub fn new_tracked(heap: SharedHeap) -> Result<Self, JErrorType> {
569        const SIMPLE_OBJECT_ALLOCATION_BYTES: usize = 64;
570        heap.borrow_mut().allocate(SIMPLE_OBJECT_ALLOCATION_BYTES)?;
571        Ok(SimpleObject {
572            base: ObjectBase::new(),
573            heap: Some(heap),
574            allocated_bytes: SIMPLE_OBJECT_ALLOCATION_BYTES,
575        })
576    }
577}
578
579impl Drop for SimpleObject {
580    fn drop(&mut self) {
581        if let Some(heap) = &self.heap {
582            heap.borrow_mut().deallocate(self.allocated_bytes);
583        }
584    }
585}
586
587impl JsObject for SimpleObject {
588    fn get_object_base_mut(&mut self) -> &mut ObjectBase {
589        &mut self.base
590    }
591
592    fn get_object_base(&self) -> &ObjectBase {
593        &self.base
594    }
595
596    fn as_js_object(&self) -> &dyn JsObject {
597        self
598    }
599
600    fn as_js_object_mut(&mut self) -> &mut dyn JsObject {
601        self
602    }
603}
604
605/// Function signature for built-in methods.
606/// Native functions receive the evaluation context, `this` value, and arguments.
607pub type NativeFn = fn(
608    ctx: &mut EvalContext,
609    this: JsValue,
610    args: Vec<JsValue>,
611) -> Result<JsValue, JErrorType>;
612
613/// Built-in function - either compiled-in or plugin-provided.
614pub enum BuiltInFn {
615    /// Direct function pointer - zero overhead for compiled-in functions.
616    Native(NativeFn),
617
618    /// Plugin-provided function - small vtable indirection cost.
619    Plugin(Box<dyn Fn(&mut EvalContext, JsValue, Vec<JsValue>) -> Result<JsValue, JErrorType> + Send + Sync>),
620
621    /// JavaScript implementation - interpreted at runtime.
622    Script(Rc<FunctionData>),
623}
624
625impl BuiltInFn {
626    /// Execute this built-in function.
627    pub fn call(
628        &self,
629        ctx: &mut EvalContext,
630        this: JsValue,
631        args: Vec<JsValue>,
632    ) -> Result<JsValue, JErrorType> {
633        match self {
634            BuiltInFn::Native(f) => f(ctx, this, args),
635            BuiltInFn::Plugin(f) => f(ctx, this, args),
636            BuiltInFn::Script(_f) => {
637                // TODO: Implement script execution when runtime is ready
638                Err(JErrorType::TypeError("Script built-ins not yet implemented".to_string()))
639            }
640        }
641    }
642}
643
644/// Built-in object definition.
645/// Represents a JavaScript built-in object like Array, Object, String, etc.
646pub struct BuiltInObject {
647    /// Name of the object (e.g., "Array", "Object", "Math").
648    pub name: String,
649
650    /// Parent prototype name, if any (e.g., "Object" for most built-ins).
651    pub prototype: Option<String>,
652
653    /// Methods defined on this object or its prototype.
654    pub methods: HashMap<String, BuiltInFn>,
655
656    /// Static properties.
657    pub properties: HashMap<String, JsValue>,
658
659    /// Constructor function, if this object is constructable.
660    pub constructor: Option<BuiltInFn>,
661}
662
663impl BuiltInObject {
664    /// Create a new built-in object with the given name.
665    pub fn new(name: impl Into<String>) -> Self {
666        BuiltInObject {
667            name: name.into(),
668            prototype: Some("Object".to_string()),
669            methods: HashMap::new(),
670            properties: HashMap::new(),
671            constructor: None,
672        }
673    }
674
675    /// Set the prototype chain parent.
676    pub fn with_prototype(mut self, prototype: impl Into<String>) -> Self {
677        self.prototype = Some(prototype.into());
678        self
679    }
680
681    /// Set no prototype (for objects like Object.prototype itself).
682    pub fn with_no_prototype(mut self) -> Self {
683        self.prototype = None;
684        self
685    }
686
687    /// Add a native method.
688    pub fn add_method(mut self, name: impl Into<String>, func: NativeFn) -> Self {
689        self.methods.insert(name.into(), BuiltInFn::Native(func));
690        self
691    }
692
693    /// Add a property.
694    pub fn add_property(mut self, name: impl Into<String>, value: JsValue) -> Self {
695        self.properties.insert(name.into(), value);
696        self
697    }
698
699    /// Set the constructor function.
700    pub fn with_constructor(mut self, constructor: NativeFn) -> Self {
701        self.constructor = Some(BuiltInFn::Native(constructor));
702        self
703    }
704}
705
706/// Plugin metadata.
707/// Contains information about a loaded plugin.
708#[derive(Debug, Clone)]
709pub struct PluginInfo {
710    /// Plugin name.
711    pub name: String,
712
713    /// Plugin version.
714    pub version: String,
715
716    /// List of object names this plugin provides.
717    pub provides: Vec<String>,
718}
719
720impl PluginInfo {
721    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
722        PluginInfo {
723            name: name.into(),
724            version: version.into(),
725            provides: Vec::new(),
726        }
727    }
728
729    pub fn with_provides(mut self, provides: Vec<String>) -> Self {
730        self.provides = provides;
731        self
732    }
733}
734