Skip to content

Commit a96c044

Browse files
committed
Refactor: JSError::TypeError
1 parent d23fd75 commit a96c044

File tree

13 files changed

+219
-449
lines changed

13 files changed

+219
-449
lines changed

src/core/eval.rs

Lines changed: 32 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::{
1717
js_number::make_number_object,
1818
js_promise::{PromiseState, handle_promise_method, run_event_loop},
1919
js_testintl::make_testintl_object,
20-
obj_get_value,
20+
make_type_error, obj_get_value,
2121
sprintf::handle_sprintf_call,
2222
tmpfile::{create_tmpfile, handle_file_method},
2323
unicode::{utf8_to_utf16, utf16_char_at, utf16_len, utf16_to_utf8},
@@ -1929,9 +1929,7 @@ fn evaluate_add_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Re
19291929
}
19301930
// Disallow mixing BigInt and Number for arithmetic
19311931
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
1932-
return Err(JSError::TypeError {
1933-
message: "Cannot mix BigInt and other types".to_string(),
1934-
});
1932+
return Err(make_type_error!("Cannot mix BigInt and other types"));
19351933
}
19361934
_ => {
19371935
return Err(eval_error_here!("Invalid operands for +="));
@@ -1959,10 +1957,9 @@ fn evaluate_sub_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Re
19591957
Value::BigInt((a - b).to_string())
19601958
}
19611959
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
1962-
return Err(JSError::TypeError {
1963-
message: "Cannot mix BigInt and other types".to_string(),
1964-
});
1960+
return Err(make_type_error!("Cannot mix BigInt and other types"));
19651961
}
1962+
19661963
_ => {
19671964
return Err(eval_error_here!("Invalid operands for -="));
19681965
}
@@ -1991,9 +1988,7 @@ fn evaluate_mul_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Re
19911988
Value::BigInt((a * b).to_string())
19921989
}
19931990
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
1994-
return Err(JSError::TypeError {
1995-
message: "Cannot mix BigInt and other types".to_string(),
1996-
});
1991+
return Err(make_type_error!("Cannot mix BigInt and other types"));
19971992
}
19981993
_ => {
19991994
return Err(eval_error_here!("Invalid operands for *="));
@@ -2028,9 +2023,7 @@ fn evaluate_pow_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Re
20282023
}
20292024
// Mixing BigInt and Number is disallowed for exponentiation
20302025
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
2031-
return Err(JSError::TypeError {
2032-
message: "Cannot mix BigInt and other types".to_string(),
2033-
});
2026+
return Err(make_type_error!("Cannot mix BigInt and other types"));
20342027
}
20352028
_ => {
20362029
return Err(eval_error_here!("Invalid operands for **="));
@@ -2070,9 +2063,7 @@ fn evaluate_div_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Re
20702063
Value::BigInt((a / b).to_string())
20712064
}
20722065
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
2073-
return Err(JSError::TypeError {
2074-
message: "Cannot mix BigInt and other types".to_string(),
2075-
});
2066+
return Err(make_type_error!("Cannot mix BigInt and other types"));
20762067
}
20772068
_ => {
20782069
return Err(eval_error_here!("Invalid operands for /="));
@@ -2110,9 +2101,7 @@ fn evaluate_mod_assign(env: &JSObjectDataPtr, target: &Expr, value: &Expr) -> Re
21102101
Value::BigInt((a % b).to_string())
21112102
}
21122103
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
2113-
return Err(JSError::TypeError {
2114-
message: "Cannot mix BigInt and other types".to_string(),
2115-
});
2104+
return Err(make_type_error!("Cannot mix BigInt and other types"));
21162105
}
21172106
_ => {
21182107
return Err(eval_error_here!("Invalid operands for %="));
@@ -2486,9 +2475,7 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
24862475
};
24872476
// '+' should throw when a Symbol is encountered during implicit coercion
24882477
if matches!(l_prim, Value::Symbol(_)) || matches!(r_prim, Value::Symbol(_)) {
2489-
return Err(JSError::TypeError {
2490-
message: "Cannot convert Symbol to primitive".to_string(),
2491-
});
2478+
return Err(make_type_error!("Cannot convert Symbol to primitive"));
24922479
}
24932480
match (l_prim, r_prim) {
24942481
(Value::Number(ln), Value::Number(rn)) => Ok(Value::Number(ln + rn)),
@@ -2557,9 +2544,9 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
25572544
Ok(Value::BigInt((a - b).to_string()))
25582545
}
25592546
// Mixing BigInt and Number is not allowed for arithmetic
2560-
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => Err(JSError::TypeError {
2561-
message: "Cannot mix BigInt and other types".to_string(),
2562-
}),
2547+
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
2548+
Err(make_type_error!("Cannot mix BigInt and other types"))
2549+
}
25632550
_ => Err(eval_error_here!("error")),
25642551
},
25652552
BinaryOp::Mul => match (l, r) {
@@ -2569,9 +2556,9 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
25692556
let b = BigInt::parse_bytes(rb.as_bytes(), 10).ok_or(eval_error_here!("invalid bigint"))?;
25702557
Ok(Value::BigInt((a * b).to_string()))
25712558
}
2572-
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => Err(JSError::TypeError {
2573-
message: "Cannot mix BigInt and other types".to_string(),
2574-
}),
2559+
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
2560+
Err(make_type_error!("Cannot mix BigInt and other types"))
2561+
}
25752562
_ => Err(eval_error_here!("error")),
25762563
},
25772564
BinaryOp::Pow => match (l, r) {
@@ -2587,9 +2574,9 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
25872574
Ok(Value::BigInt(a.pow(exp).to_string()))
25882575
}
25892576
// Mixing BigInt and Number is disallowed for exponentiation
2590-
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => Err(JSError::TypeError {
2591-
message: "Cannot mix BigInt and other types".to_string(),
2592-
}),
2577+
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
2578+
Err(make_type_error!("Cannot mix BigInt and other types"))
2579+
}
25932580
_ => Err(eval_error_here!("error")),
25942581
},
25952582
BinaryOp::Div => match (l, r) {
@@ -2610,9 +2597,9 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
26102597
}
26112598
}
26122599
// Mixing BigInt and Number is not allowed
2613-
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => Err(JSError::TypeError {
2614-
message: "Cannot mix BigInt and other types".to_string(),
2615-
}),
2600+
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
2601+
Err(make_type_error!("Cannot mix BigInt and other types"))
2602+
}
26162603
_ => Err(eval_error_here!("error")),
26172604
},
26182605
BinaryOp::Equal => match (l, r) {
@@ -2757,9 +2744,7 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
27572744
}
27582745
}
27592746
Value::Undefined => Ok(f64::NAN),
2760-
Value::Symbol(_) => Err(JSError::TypeError {
2761-
message: "Cannot convert Symbol to number".to_string(),
2762-
}),
2747+
Value::Symbol(_) => Err(make_type_error!("Cannot convert Symbol to number")),
27632748
_ => Err(eval_error_here!("error")),
27642749
}
27652750
};
@@ -2841,9 +2826,7 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
28412826
}
28422827
}
28432828
Value::Undefined => Ok(f64::NAN),
2844-
Value::Symbol(_) => Err(JSError::TypeError {
2845-
message: "Cannot convert Symbol to number".to_string(),
2846-
}),
2829+
Value::Symbol(_) => Err(make_type_error!("Cannot convert Symbol to number")),
28472830
_ => Err(eval_error_here!("error")),
28482831
}
28492832
};
@@ -2922,9 +2905,7 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
29222905
}
29232906
}
29242907
Value::Undefined => Ok(f64::NAN),
2925-
Value::Symbol(_) => Err(JSError::TypeError {
2926-
message: "Cannot convert Symbol to number".to_string(),
2927-
}),
2908+
Value::Symbol(_) => Err(make_type_error!("Cannot convert Symbol to number")),
29282909
_ => Err(eval_error_here!("error")),
29292910
}
29302911
};
@@ -3005,9 +2986,7 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
30052986
}
30062987
}
30072988
Value::Undefined => Ok(f64::NAN),
3008-
Value::Symbol(_) => Err(JSError::TypeError {
3009-
message: "Cannot convert Symbol to number".to_string(),
3010-
}),
2989+
Value::Symbol(_) => Err(make_type_error!("Cannot convert Symbol to number")),
30112990
_ => Err(eval_error_here!("error")),
30122991
}
30132992
};
@@ -3038,9 +3017,9 @@ fn evaluate_binary(env: &JSObjectDataPtr, left: &Expr, op: &BinaryOp, right: &Ex
30383017
}
30393018
}
30403019
// Mixing BigInt and Number is not allowed
3041-
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => Err(JSError::TypeError {
3042-
message: "Cannot mix BigInt and other types".to_string(),
3043-
}),
3020+
(Value::BigInt(_), Value::Number(_)) | (Value::Number(_), Value::BigInt(_)) => {
3021+
Err(make_type_error!("Cannot mix BigInt and other types"))
3022+
}
30443023
_ => Err(eval_error_here!("Modulo operation only supported for numbers")),
30453024
},
30463025
BinaryOp::InstanceOf => {
@@ -4148,9 +4127,7 @@ fn handle_symbol_static_method(method: &str, args: &[Expr], env: &JSObjectDataPt
41484127
"for" => {
41494128
// Symbol.for(key) - returns a symbol from the global registry
41504129
if args.len() != 1 {
4151-
return Err(JSError::TypeError {
4152-
message: "Symbol.for requires exactly one argument".to_string(),
4153-
});
4130+
return Err(make_type_error!("Symbol.for requires exactly one argument"));
41544131
}
41554132
let key_expr = &args[0];
41564133
let key_val = evaluate_expr(env, key_expr)?;
@@ -4177,9 +4154,7 @@ fn handle_symbol_static_method(method: &str, args: &[Expr], env: &JSObjectDataPt
41774154
"keyFor" => {
41784155
// Symbol.keyFor(symbol) - returns the key for a symbol in the global registry
41794156
if args.len() != 1 {
4180-
return Err(JSError::TypeError {
4181-
message: "Symbol.keyFor requires exactly one argument".to_string(),
4182-
});
4157+
return Err(make_type_error!("Symbol.keyFor requires exactly one argument"));
41834158
}
41844159
let symbol_expr = &args[0];
41854160
let symbol_val = evaluate_expr(env, symbol_expr)?;
@@ -4197,14 +4172,10 @@ fn handle_symbol_static_method(method: &str, args: &[Expr], env: &JSObjectDataPt
41974172
Ok(Value::Undefined)
41984173
})
41994174
} else {
4200-
Err(JSError::TypeError {
4201-
message: "Symbol.keyFor requires a symbol as argument".to_string(),
4202-
})
4175+
Err(make_type_error!("Symbol.keyFor requires a symbol as argument"))
42034176
}
42044177
}
4205-
_ => Err(JSError::TypeError {
4206-
message: format!("Symbol has no static method '{}'", method),
4207-
}),
4178+
_ => Err(make_type_error!(format!("Symbol has no static method '{method}'"))),
42084179
}
42094180
}
42104181

src/core/value.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
js_array::is_array,
77
js_class::ClassDefinition,
88
js_promise::JSPromise,
9+
make_type_error,
910
};
1011

1112
pub type JSObjectDataPtr = Rc<RefCell<JSObjectData>>;
@@ -209,9 +210,7 @@ pub fn to_primitive(val: &Value, hint: &str) -> Result<Value, JSError> {
209210
match result {
210211
Value::Number(_) | Value::String(_) | Value::Boolean(_) | Value::Symbol(_) => return Ok(result),
211212
_ => {
212-
return Err(JSError::TypeError {
213-
message: "[Symbol.toPrimitive] must return a primitive".to_string(),
214-
});
213+
return Err(make_type_error!("[Symbol.toPrimitive] must return a primitive"));
215214
}
216215
}
217216
}
@@ -245,9 +244,7 @@ pub fn to_primitive(val: &Value, hint: &str) -> Result<Value, JSError> {
245244
}
246245
}
247246

248-
Err(JSError::TypeError {
249-
message: "Cannot convert object to primitive".to_string(),
250-
})
247+
Err(make_type_error!("Cannot convert object to primitive"))
251248
}
252249
_ => Ok(val.clone()),
253250
}
@@ -528,9 +525,7 @@ pub fn env_get<T: AsRef<str>>(env: &JSObjectDataPtr, key: T) -> Option<Rc<RefCel
528525
pub fn env_set<T: AsRef<str>>(env: &JSObjectDataPtr, key: T, val: Value) -> Result<(), JSError> {
529526
let key = key.as_ref();
530527
if env.borrow().is_const(key) {
531-
return Err(JSError::TypeError {
532-
message: format!("Assignment to constant variable '{key}'"),
533-
});
528+
return Err(make_type_error!(format!("Assignment to constant variable '{key}'")));
534529
}
535530
env.borrow_mut()
536531
.insert(PropertyKey::String(key.to_string()), Rc::new(RefCell::new(val)));

src/error.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,13 @@ pub enum JSError {
2020
#[error("Variable '{name}' not found")]
2121
VariableNotFound { name: String },
2222

23-
#[error("Type error: {message}")]
24-
TypeError { message: String },
23+
#[error("Type error: {message} at {method} {file}:{line}")]
24+
TypeError {
25+
message: String,
26+
file: String,
27+
line: usize,
28+
method: String,
29+
},
2530

2631
#[error("Syntax error: {message}")]
2732
SyntaxError { message: String },
@@ -75,6 +80,20 @@ macro_rules! eval_error_here {
7580
};
7681
}
7782

83+
// Macro that constructs a TypeError using the compile-time caller
84+
// location and the provided message.
85+
#[macro_export]
86+
macro_rules! make_type_error {
87+
($msg:expr) => {
88+
$crate::JSError::TypeError {
89+
message: $msg.to_string(),
90+
file: file!().to_string(),
91+
line: line!() as usize,
92+
method: $crate::function_name!().to_string(),
93+
}
94+
};
95+
}
96+
7897
#[macro_export]
7998
macro_rules! function_name {
8099
() => {{

src/js_array.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -117,24 +117,16 @@ pub(crate) fn handle_array_constructor(args: &[Expr], env: &JSObjectDataPtr) ->
117117
match arg_val {
118118
Value::Number(n) => {
119119
if n.is_nan() {
120-
return Err(JSError::TypeError {
121-
message: "Invalid array length".to_string(),
122-
});
120+
return Err(make_type_error!("Invalid array length"));
123121
}
124122
if n.fract() != 0.0 {
125-
return Err(JSError::TypeError {
126-
message: "Array length must be an integer".to_string(),
127-
});
123+
return Err(make_type_error!("Array length must be an integer"));
128124
}
129125
if n < 0.0 {
130-
return Err(JSError::TypeError {
131-
message: "Array length cannot be negative".to_string(),
132-
});
126+
return Err(make_type_error!("Array length cannot be negative"));
133127
}
134128
if n > u32::MAX as f64 {
135-
return Err(JSError::TypeError {
136-
message: "Array length too large".to_string(),
137-
});
129+
return Err(make_type_error!("Array length too large"));
138130
}
139131
// Array(length) - create array with specified length
140132
let array_obj = Rc::new(RefCell::new(JSObjectData::new()));

src/js_class.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ pub(crate) fn get_class_proto_obj(class_obj: &JSObjectDataPtr) -> Result<JSObjec
4949
{
5050
return Ok(proto_obj.clone());
5151
}
52-
Err(JSError::TypeError {
53-
message: "Prototype object not found".to_string(),
54-
})
52+
Err(make_type_error!("Prototype object not found"))
5553
}
5654

5755
pub(crate) fn evaluate_this(env: &JSObjectDataPtr) -> Result<Value, JSError> {
@@ -203,9 +201,7 @@ pub(crate) fn evaluate_new(env: &JSObjectDataPtr, constructor: &Expr, args: &[Ex
203201
}
204202
}
205203

206-
Err(JSError::TypeError {
207-
message: "Constructor is not callable".to_string(),
208-
})
204+
Err(make_type_error!("Constructor is not callable"))
209205
}
210206

211207
pub(crate) fn create_class_object(

src/js_console.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ pub fn handle_console_method(method: &str, args: &[Expr], env: &JSObjectDataPtr)
1616
match method {
1717
"log" => {
1818
// console.log call
19-
for arg in args {
19+
let count = args.len();
20+
for (i, arg) in args.iter().enumerate() {
2021
let arg_val = evaluate_expr(env, arg)?;
2122
match arg_val {
2223
Value::Number(n) => print!("{}", n),
@@ -105,6 +106,9 @@ pub fn handle_console_method(method: &str, args: &[Expr], env: &JSObjectDataPtr)
105106
Value::Promise(_) => print!("[object Promise]"),
106107
Value::Symbol(_) => print!("[object Symbol]"),
107108
}
109+
if i < count - 1 {
110+
print!(" ");
111+
}
108112
}
109113
println!();
110114
Ok(Value::Undefined)

0 commit comments

Comments
 (0)