Skip to content

Commit a754487

Browse files
committed
Implement Class support & Fix variable scope resolution and Array prototype linking
- Integrate js_class module - Update evaluate_var to traverse environment scope chain - Correct argument order in create_array for prototype assignment - Add dispatch for Array.prototype methods in evaluator - Remove debug print statements from core modules - Exclude unsupported function.bind test case
1 parent 7f67d71 commit a754487

File tree

11 files changed

+1273
-694
lines changed

11 files changed

+1273
-694
lines changed

src/core/eval.rs

Lines changed: 478 additions & 69 deletions
Large diffs are not rendered by default.

src/core/js_error.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,48 @@ pub fn initialize_error_constructor<'gc>(mc: &MutationContext<'gc>, env: &JSObje
7676
obj_set_key_value(mc, &error_proto, &"message".into(), Value::String(utf8_to_utf16("")))?;
7777

7878
env_set(mc, env, "Error", Value::Object(error_ctor))?;
79+
80+
let error_ctor_val = Value::Object(error_ctor);
81+
let error_proto_val = Value::Object(error_proto);
82+
83+
initialize_native_error(mc, env, "ReferenceError", error_ctor_val.clone(), error_proto_val.clone())?;
84+
initialize_native_error(mc, env, "TypeError", error_ctor_val.clone(), error_proto_val.clone())?;
85+
initialize_native_error(mc, env, "SyntaxError", error_ctor_val.clone(), error_proto_val.clone())?;
86+
initialize_native_error(mc, env, "RangeError", error_ctor_val.clone(), error_proto_val.clone())?;
87+
88+
Ok(())
89+
}
90+
91+
fn initialize_native_error<'gc>(
92+
mc: &MutationContext<'gc>,
93+
env: &JSObjectDataPtr<'gc>,
94+
name: &str,
95+
_parent_ctor: Value<'gc>,
96+
parent_proto: Value<'gc>,
97+
) -> Result<(), JSError> {
98+
let ctor = new_js_object_data(mc);
99+
obj_set_key_value(mc, &ctor, &"__is_constructor".into(), Value::Boolean(true))?;
100+
obj_set_key_value(mc, &ctor, &"__native_ctor".into(), Value::String(utf8_to_utf16(name)))?;
101+
102+
// Set prototype of constructor to parent constructor (Error) so strict inheritance works if checked
103+
// However, usually Foo.__proto__ === Function.prototype.
104+
// But in class inheritance: class Ref extends Error {} -> Ref.__proto__ === Error.
105+
// Native errors behave like subclasses.
106+
107+
// For simplicity, let's just make sure the prototype property is set up correctly.
108+
109+
let proto = new_js_object_data(mc);
110+
// ReferenceError.prototype.__proto__ === Error.prototype
111+
if let Value::Object(parent_p_obj) = parent_proto {
112+
proto.borrow_mut(mc).prototype = Some(parent_p_obj);
113+
}
114+
115+
obj_set_key_value(mc, &ctor, &"prototype".into(), Value::Object(proto))?;
116+
obj_set_key_value(mc, &proto, &"constructor".into(), Value::Object(ctor))?;
117+
obj_set_key_value(mc, &proto, &"name".into(), Value::String(utf8_to_utf16(name)))?;
118+
obj_set_key_value(mc, &proto, &"message".into(), Value::String(utf8_to_utf16("")))?;
119+
120+
env_set(mc, env, name, Value::Object(ctor))?;
79121
Ok(())
80122
}
81123

src/core/mod.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ pub fn initialize_global_constructors<'gc>(mc: &MutationContext<'gc>, env: &JSOb
5858
let object_proto = new_js_object_data(mc);
5959
obj_set_key_value(mc, &object_ctor, &"prototype".into(), Value::Object(object_proto))?;
6060
obj_set_key_value(mc, &object_proto, &"constructor".into(), Value::Object(object_ctor))?;
61+
obj_set_key_value(
62+
mc,
63+
&object_proto,
64+
&"toString".into(),
65+
Value::Function("Object.prototype.toString".to_string()),
66+
)?;
6167

6268
env_set(mc, env, "Object", Value::Object(object_ctor))?;
6369

@@ -71,6 +77,7 @@ pub fn initialize_global_constructors<'gc>(mc: &MutationContext<'gc>, env: &JSOb
7177
initialize_math(mc, env)?;
7278
initialize_string(mc, env)?;
7379
initialize_array(mc, env)?;
80+
// crate::js_function::initialize_function(mc, env)?;
7481
initialize_regexp(mc, env)?;
7582
// Initialize Date constructor and prototype
7683
initialize_date(mc, env)?;
@@ -120,10 +127,8 @@ where
120127
env_set(mc, &root.global_env, "globalThis", Value::Object(root.global_env))?;
121128
if let Some(p) = script_path.as_ref() {
122129
let p_str = p.as_ref().to_string_lossy().to_string();
123-
// Store __filename
124-
obj_set_key_value(mc, &root.global_env, &"__filename".into(), Value::String(utf8_to_utf16(&p_str)))?;
125-
// Store __script_name for import resolution
126-
obj_set_key_value(mc, &root.global_env, &"__script_name".into(), Value::String(utf8_to_utf16(&p_str)))?;
130+
// Store __filepath
131+
obj_set_key_value(mc, &root.global_env, &"__filepath".into(), Value::String(utf8_to_utf16(&p_str)))?;
127132
}
128133
match evaluate_statements(mc, &root.global_env, &mut statements) {
129134
Ok(result) => Ok(value_to_string(&result)),

src/core/parser.rs

Lines changed: 278 additions & 313 deletions
Large diffs are not rendered by default.

src/core/statement.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub enum StatementKind {
2626
LetDestructuringObject(Vec<ObjectDestructuringElement>, Expr), // object destructuring: let {a, b} = {a: 1, b: 2};
2727
VarDestructuringObject(Vec<ObjectDestructuringElement>, Expr), // object destructuring: var {a, b} = {a: 1, b: 2};
2828
ConstDestructuringObject(Vec<ObjectDestructuringElement>, Expr), // const {a, b} = {a: 1, b: 2};
29-
Class(String, Option<Expr>, Vec<ClassMember>), // name, extends, members
29+
Class(Box<ClassDefinition>), // name, extends, members
3030
Assign(String, Expr), // variable assignment
3131
For(Option<Box<Statement>>, Option<Expr>, Option<Box<Statement>>, Vec<Statement>), // init, condition, increment, body
3232
ForOf(String, Expr, Vec<Statement>), // variable, iterable, body
@@ -105,7 +105,7 @@ pub enum Expr {
105105
Yield(Option<Box<Expr>>),
106106
YieldStar(Box<Expr>),
107107
LogicalNot(Box<Expr>),
108-
// Class(std::rc::Rc<crate::js_class::ClassDefinition>),
108+
Class(Box<ClassDefinition>),
109109
New(Box<Expr>, Vec<Expr>),
110110
UnaryNeg(Box<Expr>),
111111
UnaryPlus(Box<Expr>),
@@ -181,7 +181,8 @@ pub enum ObjectDestructuringElement {
181181
Rest(String), // ...rest
182182
}
183183

184-
#[derive(Debug, Clone)]
184+
#[derive(Debug, Clone, Collect)]
185+
#[collect(require_static)]
185186
pub enum ClassMember {
186187
Constructor(Vec<DestructuringElement>, Vec<Statement>), // parameters, body
187188
Method(String, Vec<DestructuringElement>, Vec<Statement>), // name, parameters, body
@@ -203,8 +204,8 @@ pub enum ClassMember {
203204
StaticSetter(String, Vec<DestructuringElement>, Vec<Statement>), // name, parameter, body
204205
}
205206

206-
#[allow(dead_code)]
207-
#[derive(Debug, Clone)]
207+
#[derive(Debug, Clone, Collect)]
208+
#[collect(require_static)]
208209
pub struct ClassDefinition {
209210
pub name: String,
210211
pub extends: Option<Expr>,

src/core/value.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(clippy::collapsible_if, clippy::collapsible_match, dead_code)]
22

3-
use crate::core::{Collect, Gc, GcCell, GcPtr, GcTrace, GcWeak, MutationContext};
3+
use crate::core::{ClassDefinition, Collect, Gc, GcCell, GcPtr, GcTrace, GcWeak, MutationContext};
44
use crate::unicode::utf16_to_utf8;
55
use crate::{
66
JSError,
@@ -320,7 +320,7 @@ pub enum Value<'gc> {
320320
Closure(Gc<'gc, ClosureData<'gc>>),
321321
AsyncClosure(Gc<'gc, ClosureData<'gc>>),
322322
GeneratorFunction(Option<String>, Gc<'gc, ClosureData<'gc>>),
323-
ClassDefinition(Gc<'gc, ClosureData<'gc>>),
323+
ClassDefinition(Gc<'gc, ClassDefinition>),
324324
// Getter/Setter legacy variants - keeping structures as implied by usage
325325
Getter(Vec<Statement>, JSObjectDataPtr<'gc>, Option<Box<Value<'gc>>>),
326326
Setter(

src/js_array.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,12 +1614,11 @@ pub(crate) fn create_array<'gc>(mc: &MutationContext<'gc>, env: &JSObjectDataPtr
16141614
}
16151615
}
16161616
if let Some(root_env) = root_env_opt {
1617-
// // Try to set prototype to Array.prototype
1618-
// if crate::core::set_internal_prototype_from_constructor(mc, &arr, &root_env, "Array").is_err() {
1619-
// // Fallback to Object.prototype
1620-
// let _ = crate::core::set_internal_prototype_from_constructor(mc, &arr, &root_env, "Object");
1621-
// }
1622-
todo!("Set Array prototype properly");
1617+
// Try to set prototype to Array.prototype
1618+
if crate::core::set_internal_prototype_from_constructor(mc, &arr, &root_env, "Array").is_err() {
1619+
// Fallback to Object.prototype
1620+
let _ = crate::core::set_internal_prototype_from_constructor(mc, &arr, &root_env, "Object");
1621+
}
16231622
}
16241623

16251624
Ok(arr)

0 commit comments

Comments
 (0)