use crate::runner::ds::env_record::{
new_declarative_environment, new_global_environment, EnvironmentRecord, EnvironmentRecordType,
};
use crate::runner::ds::error::JErrorType;
use crate::runner::ds::execution_context::ExecutionContextStack;
use crate::runner::ds::heap::{Heap, HeapConfig};
use crate::runner::ds::lex_env::JsLexEnvironmentType;
use crate::runner::ds::object::{JsObject, JsObjectType, ObjectBase, ObjectType};
use crate::runner::ds::value::JsValue;
use crate::parser::ast::FunctionData;
use super::super_global::SuperGlobalEnvironment;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
pub type SharedHeap = Rc<RefCell<Heap>>;
pub type SharedSuperGlobal = Rc<RefCell<SuperGlobalEnvironment>>;
pub struct EvalContext {
pub global_this: Option<JsValue>,
pub heap: SharedHeap,
pub lex_env: JsLexEnvironmentType,
pub var_env: JsLexEnvironmentType,
pub ctx_stack: ExecutionContextStack,
pub strict: bool,
pub lex_env_version: u64,
pub super_global: SharedSuperGlobal,
}
impl EvalContext {
pub fn new() -> Self {
let global_obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(
SimpleObject::new(),
))));
let global_env = new_global_environment(global_obj.clone());
EvalContext {
global_this: Some(JsValue::Object(global_obj)),
heap: Rc::new(RefCell::new(Heap::default())),
lex_env: global_env.clone(),
var_env: global_env,
ctx_stack: ExecutionContextStack::new(),
strict: false,
lex_env_version: 0,
super_global: Rc::new(RefCell::new(SuperGlobalEnvironment::new())),
}
}
pub fn with_heap_config(config: HeapConfig) -> Self {
let global_obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(
SimpleObject::new(),
))));
let global_env = new_global_environment(global_obj.clone());
EvalContext {
global_this: Some(JsValue::Object(global_obj)),
heap: Rc::new(RefCell::new(Heap::new(config))),
lex_env: global_env.clone(),
var_env: global_env,
ctx_stack: ExecutionContextStack::new(),
strict: false,
lex_env_version: 0,
super_global: Rc::new(RefCell::new(SuperGlobalEnvironment::new())),
}
}
pub fn current_lex_env_version(&self) -> u64 {
self.lex_env_version
}
pub fn add_resolver(&mut self, resolver: Box<dyn super::resolver::PluginResolver>) {
self.super_global.borrow_mut().add_resolver(resolver);
}
pub fn install_core_builtins(&mut self, registry: super::registry::BuiltInRegistry) {
self.add_resolver(Box::new(super::core_resolver::CorePluginResolver::new(registry)));
}
pub fn allocate(&self, bytes: usize) -> Result<(), JErrorType> {
self.heap.borrow_mut().allocate(bytes)
}
pub fn deallocate(&self, bytes: usize) {
self.heap.borrow_mut().deallocate(bytes)
}
pub fn heap_usage(&self) -> usize {
self.heap.borrow().get_allocated()
}
pub fn new_tracked_object(&self) -> Result<SimpleObject, JErrorType> {
SimpleObject::new_tracked(self.heap.clone())
}
pub fn get_binding(&mut self, name: &str) -> Result<JsValue, JErrorType> {
let name_string = name.to_string();
self.resolve_binding(&name_string)
}
pub fn get_binding_with_env(
&mut self,
name: &str,
) -> Result<(JsValue, JsLexEnvironmentType), JErrorType> {
let name_string = name.to_string();
let mut current_env = Some(self.lex_env.clone());
let mut last_env: Option<JsLexEnvironmentType> = None;
while let Some(env) = current_env {
let env_borrowed = env.borrow();
if env_borrowed.inner.as_env_record().has_binding(&name_string) {
drop(env_borrowed);
let value = env
.borrow()
.inner
.as_env_record()
.get_binding_value(&mut self.ctx_stack, &name_string)?;
return Ok((value, env));
}
last_env = Some(env.clone());
current_env = env_borrowed.outer.clone();
}
let sg = self.super_global.clone();
let result = sg.borrow_mut().resolve_binding(&name_string, self);
let value = result?;
let env = last_env.unwrap_or_else(|| self.lex_env.clone());
Ok((value, env))
}
pub fn get_binding_in_env(
&mut self,
env: &JsLexEnvironmentType,
name: &str,
) -> Result<JsValue, JErrorType> {
let name_string = name.to_string();
env.borrow()
.inner
.as_env_record()
.get_binding_value(&mut self.ctx_stack, &name_string)
}
pub fn resolve_binding(&mut self, name: &String) -> Result<JsValue, JErrorType> {
let mut current_env = Some(self.lex_env.clone());
while let Some(env) = current_env {
let env_borrowed = env.borrow();
if env_borrowed.inner.as_env_record().has_binding(name) {
drop(env_borrowed);
return env
.borrow()
.inner
.as_env_record()
.get_binding_value(&mut self.ctx_stack, name);
}
current_env = env_borrowed.outer.clone();
}
let sg = self.super_global.clone();
let result = sg.borrow_mut().resolve_binding(name, self);
result
}
pub fn set_binding(&mut self, name: &str, value: JsValue) -> Result<(), JErrorType> {
let name_string = name.to_string();
self.resolve_and_set_binding(&name_string, value)
}
pub fn set_binding_with_env(
&mut self,
name: &str,
value: JsValue,
) -> Result<JsLexEnvironmentType, JErrorType> {
let name_string = name.to_string();
let mut current_env = Some(self.lex_env.clone());
while let Some(env) = current_env.clone() {
let has_binding = env.borrow().inner.as_env_record().has_binding(&name_string);
if has_binding {
self.set_binding_in_env(&env, &name_string, value)?;
return Ok(env);
}
current_env = env.borrow().outer.clone();
}
if !self.strict {
let env = self.var_env.clone();
self.set_binding_in_env(&env, &name_string, value)?;
return Ok(env);
}
Err(JErrorType::ReferenceError(format!("{} is not defined", name)))
}
pub fn set_binding_in_env_cached(
&mut self,
env: &JsLexEnvironmentType,
name: &str,
value: JsValue,
) -> Result<(), JErrorType> {
let name_string = name.to_string();
self.set_binding_in_env(env, &name_string, value)
}
fn resolve_and_set_binding(&mut self, name: &String, value: JsValue) -> Result<(), JErrorType> {
let mut current_env = Some(self.lex_env.clone());
while let Some(env) = current_env.clone() {
let has_binding = env.borrow().inner.as_env_record().has_binding(name);
if has_binding {
return self.set_binding_in_env(&env, name, value);
}
current_env = env.borrow().outer.clone();
}
if !self.strict {
return self.set_binding_in_env(&self.var_env.clone(), name, value);
}
Err(JErrorType::ReferenceError(format!("{} is not defined", name)))
}
fn set_binding_in_env(
&mut self,
env: &JsLexEnvironmentType,
name: &String,
value: JsValue,
) -> Result<(), JErrorType> {
let mut env_borrowed = env.borrow_mut();
match env_borrowed.inner.as_mut() {
EnvironmentRecordType::Declarative(rec) => {
rec.set_mutable_binding(&mut self.ctx_stack, name.clone(), value)
}
EnvironmentRecordType::Function(rec) => {
rec.set_mutable_binding(&mut self.ctx_stack, name.clone(), value)
}
EnvironmentRecordType::Global(rec) => {
rec.set_mutable_binding(&mut self.ctx_stack, name.clone(), value)
}
EnvironmentRecordType::Object(rec) => {
rec.set_mutable_binding(&mut self.ctx_stack, name.clone(), value)
}
}
}
pub fn create_binding(&mut self, name: &str, is_const: bool) -> Result<(), JErrorType> {
let mut env = self.lex_env.borrow_mut();
let name_string = name.to_string();
match env.inner.as_mut() {
EnvironmentRecordType::Declarative(rec) => {
if is_const {
rec.create_immutable_binding(name_string)?
} else {
rec.create_mutable_binding(name_string, false)?
}
}
EnvironmentRecordType::Function(rec) => {
if is_const {
rec.create_immutable_binding(name_string)?
} else {
rec.create_mutable_binding(name_string, false)?
}
}
EnvironmentRecordType::Global(rec) => {
if is_const {
rec.create_immutable_binding(name_string)?
} else {
rec.create_mutable_binding(name_string, false)?
}
}
EnvironmentRecordType::Object(rec) => {
rec.create_mutable_binding(name_string, false)?
}
}
self.lex_env_version = self.lex_env_version.wrapping_add(1);
Ok(())
}
pub fn create_var_binding(&mut self, name: &str) -> Result<(), JErrorType> {
let mut env = self.var_env.borrow_mut();
let name_string = name.to_string();
match env.inner.as_mut() {
EnvironmentRecordType::Declarative(rec) => {
rec.create_mutable_binding(name_string, true)?
}
EnvironmentRecordType::Function(rec) => rec.create_mutable_binding(name_string, true)?,
EnvironmentRecordType::Global(rec) => rec.create_mutable_binding(name_string, true)?,
EnvironmentRecordType::Object(rec) => rec.create_mutable_binding(name_string, true)?,
}
self.lex_env_version = self.lex_env_version.wrapping_add(1);
Ok(())
}
pub fn initialize_binding(&mut self, name: &str, value: JsValue) -> Result<(), JErrorType> {
let name_string = name.to_string();
let mut env = self.lex_env.borrow_mut();
match env.inner.as_mut() {
EnvironmentRecordType::Declarative(rec) => {
rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
Ok(())
}
EnvironmentRecordType::Function(rec) => {
rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
Ok(())
}
EnvironmentRecordType::Global(rec) => {
rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
Ok(())
}
EnvironmentRecordType::Object(rec) => {
rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
Ok(())
}
}
}
pub fn initialize_var_binding(&mut self, name: &str, value: JsValue) -> Result<(), JErrorType> {
let name_string = name.to_string();
let mut env = self.var_env.borrow_mut();
match env.inner.as_mut() {
EnvironmentRecordType::Declarative(rec) => {
rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
Ok(())
}
EnvironmentRecordType::Function(rec) => {
rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
Ok(())
}
EnvironmentRecordType::Global(rec) => {
rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
Ok(())
}
EnvironmentRecordType::Object(rec) => {
rec.initialize_binding(&mut self.ctx_stack, name_string, value)?;
Ok(())
}
}
}
pub fn has_var_binding(&self, name: &str) -> bool {
let env = self.var_env.borrow();
env.inner.as_env_record().has_binding(&name.to_string())
}
pub fn set_var_binding(&mut self, name: &str, value: JsValue) -> Result<(), JErrorType> {
let name_string = name.to_string();
let mut env = self.var_env.borrow_mut();
match env.inner.as_mut() {
EnvironmentRecordType::Declarative(rec) => {
rec.set_mutable_binding(&mut self.ctx_stack, name_string, value)
}
EnvironmentRecordType::Function(rec) => {
rec.set_mutable_binding(&mut self.ctx_stack, name_string, value)
}
EnvironmentRecordType::Global(rec) => {
rec.set_mutable_binding(&mut self.ctx_stack, name_string, value)
}
EnvironmentRecordType::Object(rec) => {
rec.set_mutable_binding(&mut self.ctx_stack, name_string, value)
}
}
}
pub fn push_block_scope(&mut self) {
let new_env = new_declarative_environment(Some(self.lex_env.clone()));
self.lex_env = new_env;
self.lex_env_version = self.lex_env_version.wrapping_add(1);
}
pub fn pop_block_scope(&mut self) {
let outer = self.lex_env.borrow().outer.clone();
if let Some(outer_env) = outer {
self.lex_env = outer_env;
}
self.lex_env_version = self.lex_env_version.wrapping_add(1);
}
pub fn has_binding(&self, name: &str) -> bool {
let name_string = name.to_string();
let mut current_env = Some(self.lex_env.clone());
while let Some(env) = current_env {
let env_borrowed = env.borrow();
if env_borrowed.inner.as_env_record().has_binding(&name_string) {
return true;
}
current_env = env_borrowed.outer.clone();
}
self.super_global.borrow().has_name(name)
}
}
impl Default for EvalContext {
fn default() -> Self {
Self::new()
}
}
pub struct SimpleObject {
base: ObjectBase,
heap: Option<SharedHeap>,
allocated_bytes: usize,
}
impl SimpleObject {
pub fn new() -> Self {
SimpleObject {
base: ObjectBase::new(),
heap: None,
allocated_bytes: 0,
}
}
pub fn new_tracked(heap: SharedHeap) -> Result<Self, JErrorType> {
const SIMPLE_OBJECT_ALLOCATION_BYTES: usize = 64;
heap.borrow_mut().allocate(SIMPLE_OBJECT_ALLOCATION_BYTES)?;
Ok(SimpleObject {
base: ObjectBase::new(),
heap: Some(heap),
allocated_bytes: SIMPLE_OBJECT_ALLOCATION_BYTES,
})
}
}
impl Drop for SimpleObject {
fn drop(&mut self) {
if let Some(heap) = &self.heap {
heap.borrow_mut().deallocate(self.allocated_bytes);
}
}
}
impl JsObject for SimpleObject {
fn get_object_base_mut(&mut self) -> &mut ObjectBase {
&mut self.base
}
fn get_object_base(&self) -> &ObjectBase {
&self.base
}
fn as_js_object(&self) -> &dyn JsObject {
self
}
fn as_js_object_mut(&mut self) -> &mut dyn JsObject {
self
}
}
pub type NativeFn = fn(
ctx: &mut EvalContext,
this: JsValue,
args: Vec<JsValue>,
) -> Result<JsValue, JErrorType>;
pub enum BuiltInFn {
Native(NativeFn),
Plugin(Box<dyn Fn(&mut EvalContext, JsValue, Vec<JsValue>) -> Result<JsValue, JErrorType> + Send + Sync>),
Script(Rc<FunctionData>),
}
impl BuiltInFn {
pub fn call(
&self,
ctx: &mut EvalContext,
this: JsValue,
args: Vec<JsValue>,
) -> Result<JsValue, JErrorType> {
match self {
BuiltInFn::Native(f) => f(ctx, this, args),
BuiltInFn::Plugin(f) => f(ctx, this, args),
BuiltInFn::Script(_f) => {
Err(JErrorType::TypeError("Script built-ins not yet implemented".to_string()))
}
}
}
}
pub struct BuiltInObject {
pub name: String,
pub prototype: Option<String>,
pub methods: HashMap<String, BuiltInFn>,
pub properties: HashMap<String, JsValue>,
pub constructor: Option<BuiltInFn>,
}
impl BuiltInObject {
pub fn new(name: impl Into<String>) -> Self {
BuiltInObject {
name: name.into(),
prototype: Some("Object".to_string()),
methods: HashMap::new(),
properties: HashMap::new(),
constructor: None,
}
}
pub fn with_prototype(mut self, prototype: impl Into<String>) -> Self {
self.prototype = Some(prototype.into());
self
}
pub fn with_no_prototype(mut self) -> Self {
self.prototype = None;
self
}
pub fn add_method(mut self, name: impl Into<String>, func: NativeFn) -> Self {
self.methods.insert(name.into(), BuiltInFn::Native(func));
self
}
pub fn add_property(mut self, name: impl Into<String>, value: JsValue) -> Self {
self.properties.insert(name.into(), value);
self
}
pub fn with_constructor(mut self, constructor: NativeFn) -> Self {
self.constructor = Some(BuiltInFn::Native(constructor));
self
}
}
#[derive(Debug, Clone)]
pub struct PluginInfo {
pub name: String,
pub version: String,
pub provides: Vec<String>,
}
impl PluginInfo {
pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
PluginInfo {
name: name.into(),
version: version.into(),
provides: Vec::new(),
}
}
pub fn with_provides(mut self, provides: Vec<String>) -> Self {
self.provides = provides;
self
}
}