style: 减少自动格式化换行

This commit is contained in:
2026-06-09 00:53:46 +08:00
parent 380bef9ad1
commit f77499e5cd
86 changed files with 672 additions and 2645 deletions
+5 -23
View File
@@ -77,11 +77,7 @@ fn make_kernel() -> Arc<OxideKernel> {
} }
fn make_pool(kernel: &Arc<OxideKernel>) -> Arc<VmPool> { fn make_pool(kernel: &Arc<OxideKernel>) -> Arc<VmPool> {
VmPool::new( VmPool::new(Arc::clone(kernel), kernel.config().min_pool_size, kernel.config().max_pool_size)
Arc::clone(kernel),
kernel.config().min_pool_size,
kernel.config().max_pool_size,
)
} }
fn eval(code: &str, kernel: &Arc<OxideKernel>, pool: &Arc<VmPool>) -> ExitCode { fn eval(code: &str, kernel: &Arc<OxideKernel>, pool: &Arc<VmPool>) -> ExitCode {
@@ -108,11 +104,7 @@ fn eval(code: &str, kernel: &Arc<OxideKernel>, pool: &Arc<VmPool>) -> ExitCode {
let mut guard = pool.spawn(); let mut guard = pool.spawn();
match guard.vm_mut().run(&module) { match guard.vm_mut().run(&module) {
Ok(result) => { Ok(result) => {
format_result( format_result(kernel.string_forge().as_ref(), kernel.shape_forge().as_ref(), result);
kernel.string_forge().as_ref(),
kernel.shape_forge().as_ref(),
result,
);
ExitCode::SUCCESS ExitCode::SUCCESS
} }
Err(err) => { Err(err) => {
@@ -218,10 +210,7 @@ fn compile(expr: Option<String>, file: Option<String>) -> ExitCode {
} }
} }
} else { } else {
eprintln!( eprintln!("{}", Red.paint("compile requires -e '<code>' or a file path"));
"{}",
Red.paint("compile requires -e '<code>' or a file path")
);
return ExitCode::FAILURE; return ExitCode::FAILURE;
}; };
@@ -279,11 +268,7 @@ fn repl() -> ExitCode {
let mut input_buf = String::new(); let mut input_buf = String::new();
loop { loop {
let prompt = if input_buf.is_empty() { let prompt = if input_buf.is_empty() { "oxide> " } else { "...> " };
"oxide> "
} else {
"...> "
};
match rl.readline(prompt) { match rl.readline(prompt) {
Ok(line) => { Ok(line) => {
let trimmed = line.trim(); let trimmed = line.trim();
@@ -336,9 +321,6 @@ fn repl() -> ExitCode {
} }
fn not_implemented(command: &str) -> ExitCode { fn not_implemented(command: &str) -> ExitCode {
use ansi_term::Colour::Yellow; use ansi_term::Colour::Yellow;
eprintln!( eprintln!("{}", Yellow.paint(format!("'{command}' is not yet implemented")));
"{}",
Yellow.paint(format!("'{command}' is not yet implemented"))
);
ExitCode::SUCCESS ExitCode::SUCCESS
} }
+3 -12
View File
@@ -19,15 +19,9 @@ fn eval_syntax_error() {
.output() .output()
.expect("failed to run oxide eval"); .expect("failed to run oxide eval");
assert!( assert!(!output.status.success(), "syntax error should exit non-zero");
!output.status.success(),
"syntax error should exit non-zero"
);
let stderr = String::from_utf8_lossy(&output.stderr); let stderr = String::from_utf8_lossy(&output.stderr);
assert!( assert!(!stderr.is_empty(), "syntax error should produce stderr output");
!stderr.is_empty(),
"syntax error should produce stderr output"
);
} }
#[test] #[test]
@@ -51,8 +45,5 @@ fn bench_not_implemented() {
.expect("failed to run oxide bench"); .expect("failed to run oxide bench");
let stderr = String::from_utf8_lossy(&output.stderr); let stderr = String::from_utf8_lossy(&output.stderr);
assert!( assert!(stderr.contains("not yet implemented"), "bench should print not-implemented message");
stderr.contains("not yet implemented"),
"bench should print not-implemented message"
);
} }
+4 -21
View File
@@ -97,12 +97,7 @@ impl CompileCtx {
} }
pub(crate) fn emit_load_const(&mut self, reg: u8, idx: u16) { pub(crate) fn emit_load_const(&mut self, reg: u8, idx: u16) {
self.emit(opcode::encode( self.emit(opcode::encode(OpCode::LOAD_CONST, reg, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8));
OpCode::LOAD_CONST,
reg,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
} }
pub(crate) fn alloc_reg(&mut self) -> u8 { pub(crate) fn alloc_reg(&mut self) -> u8 {
@@ -153,21 +148,13 @@ impl CompileCtx {
} }
pub(crate) fn declare( pub(crate) fn declare(
&mut self, &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
name: &str,
reg: u8,
kind: VariableDeclarationKind,
is_const: bool,
) -> Result<(), String> { ) -> Result<(), String> {
self.symbols.declare(name, reg, kind, is_const) self.symbols.declare(name, reg, kind, is_const)
} }
pub(crate) fn declare_initialized( pub(crate) fn declare_initialized(
&mut self, &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
name: &str,
reg: u8,
kind: VariableDeclarationKind,
is_const: bool,
) -> Result<(), String> { ) -> Result<(), String> {
self.symbols.declare_initialized(name, reg, kind, is_const) self.symbols.declare_initialized(name, reg, kind, is_const)
} }
@@ -277,11 +264,7 @@ impl Compiler {
/// When `is_expression_body` is true (arrow function with expression body), /// When `is_expression_body` is true (arrow function with expression body),
/// the last expression's value is returned instead of undefined. /// the last expression's value is returned instead of undefined.
pub(crate) fn compile_function_body<'a>( pub(crate) fn compile_function_body<'a>(
&self, &self, param_names: &[String], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, is_expression_body: bool,
param_names: &[String],
body_stmts: &[Statement<'a>],
parent_ctx: &CompileCtx,
is_expression_body: bool,
) -> Result<CompiledModule, String> { ) -> Result<CompiledModule, String> {
let mut ctx = CompileCtx::new(); let mut ctx = CompileCtx::new();
+5 -9
View File
@@ -1,6 +1,6 @@
use oxide_parser::{ use oxide_parser::{
AssignmentOperator, Expression, ForStatementInit, SimpleAssignmentTarget, Statement, AssignmentOperator, Expression, ForStatementInit, SimpleAssignmentTarget, Statement, UnaryOperator,
UnaryOperator, VariableDeclarationKind, VariableDeclarationKind,
}; };
use crate::compiler::{is_side_effect_free, CompileCtx, Compiler, Label}; use crate::compiler::{is_side_effect_free, CompileCtx, Compiler, Label};
@@ -192,8 +192,7 @@ impl Compiler {
// Hoisting: declare function name as initialized // Hoisting: declare function name as initialized
let func_reg = ctx.alloc_reg(); let func_reg = ctx.alloc_reg();
let _ = let _ = ctx.declare_initialized(&name, func_reg, VariableDeclarationKind::Var, false);
ctx.declare_initialized(&name, func_reg, VariableDeclarationKind::Var, false);
// Body is compiled in the emit pass only. // Body is compiled in the emit pass only.
// FD emits LOAD_CONST(BytecodeFunc) + STORE_VAR // FD emits LOAD_CONST(BytecodeFunc) + STORE_VAR
@@ -326,8 +325,7 @@ impl Compiler {
ctx.projected_pc += 1; // LOAD_VAR result <- regs[0] ctx.projected_pc += 1; // LOAD_VAR result <- regs[0]
} }
Expression::AssignmentExpression(assign) => { Expression::AssignmentExpression(assign) => {
if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left {
{
self.count_expression(&member.object, ctx); self.count_expression(&member.object, ctx);
self.count_expression(&assign.right, ctx); self.count_expression(&assign.right, ctx);
if assign.operator != AssignmentOperator::Assign { if assign.operator != AssignmentOperator::Assign {
@@ -338,9 +336,7 @@ impl Compiler {
ctx.projected_pc += 1; // LOAD_CONST key ctx.projected_pc += 1; // LOAD_CONST key
ctx.projected_pc += 1; // IC_SET_PROP or COMPOUND_MEMBER_* ctx.projected_pc += 1; // IC_SET_PROP or COMPOUND_MEMBER_*
ctx.projected_pc += 3; // 3 IC ext words ctx.projected_pc += 3; // 3 IC ext words
} else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left {
&assign.left
{
self.count_expression(&member.object, ctx); self.count_expression(&member.object, ctx);
self.count_expression(&member.expression, ctx); self.count_expression(&member.expression, ctx);
self.count_expression(&assign.right, ctx); self.count_expression(&assign.right, ctx);
+46 -203
View File
@@ -1,23 +1,17 @@
use crate::compiler::Label; use crate::compiler::Label;
use crate::opcode::{self, OpCode}; use crate::opcode::{self, OpCode};
use oxide_parser::{ use oxide_parser::{
AssignmentOperator, Expression, ForStatementInit, ForStatementLeft, SimpleAssignmentTarget, AssignmentOperator, Expression, ForStatementInit, ForStatementLeft, SimpleAssignmentTarget, Statement,
Statement, UnaryOperator, UpdateOperator, VariableDeclarationKind, UnaryOperator, UpdateOperator, VariableDeclarationKind,
}; };
use crate::compiler::{is_int_literal, is_side_effect_free, BinaryOperator, CompileCtx, Compiler}; use crate::compiler::{is_int_literal, is_side_effect_free, BinaryOperator, CompileCtx, Compiler};
use crate::module::Constant; use crate::module::Constant;
impl Compiler { impl Compiler {
pub(crate) fn emit_statement( pub(crate) fn emit_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u8>, String> {
&self,
stmt: &Statement,
ctx: &mut CompileCtx,
) -> Result<Option<u8>, String> {
match stmt { match stmt {
Statement::ExpressionStatement(es) => { Statement::ExpressionStatement(es) => Ok(Some(self.emit_expression(&es.expression, ctx)?)),
Ok(Some(self.emit_expression(&es.expression, ctx)?))
}
Statement::VariableDeclaration(decl) => { Statement::VariableDeclaration(decl) => {
let mut r = None; let mut r = None;
for d in &decl.declarations { for d in &decl.declarations {
@@ -34,12 +28,7 @@ impl Compiler {
if let Some(init) = &d.init { if let Some(init) = &d.init {
let val_reg = self.emit_expression(init, ctx)?; let val_reg = self.emit_expression(init, ctx)?;
let const_flag = if is_const { 1 } else { 0 }; let const_flag = if is_const { 1 } else { 0 };
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, const_flag));
OpCode::STORE_VAR,
var_reg,
val_reg,
const_flag,
));
ctx.init_var(name); ctx.init_var(name);
// Name inference (D-04): if the initializer is an arrow function, // Name inference (D-04): if the initializer is an arrow function,
// set the compiled sub_module's function_name. // set the compiled sub_module's function_name.
@@ -176,18 +165,11 @@ impl Compiler {
} else if let ForStatementInit::VariableDeclaration(decl) = init { } else if let ForStatementInit::VariableDeclaration(decl) = init {
for d in &decl.declarations { for d in &decl.declarations {
let name = match &d.id { let name = match &d.id {
oxide_parser::BindingPattern::BindingIdentifier(bi) => { oxide_parser::BindingPattern::BindingIdentifier(bi) => bi.name.as_str(),
bi.name.as_str()
}
_ => return Err("destructuring not supported".into()), _ => return Err("destructuring not supported".into()),
}; };
let var_reg = ctx.alloc_reg(); let var_reg = ctx.alloc_reg();
ctx.declare( ctx.declare(name, var_reg, decl.kind, matches!(decl.kind, VariableDeclarationKind::Const))?;
name,
var_reg,
decl.kind,
matches!(decl.kind, VariableDeclarationKind::Const),
)?;
if let Some(init_expr) = &d.init { if let Some(init_expr) = &d.init {
let val_reg = self.emit_expression(init_expr, ctx)?; let val_reg = self.emit_expression(init_expr, ctx)?;
ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, 0)); ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, 0));
@@ -243,18 +225,11 @@ impl Compiler {
ForStatementLeft::VariableDeclaration(decl) => { ForStatementLeft::VariableDeclaration(decl) => {
for d in &decl.declarations { for d in &decl.declarations {
let name = match &d.id { let name = match &d.id {
oxide_parser::BindingPattern::BindingIdentifier(bi) => { oxide_parser::BindingPattern::BindingIdentifier(bi) => bi.name.as_str(),
bi.name.as_str()
}
_ => return Err("destructuring not supported".into()), _ => return Err("destructuring not supported".into()),
}; };
let var_reg = ctx.alloc_reg(); let var_reg = ctx.alloc_reg();
ctx.declare( ctx.declare(name, var_reg, decl.kind, matches!(decl.kind, VariableDeclarationKind::Const))?;
name,
var_reg,
decl.kind,
matches!(decl.kind, VariableDeclarationKind::Const),
)?;
ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, key_reg, 0)); ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, key_reg, 0));
ctx.init_var(name); ctx.init_var(name);
} }
@@ -321,9 +296,7 @@ impl Compiler {
let break_label = if let Some(sw_label) = ctx.current_switch() { let break_label = if let Some(sw_label) = ctx.current_switch() {
*sw_label *sw_label
} else { } else {
let (bl, _) = ctx let (bl, _) = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
.current_loop()
.ok_or("break outside switch or loop".to_string())?;
*bl *bl
}; };
let break_pos = ctx.resolve_label(break_label)?; let break_pos = ctx.resolve_label(break_label)?;
@@ -332,9 +305,7 @@ impl Compiler {
Ok(None) Ok(None)
} }
Statement::ContinueStatement(_) => { Statement::ContinueStatement(_) => {
let (_, continue_label) = ctx let (_, continue_label) = ctx.current_loop().ok_or("continue outside loop".to_string())?;
.current_loop()
.ok_or("continue outside loop".to_string())?;
let continue_pos = ctx.resolve_label(*continue_label)?; let continue_pos = ctx.resolve_label(*continue_label)?;
let offset = (continue_pos as isize) - (ctx.bytecode.len() as isize); let offset = (continue_pos as isize) - (ctx.bytecode.len() as isize);
ctx.emit(opcode::encode_jmp(offset as i16)); ctx.emit(opcode::encode_jmp(offset as i16));
@@ -357,15 +328,10 @@ impl Compiler {
} }
// Extract body statements (pass by reference) // Extract body statements (pass by reference)
let body_stmts: &[Statement] = if let Some(body) = &fd.body { let body_stmts: &[Statement] = if let Some(body) = &fd.body { &body.statements } else { &[] };
&body.statements
} else {
&[]
};
// Compile body into sub-module // Compile body into sub-module
let mut sub_module = let mut sub_module = self.compile_function_body(&param_names, body_stmts, ctx, false)?;
self.compile_function_body(&param_names, body_stmts, ctx, false)?;
sub_module.function_name = Some(name.clone()); sub_module.function_name = Some(name.clone());
ctx.sub_modules.push(sub_module); ctx.sub_modules.push(sub_module);
// 1-indexed: 0 = no sub_module (sentinel) // 1-indexed: 0 = no sub_module (sentinel)
@@ -414,12 +380,7 @@ impl Compiler {
last_try_result = Some(r); last_try_result = Some(r);
} }
} }
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::LOAD_VAR, result_reg, last_try_result.unwrap_or(result_reg), 0));
OpCode::LOAD_VAR,
result_reg,
last_try_result.unwrap_or(result_reg),
0,
));
if has_catch { if has_catch {
ctx.emit(opcode::encode(OpCode::TRY_END, 0, 0, 0)); ctx.emit(opcode::encode(OpCode::TRY_END, 0, 0, 0));
@@ -446,14 +407,8 @@ impl Compiler {
ctx.push_scope(); ctx.push_scope();
if let Some(param) = &catch.param { if let Some(param) = &catch.param {
let catch_reg = ctx.alloc_reg(); let catch_reg = ctx.alloc_reg();
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &param.pattern if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &param.pattern {
{ ctx.declare_initialized(bi.name.as_str(), catch_reg, VariableDeclarationKind::Let, false)?;
ctx.declare_initialized(
bi.name.as_str(),
catch_reg,
VariableDeclarationKind::Let,
false,
)?;
ctx.emit(opcode::encode(OpCode::STORE_VAR, catch_reg, 0, 0)); ctx.emit(opcode::encode(OpCode::STORE_VAR, catch_reg, 0, 0));
} }
} }
@@ -463,12 +418,7 @@ impl Compiler {
last_catch_result = Some(r); last_catch_result = Some(r);
} }
} }
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::LOAD_VAR, result_reg, last_catch_result.unwrap_or(result_reg), 0));
OpCode::LOAD_VAR,
result_reg,
last_catch_result.unwrap_or(result_reg),
0,
));
ctx.pop_scope(); ctx.pop_scope();
} }
@@ -513,11 +463,7 @@ impl Compiler {
} }
} }
pub(crate) fn emit_expression( pub(crate) fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u8, String> {
&self,
expr: &Expression,
ctx: &mut CompileCtx,
) -> Result<u8, String> {
match expr { match expr {
Expression::NumericLiteral(n) => { Expression::NumericLiteral(n) => {
let idx = if is_int_literal(n.value) { let idx = if is_int_literal(n.value) {
@@ -607,8 +553,7 @@ impl Compiler {
Expression::StaticMemberExpression(member) => { Expression::StaticMemberExpression(member) => {
let obj_reg = self.emit_expression(&member.object, ctx)?; let obj_reg = self.emit_expression(&member.object, ctx)?;
let prop_name = member.property.name.as_str(); let prop_name = member.property.name.as_str();
let const_idx = let const_idx = ctx.add_constant(Constant::String(prop_name.to_string()));
ctx.add_constant(Constant::String(prop_name.to_string()));
let r = ctx.alloc_reg(); let r = ctx.alloc_reg();
ctx.emit(opcode::encode(OpCode::DELETE_PROP_STATIC, r, obj_reg, 0)); ctx.emit(opcode::encode(OpCode::DELETE_PROP_STATIC, r, obj_reg, 0));
ctx.emit(const_idx as u32); ctx.emit(const_idx as u32);
@@ -618,12 +563,7 @@ impl Compiler {
let obj_reg = self.emit_expression(&member.object, ctx)?; let obj_reg = self.emit_expression(&member.object, ctx)?;
let key_reg = self.emit_expression(&member.expression, ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?;
let r = ctx.alloc_reg(); let r = ctx.alloc_reg();
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::DELETE_PROP_DYNAMIC, r, obj_reg, key_reg));
OpCode::DELETE_PROP_DYNAMIC,
r,
obj_reg,
key_reg,
));
Ok(r) Ok(r)
} }
_ => Err("invalid delete target".into()), _ => Err("invalid delete target".into()),
@@ -709,12 +649,7 @@ impl Compiler {
let prop_name = member.property.name.as_str(); let prop_name = member.property.name.as_str();
let idx = ctx.add_constant(Constant::String(prop_name.to_string())); let idx = ctx.add_constant(Constant::String(prop_name.to_string()));
let key_reg = ctx.alloc_reg(); let key_reg = ctx.alloc_reg();
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::LOAD_CONST, key_reg, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8));
OpCode::LOAD_CONST,
key_reg,
(idx & 0xFF) as u8,
((idx >> 8) & 0xFF) as u8,
));
ctx.emit(opcode::encode(OpCode::IC_GET_PROP, 0, obj_reg, key_reg)); ctx.emit(opcode::encode(OpCode::IC_GET_PROP, 0, obj_reg, key_reg));
ctx.emit(0); ctx.emit(0);
ctx.emit(0); ctx.emit(0);
@@ -725,12 +660,7 @@ impl Compiler {
let obj_reg = self.emit_expression(&member.object, ctx)?; let obj_reg = self.emit_expression(&member.object, ctx)?;
let key_reg = self.emit_expression(&member.expression, ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?;
let r = ctx.alloc_reg(); let r = ctx.alloc_reg();
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::GET_PROP_DYNAMIC, obj_reg, key_reg, r));
OpCode::GET_PROP_DYNAMIC,
obj_reg,
key_reg,
r,
));
Ok(r) Ok(r)
} }
Expression::ObjectExpression(obj) => { Expression::ObjectExpression(obj) => {
@@ -741,9 +671,7 @@ impl Compiler {
return Err("spread properties not yet supported".into()); return Err("spread properties not yet supported".into());
}; };
let prop_name = match &p.key { let prop_name = match &p.key {
oxide_parser::PropertyKey::StaticIdentifier(ident) => { oxide_parser::PropertyKey::StaticIdentifier(ident) => ident.name.as_str().to_string(),
ident.name.as_str().to_string()
}
oxide_parser::PropertyKey::StringLiteral(s) => s.value.to_string(), oxide_parser::PropertyKey::StringLiteral(s) => s.value.to_string(),
_ => return Err("unsupported object property key type".into()), _ => return Err("unsupported object property key type".into()),
}; };
@@ -765,12 +693,7 @@ impl Compiler {
Expression::ArrayExpression(arr) => { Expression::ArrayExpression(arr) => {
let arr_reg = ctx.alloc_reg(); let arr_reg = ctx.alloc_reg();
let n = arr.elements.len() as u16; let n = arr.elements.len() as u16;
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::NEW_ARRAY, arr_reg, (n & 0xFF) as u8, ((n >> 8) & 0xFF) as u8));
OpCode::NEW_ARRAY,
arr_reg,
(n & 0xFF) as u8,
((n >> 8) & 0xFF) as u8,
));
for (i, elem) in arr.elements.iter().enumerate() { for (i, elem) in arr.elements.iter().enumerate() {
let Some(e) = elem.as_expression() else { let Some(e) = elem.as_expression() else {
return Err("spread not supported".into()); return Err("spread not supported".into());
@@ -784,8 +707,7 @@ impl Compiler {
Ok(arr_reg) Ok(arr_reg)
} }
Expression::AssignmentExpression(assign) => { Expression::AssignmentExpression(assign) => {
if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left {
{
let obj_reg = self.emit_expression(&member.object, ctx)?; let obj_reg = self.emit_expression(&member.object, ctx)?;
let val_reg = self.emit_expression(&assign.right, ctx)?; let val_reg = self.emit_expression(&assign.right, ctx)?;
let prop_name = member.property.name.as_str(); let prop_name = member.property.name.as_str();
@@ -808,33 +730,19 @@ impl Compiler {
ctx.emit(0); ctx.emit(0);
Ok(val_reg) Ok(val_reg)
} else { } else {
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::IC_SET_PROP, obj_reg, val_reg, key_reg));
OpCode::IC_SET_PROP,
obj_reg,
val_reg,
key_reg,
));
ctx.emit(0); ctx.emit(0);
ctx.emit(0); ctx.emit(0);
ctx.emit(0); ctx.emit(0);
Ok(val_reg) Ok(val_reg)
} }
} else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left {
&assign.left
{
let obj_reg = self.emit_expression(&member.object, ctx)?; let obj_reg = self.emit_expression(&member.object, ctx)?;
let key_reg = self.emit_expression(&member.expression, ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?;
let val_reg = self.emit_expression(&assign.right, ctx)?; let val_reg = self.emit_expression(&assign.right, ctx)?;
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::SET_PROP_DYNAMIC, obj_reg, key_reg, val_reg));
OpCode::SET_PROP_DYNAMIC,
obj_reg,
key_reg,
val_reg,
));
Ok(val_reg) Ok(val_reg)
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = } else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = &assign.left {
&assign.left
{
if assign.operator != AssignmentOperator::Assign { if assign.operator != AssignmentOperator::Assign {
if assign.operator == AssignmentOperator::Addition if assign.operator == AssignmentOperator::Addition
|| assign.operator == AssignmentOperator::Subtraction || assign.operator == AssignmentOperator::Subtraction
@@ -858,10 +766,7 @@ impl Compiler {
ctx.emit(opcode::encode(op, var_reg, rhs, 0)); ctx.emit(opcode::encode(op, var_reg, rhs, 0));
Ok(var_reg) Ok(var_reg)
} else { } else {
Err(format!( Err(format!("compound assignment operator {:?} not supported", assign.operator))
"compound assignment operator {:?} not supported",
assign.operator
))
} }
} else { } else {
let val_reg = self.emit_expression(&assign.right, ctx)?; let val_reg = self.emit_expression(&assign.right, ctx)?;
@@ -869,12 +774,7 @@ impl Compiler {
let var_reg = ctx.lookup_or_global(name); let var_reg = ctx.lookup_or_global(name);
let is_const = ctx.lookup_const_flag(name); let is_const = ctx.lookup_const_flag(name);
let const_flag = if is_const { 1 } else { 0 }; let const_flag = if is_const { 1 } else { 0 };
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, const_flag));
OpCode::STORE_VAR,
var_reg,
val_reg,
const_flag,
));
Ok(val_reg) Ok(val_reg)
} }
} else { } else {
@@ -953,12 +853,7 @@ impl Compiler {
let quasi_const_idxs: Vec<u16> = quasis let quasi_const_idxs: Vec<u16> = quasis
.iter() .iter()
.map(|q| { .map(|q| {
let s = q let s = q.value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default();
.value
.cooked
.as_ref()
.map(|c| c.to_string())
.unwrap_or_default();
ctx.add_constant(Constant::String(s)) ctx.add_constant(Constant::String(s))
}) })
.collect(); .collect();
@@ -1000,41 +895,21 @@ impl Compiler {
// 2. Build cooked strings array (into a temp register) // 2. Build cooked strings array (into a temp register)
let cooked_temp = ctx.alloc_reg(); let cooked_temp = ctx.alloc_reg();
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::NEW_ARRAY, cooked_temp, quasis.len() as u8, 0));
OpCode::NEW_ARRAY,
cooked_temp,
quasis.len() as u8,
0,
));
for (i, quasi) in quasis.iter().enumerate() { for (i, quasi) in quasis.iter().enumerate() {
let s = quasi let s = quasi.value.cooked.as_ref().map(|c| c.to_string()).unwrap_or_default();
.value
.cooked
.as_ref()
.map(|c| c.to_string())
.unwrap_or_default();
let const_idx = ctx.add_constant(Constant::String(s)); let const_idx = ctx.add_constant(Constant::String(s));
let str_reg = ctx.alloc_reg(); let str_reg = ctx.alloc_reg();
ctx.emit_load_const(str_reg, const_idx); ctx.emit_load_const(str_reg, const_idx);
let idx_const = ctx.add_constant(Constant::Int(i as i32)); let idx_const = ctx.add_constant(Constant::Int(i as i32));
let idx_reg = ctx.alloc_reg(); let idx_reg = ctx.alloc_reg();
ctx.emit_load_const(idx_reg, idx_const); ctx.emit_load_const(idx_reg, idx_const);
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::SET_ELEM, cooked_temp, idx_reg, str_reg));
OpCode::SET_ELEM,
cooked_temp,
idx_reg,
str_reg,
));
} }
// 3. Build raw strings array (into a temp register) // 3. Build raw strings array (into a temp register)
let raw_temp = ctx.alloc_reg(); let raw_temp = ctx.alloc_reg();
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::NEW_ARRAY, raw_temp, quasis.len() as u8, 0));
OpCode::NEW_ARRAY,
raw_temp,
quasis.len() as u8,
0,
));
for (i, quasi) in quasis.iter().enumerate() { for (i, quasi) in quasis.iter().enumerate() {
let raw = quasi.value.raw.to_string(); let raw = quasi.value.raw.to_string();
let const_idx = ctx.add_constant(Constant::String(raw)); let const_idx = ctx.add_constant(Constant::String(raw));
@@ -1061,12 +936,7 @@ impl Compiler {
} }
// Copy temps to consecutive slots using LOAD_VAR (register-to-register move) // Copy temps to consecutive slots using LOAD_VAR (register-to-register move)
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::LOAD_VAR, cooked_slot, cooked_temp, 0));
OpCode::LOAD_VAR,
cooked_slot,
cooked_temp,
0,
));
ctx.emit(opcode::encode(OpCode::LOAD_VAR, raw_slot, raw_temp, 0)); ctx.emit(opcode::encode(OpCode::LOAD_VAR, raw_slot, raw_temp, 0));
for (slot, temp) in expr_slots.iter().zip(expr_temps.iter()) { for (slot, temp) in expr_slots.iter().zip(expr_temps.iter()) {
ctx.emit(opcode::encode(OpCode::LOAD_VAR, *slot, *temp, 0)); ctx.emit(opcode::encode(OpCode::LOAD_VAR, *slot, *temp, 0));
@@ -1079,12 +949,7 @@ impl Compiler {
// 7. Emit CALL(tag, undefined, cooked_slot) // 7. Emit CALL(tag, undefined, cooked_slot)
let arg_count = 2 + expressions.len(); let arg_count = 2 + expressions.len();
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::CALL, tag_reg, undef_reg, cooked_slot));
OpCode::CALL,
tag_reg,
undef_reg,
cooked_slot,
));
ctx.emit(arg_count as u32); ctx.emit(arg_count as u32);
// 8. Result from regs[0] // 8. Result from regs[0]
@@ -1113,8 +978,7 @@ impl Compiler {
let body_stmts = &arrow.body.statements; let body_stmts = &arrow.body.statements;
let is_expr_body = arrow.expression; let is_expr_body = arrow.expression;
let mut sub_module = let mut sub_module = self.compile_function_body(&param_names, body_stmts, ctx, is_expr_body)?;
self.compile_function_body(&param_names, body_stmts, ctx, is_expr_body)?;
sub_module.is_arrow = true; sub_module.is_arrow = true;
ctx.sub_modules.push(sub_module); ctx.sub_modules.push(sub_module);
@@ -1135,14 +999,9 @@ impl Compiler {
} }
} }
let body_stmts: &[Statement] = if let Some(body) = &fe.body { let body_stmts: &[Statement] = if let Some(body) = &fe.body { &body.statements } else { &[] };
&body.statements
} else {
&[]
};
let mut sub_module = let mut sub_module = self.compile_function_body(&param_names, body_stmts, ctx, false)?;
self.compile_function_body(&param_names, body_stmts, ctx, false)?;
if let Some(id) = &fe.id { if let Some(id) = &fe.id {
sub_module.function_name = Some(id.name.to_string()); sub_module.function_name = Some(id.name.to_string());
} }
@@ -1163,18 +1022,9 @@ impl Compiler {
arg_regs.push(self.emit_expression(expr, ctx)?); arg_regs.push(self.emit_expression(expr, ctx)?);
} }
} }
let first_arg_reg = if arg_regs.is_empty() { let first_arg_reg = if arg_regs.is_empty() { 0u8 } else { arg_regs[0] };
0u8
} else {
arg_regs[0]
};
let r = ctx.alloc_reg(); let r = ctx.alloc_reg();
ctx.emit(opcode::encode( ctx.emit(opcode::encode(OpCode::NEW_EXPRESSION, r, constructor_reg, first_arg_reg));
OpCode::NEW_EXPRESSION,
r,
constructor_reg,
first_arg_reg,
));
ctx.emit(arg_regs.len() as u32); ctx.emit(arg_regs.len() as u32);
Ok(r) Ok(r)
} }
@@ -1209,15 +1059,9 @@ impl Compiler {
arg_regs.push(self.emit_expression(expr, ctx)?); arg_regs.push(self.emit_expression(expr, ctx)?);
} }
} }
let first_arg_reg = if arg_regs.is_empty() { let first_arg_reg = if arg_regs.is_empty() { 0u8 } else { arg_regs[0] };
0u8
} else {
arg_regs[0]
};
let op = match &call.callee { let op = match &call.callee {
Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => { Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE,
OpCode::CALL_NATIVE
}
Expression::StaticMemberExpression(m) => { Expression::StaticMemberExpression(m) => {
if let Expression::Identifier(ident) = &m.object { if let Expression::Identifier(ident) = &m.object {
if ctx.is_builtin(ident.name.as_str()) { if ctx.is_builtin(ident.name.as_str()) {
@@ -1251,8 +1095,7 @@ impl Compiler {
let last_slash = raw_str.rfind('/').unwrap_or(raw_str.len() - 1); let last_slash = raw_str.rfind('/').unwrap_or(raw_str.len() - 1);
let pattern = &raw_str[1..last_slash]; let pattern = &raw_str[1..last_slash];
let flags = &raw_str[last_slash + 1..]; let flags = &raw_str[last_slash + 1..];
let ci = ctx let ci = ctx.add_constant(Constant::RegExp(pattern.to_string(), flags.to_string()));
.add_constant(Constant::RegExp(pattern.to_string(), flags.to_string()));
let r = ctx.alloc_reg(); let r = ctx.alloc_reg();
ctx.emit_load_const(r, ci); ctx.emit_load_const(r, ci);
Ok(r) Ok(r)
+1 -3
View File
@@ -79,9 +79,7 @@ impl fmt::Display for CompiledModule {
writeln!(f, "; constants:")?; writeln!(f, "; constants:")?;
for (i, c) in self.constants.iter().enumerate() { for (i, c) in self.constants.iter().enumerate() {
match c { match c {
Constant::BytecodeFunc(idx) => { Constant::BytecodeFunc(idx) => writeln!(f, "; [{i}] = BytecodeFunc(sub_module[{idx}])")?,
writeln!(f, "; [{i}] = BytecodeFunc(sub_module[{idx}])")?
}
other => writeln!(f, "; [{i}] = {other:?}")?, other => writeln!(f, "; [{i}] = {other:?}")?,
} }
} }
+9 -26
View File
@@ -47,10 +47,7 @@ impl SymbolTable {
} }
pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) { pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) {
self.scopes.push(Scope { self.scopes.push(Scope { bindings: HashMap::new(), kind });
bindings: HashMap::new(),
kind,
});
} }
pub fn pop_scope(&mut self) { pub fn pop_scope(&mut self) {
@@ -69,11 +66,7 @@ impl SymbolTable {
} }
pub fn declare( pub fn declare(
&mut self, &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
name: &str,
reg: u8,
kind: VariableDeclarationKind,
is_const: bool,
) -> Result<(), String> { ) -> Result<(), String> {
let target_idx = if matches!(kind, VariableDeclarationKind::Var) { let target_idx = if matches!(kind, VariableDeclarationKind::Var) {
self.find_var_target_scope() self.find_var_target_scope()
@@ -138,11 +131,7 @@ impl SymbolTable {
} }
pub fn declare_initialized( pub fn declare_initialized(
&mut self, &mut self, name: &str, reg: u8, kind: VariableDeclarationKind, is_const: bool,
name: &str,
reg: u8,
kind: VariableDeclarationKind,
is_const: bool,
) -> Result<(), String> { ) -> Result<(), String> {
let target_idx = if matches!(kind, VariableDeclarationKind::Var) { let target_idx = if matches!(kind, VariableDeclarationKind::Var) {
self.find_var_target_scope() self.find_var_target_scope()
@@ -167,14 +156,11 @@ impl SymbolTable {
} }
pub fn pre_register_global(&mut self, name: &str, reg: u8) { pub fn pre_register_global(&mut self, name: &str, reg: u8) {
self.scopes[0] self.scopes[0].bindings.entry(name.to_string()).or_insert(Binding {
.bindings reg,
.entry(name.to_string()) initialized: true,
.or_insert(Binding { is_const: false,
reg, });
initialized: true,
is_const: false,
});
} }
pub fn init_var(&mut self, name: &str) { pub fn init_var(&mut self, name: &str) {
@@ -220,10 +206,7 @@ mod tests {
fn tdz_error() { fn tdz_error() {
let mut st = SymbolTable::new(); let mut st = SymbolTable::new();
st.declare("x", 0, l(), false).unwrap(); st.declare("x", 0, l(), false).unwrap();
assert!(st assert!(st.lookup("x").unwrap_err().contains("before initialization"));
.lookup("x")
.unwrap_err()
.contains("before initialization"));
} }
#[test] #[test]
@@ -27,29 +27,20 @@ fn hash_arrow_expression_body() {
fn hash_arrow_different_params() { fn hash_arrow_different_params() {
let hash_a = parse_to_hash("var f = (x) => x;"); let hash_a = parse_to_hash("var f = (x) => x;");
let hash_b = parse_to_hash("var f = (x, y) => x;"); let hash_b = parse_to_hash("var f = (x, y) => x;");
assert_ne!( assert_ne!(hash_a, hash_b, "different param counts must produce different hashes");
hash_a, hash_b,
"different param counts must produce different hashes"
);
} }
#[test] #[test]
fn hash_arrow_vs_regular_function() { fn hash_arrow_vs_regular_function() {
let hash_a = parse_to_hash("var f = () => 42;"); let hash_a = parse_to_hash("var f = () => 42;");
let hash_b = parse_to_hash("var f = function() { return 42; };"); let hash_b = parse_to_hash("var f = function() { return 42; };");
assert_ne!( assert_ne!(hash_a, hash_b, "arrow and regular functions must have different hashes");
hash_a, hash_b,
"arrow and regular functions must have different hashes"
);
} }
#[test] #[test]
fn hash_arrow_block_body() { fn hash_arrow_block_body() {
let hash = parse_to_hash("var f = (a, b) => { return a + b; };"); let hash = parse_to_hash("var f = (a, b) => { return a + b; };");
assert!( assert!(hash != 0, "arrow with block body should produce a non-zero hash");
hash != 0,
"arrow with block body should produce a non-zero hash"
);
} }
#[test] #[test]
@@ -57,10 +48,7 @@ fn hash_arrow_same_shape() {
// Same structural shape: different variable name, same arrow expression // Same structural shape: different variable name, same arrow expression
let hash_a = parse_to_hash("var f = () => 1 + 2;"); let hash_a = parse_to_hash("var f = () => 1 + 2;");
let hash_b = parse_to_hash("var g = () => 1 + 2;"); let hash_b = parse_to_hash("var g = () => 1 + 2;");
assert_eq!( assert_eq!(hash_a, hash_b, "same-shaped arrow functions should have same hash");
hash_a, hash_b,
"same-shaped arrow functions should have same hash"
);
} }
// -- Arrow function compilation tests -- // -- Arrow function compilation tests --
@@ -70,20 +58,14 @@ fn compile_arrow_expression_body() {
let module = compile_source("var f = () => 42; f();"); let module = compile_source("var f = () => 42; f();");
// Verify the sub_module for the arrow is marked as is_arrow // Verify the sub_module for the arrow is marked as is_arrow
let arrow_found = module.sub_modules.iter().any(|m| m.is_arrow); let arrow_found = module.sub_modules.iter().any(|m| m.is_arrow);
assert!( assert!(arrow_found, "arrow function sub_module should have is_arrow=true");
arrow_found,
"arrow function sub_module should have is_arrow=true"
);
} }
#[test] #[test]
fn compile_arrow_block_body() { fn compile_arrow_block_body() {
let module = compile_source("var f = (a, b) => { return a + b; }; f(1, 2);"); let module = compile_source("var f = (a, b) => { return a + b; }; f(1, 2);");
let arrow_found = module.sub_modules.iter().any(|m| m.is_arrow); let arrow_found = module.sub_modules.iter().any(|m| m.is_arrow);
assert!( assert!(arrow_found, "arrow with block body should have is_arrow=true");
arrow_found,
"arrow with block body should have is_arrow=true"
);
} }
#[test] #[test]
@@ -94,10 +76,7 @@ fn compile_arrow_name_inference() {
.sub_modules .sub_modules
.iter() .iter()
.any(|m| m.is_arrow && m.function_name.as_deref() == Some("myArrow")); .any(|m| m.is_arrow && m.function_name.as_deref() == Some("myArrow"));
assert!( assert!(named, "arrow function should have function_name='myArrow' from D-04 inference");
named,
"arrow function should have function_name='myArrow' from D-04 inference"
);
} }
#[test] #[test]
@@ -105,20 +84,14 @@ fn compile_regular_function_not_arrow() {
let module = compile_source("var f = function() { return 42; }; f();"); let module = compile_source("var f = function() { return 42; }; f();");
// Regular FunctionExpression should NOT have is_arrow=true // Regular FunctionExpression should NOT have is_arrow=true
let has_arrow = module.sub_modules.iter().any(|m| m.is_arrow); let has_arrow = module.sub_modules.iter().any(|m| m.is_arrow);
assert!( assert!(!has_arrow, "regular function expression should not be marked as arrow");
!has_arrow,
"regular function expression should not be marked as arrow"
);
} }
#[test] #[test]
fn compile_arrow_single_param_no_parens() { fn compile_arrow_single_param_no_parens() {
let module = compile_source("var double = x => x * 2; double(5);"); let module = compile_source("var double = x => x * 2; double(5);");
let arrow_found = module.sub_modules.iter().any(|m| m.is_arrow); let arrow_found = module.sub_modules.iter().any(|m| m.is_arrow);
assert!( assert!(arrow_found, "single-param arrow without parens should compile");
arrow_found,
"single-param arrow without parens should compile"
);
} }
#[test] #[test]
@@ -25,20 +25,14 @@ fn compile_constants() {
let module = compile_source("42"); let module = compile_source("42");
assert!(!module.constants.is_empty()); assert!(!module.constants.is_empty());
assert_eq!( assert_eq!(module.constants[0], oxide_compiler::compiler::Constant::Int(42));
module.constants[0],
oxide_compiler::compiler::Constant::Int(42)
);
} }
#[test] #[test]
fn compile_negation() { fn compile_negation() {
let module = compile_source("-5"); let module = compile_source("-5");
let has_neg = module let has_neg = module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEG);
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::NEG);
assert!(has_neg); assert!(has_neg);
} }
@@ -65,10 +59,7 @@ fn compile_binary_ops() {
for (src, expected_op) in tests { for (src, expected_op) in tests {
let module = compile_source(src); let module = compile_source(src);
let has_op = module let has_op = module.bytecode.iter().any(|&i| opcode::opcode(i) == expected_op);
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == expected_op);
assert!(has_op, "expected {:?} in '{src}'", expected_op); assert!(has_op, "expected {:?} in '{src}'", expected_op);
} }
} }
@@ -77,10 +68,7 @@ fn compile_binary_ops() {
fn compile_ternary_emits_jumps() { fn compile_ternary_emits_jumps() {
let module = compile_source("true ? 1 : 2"); let module = compile_source("true ? 1 : 2");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
"ternary should contain JMP_IF_FALSE" "ternary should contain JMP_IF_FALSE"
); );
} }
@@ -89,10 +77,7 @@ fn compile_ternary_emits_jumps() {
fn compile_logical_not() { fn compile_logical_not() {
let module = compile_source("!true"); let module = compile_source("!true");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NOT),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::NOT),
"!true should emit NOT opcode" "!true should emit NOT opcode"
); );
} }
@@ -101,10 +86,7 @@ fn compile_logical_not() {
fn compile_logical_and_simple() { fn compile_logical_and_simple() {
let module = compile_source("1 && 2"); let module = compile_source("1 && 2");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::AND),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::AND),
"simple && should emit AND opcode" "simple && should emit AND opcode"
); );
} }
@@ -113,10 +95,7 @@ fn compile_logical_and_simple() {
fn compile_logical_or_simple() { fn compile_logical_or_simple() {
let module = compile_source("0 || 1"); let module = compile_source("0 || 1");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::OR),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::OR),
"simple || should emit OR opcode" "simple || should emit OR opcode"
); );
} }
@@ -126,20 +105,14 @@ fn regression_coalesce_consistency() {
let result = std::panic::catch_unwind(|| { let result = std::panic::catch_unwind(|| {
compile_source("a ?? b"); compile_source("a ?? b");
}); });
assert!( assert!(result.is_err(), "a ?? b should produce an error (not silently misbehave)");
result.is_err(),
"a ?? b should produce an error (not silently misbehave)"
);
} }
#[test] #[test]
fn compile_strict_eq() { fn compile_strict_eq() {
let module = compile_source("1 === 2"); let module = compile_source("1 === 2");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::STRICT_EQ),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::STRICT_EQ),
"1 === 2 should emit STRICT_EQ opcode" "1 === 2 should emit STRICT_EQ opcode"
); );
} }
@@ -148,10 +121,7 @@ fn compile_strict_eq() {
fn compile_strict_neq() { fn compile_strict_neq() {
let module = compile_source("1 !== 2"); let module = compile_source("1 !== 2");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::STRICT_NEQ),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::STRICT_NEQ),
"1 !== 2 should emit STRICT_NEQ opcode" "1 !== 2 should emit STRICT_NEQ opcode"
); );
} }
@@ -160,10 +130,7 @@ fn compile_strict_neq() {
fn compile_unary_plus() { fn compile_unary_plus() {
let module = compile_source("+'hello'"); let module = compile_source("+'hello'");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::UNARY_PLUS),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::UNARY_PLUS),
"+'hello' should emit UNARY_PLUS opcode" "+'hello' should emit UNARY_PLUS opcode"
); );
} }
@@ -172,17 +139,11 @@ fn compile_unary_plus() {
fn compile_typeof_strict_eq() { fn compile_typeof_strict_eq() {
let module = compile_source("typeof 42 === 'number'"); let module = compile_source("typeof 42 === 'number'");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::TYPEOF),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::TYPEOF),
"should emit TYPEOF opcode" "should emit TYPEOF opcode"
); );
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::STRICT_EQ),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::STRICT_EQ),
"should emit STRICT_EQ opcode" "should emit STRICT_EQ opcode"
); );
} }
@@ -207,10 +168,7 @@ fn compile_unary_plus_no_error() {
fn compile_compound_add() { fn compile_compound_add() {
let module = compile_source("var x=0; x+=1"); let module = compile_source("var x=0; x+=1");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_ADD),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::COMPOUND_ADD),
"x+=1 should emit COMPOUND_ADD opcode" "x+=1 should emit COMPOUND_ADD opcode"
); );
} }
@@ -219,10 +177,7 @@ fn compile_compound_add() {
fn compile_compound_exp() { fn compile_compound_exp() {
let module = compile_source("var x=0; x**=2"); let module = compile_source("var x=0; x**=2");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::COMPOUND_EXP),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::COMPOUND_EXP),
"x**=2 should emit COMPOUND_EXP opcode" "x**=2 should emit COMPOUND_EXP opcode"
); );
} }
@@ -247,10 +202,7 @@ fn compile_compound_exp_no_error() {
fn compile_inc_pre() { fn compile_inc_pre() {
let module = compile_source("++x"); let module = compile_source("++x");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::INC_PRE),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::INC_PRE),
"++x should emit INC_PRE opcode" "++x should emit INC_PRE opcode"
); );
} }
@@ -259,10 +211,7 @@ fn compile_inc_pre() {
fn compile_inc_post() { fn compile_inc_post() {
let module = compile_source("x++"); let module = compile_source("x++");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::INC_POST),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::INC_POST),
"x++ should emit INC_POST opcode" "x++ should emit INC_POST opcode"
); );
} }
@@ -287,14 +236,8 @@ fn compile_dec_no_error() {
fn compile_inc_dec_diff_opcodes() { fn compile_inc_dec_diff_opcodes() {
let m1 = compile_source("++x"); let m1 = compile_source("++x");
let m2 = compile_source("--x"); let m2 = compile_source("--x");
let has_inc_pre = m1 let has_inc_pre = m1.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::INC_PRE);
.bytecode let has_dec_pre = m2.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::DEC_PRE);
.iter()
.any(|&i| opcode::opcode(i) == OpCode::INC_PRE);
let has_dec_pre = m2
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::DEC_PRE);
assert!(has_inc_pre, "++x should emit INC_PRE"); assert!(has_inc_pre, "++x should emit INC_PRE");
assert!(has_dec_pre, "--x should emit DEC_PRE"); assert!(has_dec_pre, "--x should emit DEC_PRE");
} }
@@ -303,10 +246,7 @@ fn compile_inc_dec_diff_opcodes() {
fn compile_member_inc() { fn compile_member_inc() {
let module = compile_source("var obj={x:1}; obj.x++"); let module = compile_source("var obj={x:1}; obj.x++");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::MEMBER_INC),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::MEMBER_INC),
"obj.x++ should emit MEMBER_INC" "obj.x++ should emit MEMBER_INC"
); );
} }
@@ -315,10 +255,7 @@ fn compile_member_inc() {
fn compile_dyn_member_inc() { fn compile_dyn_member_inc() {
let module = compile_source("var obj={a:3}; var k='a'; obj[k]++"); let module = compile_source("var obj={a:3}; var k='a'; obj[k]++");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::DYN_MEMBER_INC),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::DYN_MEMBER_INC),
"obj[k]++ should emit DYN_MEMBER_INC" "obj[k]++ should emit DYN_MEMBER_INC"
); );
} }
@@ -13,10 +13,7 @@ fn compile_source(source: &str) -> oxide_compiler::module::CompiledModule {
fn compile_var_declaration() { fn compile_var_declaration() {
let module = compile_source("var x = 42;"); let module = compile_source("var x = 42;");
assert!(!module.bytecode.is_empty()); assert!(!module.bytecode.is_empty());
assert_eq!( assert_eq!(module.constants[0], oxide_compiler::compiler::Constant::Int(42));
module.constants[0],
oxide_compiler::compiler::Constant::Int(42)
);
} }
#[test] #[test]
@@ -29,27 +26,18 @@ fn compile_return_nothing() {
#[test] #[test]
fn compile_return_value() { fn compile_return_value() {
let module = compile_source("function f() { return 42; }"); let module = compile_source("function f() { return 42; }");
assert!( assert!(!module.bytecode.is_empty(), "function declaration should produce bytecode");
!module.bytecode.is_empty(),
"function declaration should produce bytecode"
);
} }
#[test] #[test]
fn compile_if_else_emits_jmp_if_false() { fn compile_if_else_emits_jmp_if_false() {
let module = compile_source("if (true) { 1 } else { 2 }"); let module = compile_source("if (true) { 1 } else { 2 }");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
"if/else should contain JMP_IF_FALSE" "if/else should contain JMP_IF_FALSE"
); );
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::JMP),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::JMP),
"if/else should contain JMP" "if/else should contain JMP"
); );
} }
@@ -57,17 +45,10 @@ fn compile_if_else_emits_jmp_if_false() {
#[test] #[test]
fn compile_while_emits_jump_back() { fn compile_while_emits_jump_back() {
let module = compile_source("while (true) { 1 }"); let module = compile_source("while (true) { 1 }");
let jmp_ops: Vec<_> = module let jmp_ops: Vec<_> = module.bytecode.iter().filter(|&&i| opcode::opcode(i) == OpCode::JMP).collect();
.bytecode
.iter()
.filter(|&&i| opcode::opcode(i) == OpCode::JMP)
.collect();
assert!(!jmp_ops.is_empty(), "while should contain JMP (backward)"); assert!(!jmp_ops.is_empty(), "while should contain JMP (backward)");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
"while should contain JMP_IF_FALSE" "while should contain JMP_IF_FALSE"
); );
} }
@@ -76,10 +57,7 @@ fn compile_while_emits_jump_back() {
fn compile_for_emits_jumps() { fn compile_for_emits_jumps() {
let module = compile_source("for (i=0; i<3; i=i+1) { 1 }"); let module = compile_source("for (i=0; i<3; i=i+1) { 1 }");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
"for should contain JMP_IF_FALSE" "for should contain JMP_IF_FALSE"
); );
} }
@@ -87,19 +65,13 @@ fn compile_for_emits_jumps() {
#[test] #[test]
fn compile_break_in_loop() { fn compile_break_in_loop() {
let module = compile_source("while (true) { break; }"); let module = compile_source("while (true) { break; }");
assert!( assert!(!module.bytecode.is_empty(), "break should compile without error");
!module.bytecode.is_empty(),
"break should compile without error"
);
} }
#[test] #[test]
fn compile_continue_in_loop() { fn compile_continue_in_loop() {
let module = compile_source("while (true) { continue; }"); let module = compile_source("while (true) { continue; }");
assert!( assert!(!module.bytecode.is_empty(), "continue should compile without error");
!module.bytecode.is_empty(),
"continue should compile without error"
);
} }
#[test] #[test]
@@ -126,10 +98,7 @@ fn compile_nested_if() {
.iter() .iter()
.filter(|&&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE) .filter(|&&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE)
.count(); .count();
assert!( assert!(jmp_if_false_count >= 2, "nested if should have 2+ JMP_IF_FALSE");
jmp_if_false_count >= 2,
"nested if should have 2+ JMP_IF_FALSE"
);
} }
#[test] #[test]
@@ -143,10 +112,7 @@ fn regression_for_var_init() {
let module = compile_source("for (var i = 0; i < 3; i = i + 1) {}"); let module = compile_source("for (var i = 0; i < 3; i = i + 1) {}");
assert!(!module.bytecode.is_empty(), "for(var) init should compile"); assert!(!module.bytecode.is_empty(), "for(var) init should compile");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::JMP_IF_FALSE),
"for(var) init should contain JMP_IF_FALSE" "for(var) init should contain JMP_IF_FALSE"
); );
} }
@@ -165,26 +131,16 @@ fn regression_var_decl_counter_no_init() {
#[test] #[test]
fn regression_redundant_jmp_if_without_else() { fn regression_redundant_jmp_if_without_else() {
let module = compile_source("if (true) { 1 }"); let module = compile_source("if (true) { 1 }");
assert!( assert!(!module.bytecode.is_empty(), "if without else should compile");
!module.bytecode.is_empty(),
"if without else should compile"
);
let last = opcode::opcode(*module.bytecode.last().unwrap()); let last = opcode::opcode(*module.bytecode.last().unwrap());
assert_eq!( assert_eq!(last, OpCode::HALT, "final opcode should be HALT, not dangling JMP");
last,
OpCode::HALT,
"final opcode should be HALT, not dangling JMP"
);
} }
#[test] #[test]
fn compile_do_while_emits_jmp_if_true() { fn compile_do_while_emits_jmp_if_true() {
let module = compile_source("do { 1 } while (true)"); let module = compile_source("do { 1 } while (true)");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::JMP_IF_TRUE),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::JMP_IF_TRUE),
"do-while should contain JMP_IF_TRUE" "do-while should contain JMP_IF_TRUE"
); );
} }
@@ -204,36 +160,24 @@ fn compile_do_while_emits_body_before_test() {
#[test] #[test]
fn compile_do_while_break() { fn compile_do_while_break() {
let module = compile_source("do { break; } while (true)"); let module = compile_source("do { break; } while (true)");
assert!( assert!(!module.bytecode.is_empty(), "do-while with break should compile");
!module.bytecode.is_empty(),
"do-while with break should compile"
);
} }
#[test] #[test]
fn compile_do_while_continue() { fn compile_do_while_continue() {
let module = compile_source("var x = 0; do { continue; } while (x < 5)"); let module = compile_source("var x = 0; do { continue; } while (x < 5)");
assert!( assert!(!module.bytecode.is_empty(), "do-while with continue should compile");
!module.bytecode.is_empty(),
"do-while with continue should compile"
);
} }
#[test] #[test]
fn compile_for_in_emits_opcodes() { fn compile_for_in_emits_opcodes() {
let module = compile_source("var obj={a:1}; for (k in obj) {}"); let module = compile_source("var obj={a:1}; for (k in obj) {}");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_IN_INIT),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::FOR_IN_INIT),
"for-in should emit FOR_IN_INIT" "for-in should emit FOR_IN_INIT"
); );
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::FOR_IN_NEXT),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::FOR_IN_NEXT),
"for-in should emit FOR_IN_NEXT" "for-in should emit FOR_IN_NEXT"
); );
} }
@@ -254,10 +198,7 @@ fn compile_for_in_let_decl() {
fn compile_switch_emits_jmp_if_true() { fn compile_switch_emits_jmp_if_true() {
let module = compile_source("var x=0; switch(x){case 1:1;case 2:2;}"); let module = compile_source("var x=0; switch(x){case 1:1;case 2:2;}");
assert!( assert!(
module module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::JMP_IF_TRUE),
.bytecode
.iter()
.any(|&i| opcode::opcode(i) == OpCode::JMP_IF_TRUE),
"switch should emit JMP_IF_TRUE" "switch should emit JMP_IF_TRUE"
); );
} }
@@ -23,17 +23,10 @@ fn structural_hash_hit() {
let allocator = Allocator::default(); let allocator = Allocator::default();
let program = oxide_parser::parse(&allocator, "1 + 2").expect("parse failed"); let program = oxide_parser::parse(&allocator, "1 + 2").expect("parse failed");
let m1 = forge let m1 = forge.get_or_compile(&program, &compiler).expect("first compile");
.get_or_compile(&program, &compiler) let m2 = forge.get_or_compile(&program, &compiler).expect("second compile");
.expect("first compile");
let m2 = forge
.get_or_compile(&program, &compiler)
.expect("second compile");
assert_eq!( assert_eq!((m1.bytecode.len(), m1.n_registers), (m2.bytecode.len(), m2.n_registers));
(m1.bytecode.len(), m1.n_registers),
(m2.bytecode.len(), m2.n_registers)
);
} }
#[test] #[test]
@@ -75,27 +68,18 @@ fn structural_hash_different_shape() {
fn regression_forstatement_hash() { fn regression_forstatement_hash() {
let hash_a = parse_to_hash("for(i=0;i<3;i++){1}"); let hash_a = parse_to_hash("for(i=0;i<3;i++){1}");
let hash_b = parse_to_hash("for(;;){1}"); let hash_b = parse_to_hash("for(;;){1}");
assert_ne!( assert_ne!(hash_a, hash_b, "different for-loop init/test/update must produce different hashes");
hash_a, hash_b,
"different for-loop init/test/update must produce different hashes"
);
} }
#[test] #[test]
fn regression_conditionalexpression_hash() { fn regression_conditionalexpression_hash() {
let hash_a = parse_to_hash("a?1:2"); let hash_a = parse_to_hash("a?1:2");
let hash_b = parse_to_hash("a?3:4"); let hash_b = parse_to_hash("a?3:4");
assert_ne!( assert_ne!(hash_a, hash_b, "different ternary branches must produce different hashes");
hash_a, hash_b,
"different ternary branches must produce different hashes"
);
let hash_c = parse_to_hash("a?1:2"); let hash_c = parse_to_hash("a?1:2");
let hash_d = parse_to_hash("a?1:2||3"); let hash_d = parse_to_hash("a?1:2||3");
assert_ne!( assert_ne!(hash_c, hash_d, "different expression types with same test must produce different hashes");
hash_c, hash_d,
"different expression types with same test must produce different hashes"
);
} }
#[test] #[test]
@@ -13,10 +13,7 @@ fn compile_source(source: &str) -> oxide_compiler::module::CompiledModule {
fn symbol_table_declare_and_lookup() { fn symbol_table_declare_and_lookup() {
let module = compile_source("var x = 42;"); let module = compile_source("var x = 42;");
assert!(!module.bytecode.is_empty()); assert!(!module.bytecode.is_empty());
assert_eq!( assert_eq!(module.constants[0], oxide_compiler::compiler::Constant::Int(42));
module.constants[0],
oxide_compiler::compiler::Constant::Int(42)
);
} }
#[test] #[test]
@@ -36,10 +33,7 @@ fn symbol_table_tdz_global_shadow() {
let result = std::panic::catch_unwind(|| { let result = std::panic::catch_unwind(|| {
compile_source("var x = 1; { var x = x; }"); compile_source("var x = 1; { var x = x; }");
}); });
assert!( assert!(result.is_err(), "TDZ: accessing x in its own initializer inside a block should error");
result.is_err(),
"TDZ: accessing x in its own initializer inside a block should error"
);
} }
#[test] #[test]
@@ -56,8 +50,5 @@ fn symbol_table_duplicate_var() {
#[test] #[test]
fn symbol_table_undeclared_auto_global() { fn symbol_table_undeclared_auto_global() {
let module = compile_source("x = 5;"); let module = compile_source("x = 5;");
assert!( assert!(!module.bytecode.is_empty(), "undeclared assignment should auto-create global");
!module.bytecode.is_empty(),
"undeclared assignment should auto-create global"
);
} }
+41 -169
View File
@@ -202,11 +202,7 @@ fn intern_label(string_forge: &StringForge, label: &str) -> u32 {
} }
fn make_pair( fn make_pair(
string_forge: &StringForge, string_forge: &StringForge, shape_forge: &ShapeForge, name: &str, si_prototype: u32, si_constructor: u32,
shape_forge: &ShapeForge,
name: &str,
si_prototype: u32,
si_constructor: u32,
si_name: u32, si_name: u32,
) -> (P<JsObject>, P<JsObject>) { ) -> (P<JsObject>, P<JsObject>) {
intern_label(string_forge, name); intern_label(string_forge, name);
@@ -226,13 +222,9 @@ fn make_pair(
// Pre-allocate Vec slots so wire_ctor_proto can overwrite vec[0] later. // Pre-allocate Vec slots so wire_ctor_proto can overwrite vec[0] later.
// Proto: 1 property ("constructor"), Ctor: 2 properties ("prototype", "name"). // Proto: 1 property ("constructor"), Ctor: 2 properties ("prototype", "name").
proto proto.ensure_hash_props().push(Box::new(JsValue::undefined())); // slot 0: "constructor" placeholder
.ensure_hash_props() ctor.ensure_hash_props().push(Box::new(JsValue::undefined())); // slot 0: "prototype" placeholder
.push(Box::new(JsValue::undefined())); // slot 0: "constructor" placeholder ctor.ensure_hash_props().push(Box::new(JsValue::undefined())); // slot 1: "name" placeholder
ctor.ensure_hash_props()
.push(Box::new(JsValue::undefined())); // slot 0: "prototype" placeholder
ctor.ensure_hash_props()
.push(Box::new(JsValue::undefined())); // slot 1: "name" placeholder
(P::new(proto), P::new(ctor)) (P::new(proto), P::new(ctor))
} }
@@ -250,70 +242,22 @@ impl BuiltinWorld {
intern_label(string_forge, "toString"); intern_label(string_forge, "toString");
intern_label(string_forge, "valueOf"); intern_label(string_forge, "valueOf");
let (object_proto, object_constructor) = make_pair( let (object_proto, object_constructor) =
string_forge, make_pair(string_forge, shape_forge, "Object", si_prototype, si_constructor, si_name);
shape_forge, let (array_proto, array_constructor) =
"Object", make_pair(string_forge, shape_forge, "Array", si_prototype, si_constructor, si_name);
si_prototype, let (function_proto, function_constructor) =
si_constructor, make_pair(string_forge, shape_forge, "Function", si_prototype, si_constructor, si_name);
si_name, let (string_proto, string_constructor) =
); make_pair(string_forge, shape_forge, "String", si_prototype, si_constructor, si_name);
let (array_proto, array_constructor) = make_pair( let (number_proto, number_constructor) =
string_forge, make_pair(string_forge, shape_forge, "Number", si_prototype, si_constructor, si_name);
shape_forge, let (boolean_proto, boolean_constructor) =
"Array", make_pair(string_forge, shape_forge, "Boolean", si_prototype, si_constructor, si_name);
si_prototype, let (error_proto, error_constructor) =
si_constructor, make_pair(string_forge, shape_forge, "Error", si_prototype, si_constructor, si_name);
si_name, let (symbol_proto, symbol_constructor) =
); make_pair(string_forge, shape_forge, "Symbol", si_prototype, si_constructor, si_name);
let (function_proto, function_constructor) = make_pair(
string_forge,
shape_forge,
"Function",
si_prototype,
si_constructor,
si_name,
);
let (string_proto, string_constructor) = make_pair(
string_forge,
shape_forge,
"String",
si_prototype,
si_constructor,
si_name,
);
let (number_proto, number_constructor) = make_pair(
string_forge,
shape_forge,
"Number",
si_prototype,
si_constructor,
si_name,
);
let (boolean_proto, boolean_constructor) = make_pair(
string_forge,
shape_forge,
"Boolean",
si_prototype,
si_constructor,
si_name,
);
let (error_proto, error_constructor) = make_pair(
string_forge,
shape_forge,
"Error",
si_prototype,
si_constructor,
si_name,
);
let (symbol_proto, symbol_constructor) = make_pair(
string_forge,
shape_forge,
"Symbol",
si_prototype,
si_constructor,
si_name,
);
let error_proto_val = JsValue::from_js_object(error_proto.as_ptr() as *mut JsObject); let error_proto_val = JsValue::from_js_object(error_proto.as_ptr() as *mut JsObject);
let type_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); let type_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val));
@@ -327,38 +271,14 @@ impl BuiltinWorld {
let json_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let json_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
let (date_proto, date_constructor) = make_pair( let (date_proto, date_constructor) =
string_forge, make_pair(string_forge, shape_forge, "Date", si_prototype, si_constructor, si_name);
shape_forge, let (set_proto, set_constructor) =
"Date", make_pair(string_forge, shape_forge, "Set", si_prototype, si_constructor, si_name);
si_prototype, let (map_proto, map_constructor) =
si_constructor, make_pair(string_forge, shape_forge, "Map", si_prototype, si_constructor, si_name);
si_name, let (regexp_proto, regexp_constructor) =
); make_pair(string_forge, shape_forge, "RegExp", si_prototype, si_constructor, si_name);
let (set_proto, set_constructor) = make_pair(
string_forge,
shape_forge,
"Set",
si_prototype,
si_constructor,
si_name,
);
let (map_proto, map_constructor) = make_pair(
string_forge,
shape_forge,
"Map",
si_prototype,
si_constructor,
si_name,
);
let (regexp_proto, regexp_constructor) = make_pair(
string_forge,
shape_forge,
"RegExp",
si_prototype,
si_constructor,
si_name,
);
let sym_match = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let sym_match = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
let sym_replace = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let sym_replace = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
@@ -458,12 +378,7 @@ impl BuiltinWorld {
} }
} }
pub fn bind_object_methods( pub fn bind_object_methods(&self, methods: &ObjectMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
&self,
methods: &ObjectMethods,
string_forge: &StringForge,
shape_forge: &ShapeForge,
) {
let ctor_ptr = P::as_ptr(&self.object_constructor) as *mut JsObject; let ctor_ptr = P::as_ptr(&self.object_constructor) as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr }; let ctor = unsafe { &mut *ctor_ptr };
bind_methods!( bind_methods!(
@@ -475,11 +390,7 @@ impl BuiltinWorld {
("create", methods.create, 2), ("create", methods.create, 2),
("assign", methods.assign, 2), ("assign", methods.assign, 2),
("defineProperty", methods.define_property, 3), ("defineProperty", methods.define_property, 3),
( ("getOwnPropertyDescriptor", methods.get_own_property_descriptor, 2),
"getOwnPropertyDescriptor",
methods.get_own_property_descriptor,
2
),
("freeze", methods.freeze, 1), ("freeze", methods.freeze, 1),
("seal", methods.seal, 1), ("seal", methods.seal, 1),
("preventExtensions", methods.prevent_extensions, 1), ("preventExtensions", methods.prevent_extensions, 1),
@@ -496,12 +407,7 @@ impl BuiltinWorld {
); );
} }
pub fn bind_array_methods( pub fn bind_array_methods(&self, methods: &ArrayMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
&self,
methods: &ArrayMethods,
string_forge: &StringForge,
shape_forge: &ShapeForge,
) {
let proto_ptr = P::as_ptr(&self.array_proto) as *mut JsObject; let proto_ptr = P::as_ptr(&self.array_proto) as *mut JsObject;
let proto = unsafe { &mut *proto_ptr }; let proto = unsafe { &mut *proto_ptr };
bind_methods!( bind_methods!(
@@ -540,12 +446,7 @@ impl BuiltinWorld {
); );
} }
pub fn bind_error_methods( pub fn bind_error_methods(&self, methods: &ErrorMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
&self,
methods: &ErrorMethods,
string_forge: &StringForge,
shape_forge: &ShapeForge,
) {
let ctor_ptr = P::as_ptr(&self.error_constructor) as *mut JsObject; let ctor_ptr = P::as_ptr(&self.error_constructor) as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr }; let ctor = unsafe { &mut *ctor_ptr };
bind_methods!( bind_methods!(
@@ -574,12 +475,7 @@ impl BuiltinWorld {
); );
} }
pub fn bind_string_methods( pub fn bind_string_methods(&self, methods: &StringMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
&self,
methods: &StringMethods,
string_forge: &StringForge,
shape_forge: &ShapeForge,
) {
let proto_ptr = P::as_ptr(&self.string_proto) as *mut JsObject; let proto_ptr = P::as_ptr(&self.string_proto) as *mut JsObject;
let proto = unsafe { &mut *proto_ptr }; let proto = unsafe { &mut *proto_ptr };
bind_methods!( bind_methods!(
@@ -615,12 +511,7 @@ impl BuiltinWorld {
); );
} }
pub fn bind_number_methods( pub fn bind_number_methods(&self, methods: &NumberMethods, string_forge: &StringForge, shape_forge: &ShapeForge) {
&self,
methods: &NumberMethods,
string_forge: &StringForge,
shape_forge: &ShapeForge,
) {
let ctor_ptr = P::as_ptr(&self.number_constructor) as *mut JsObject; let ctor_ptr = P::as_ptr(&self.number_constructor) as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr }; let ctor = unsafe { &mut *ctor_ptr };
let proto_ptr = P::as_ptr(&self.number_proto) as *mut JsObject; let proto_ptr = P::as_ptr(&self.number_proto) as *mut JsObject;
@@ -651,10 +542,7 @@ impl BuiltinWorld {
} }
pub fn bind_function_methods( pub fn bind_function_methods(
&self, &self, methods: &FunctionMethods, string_forge: &StringForge, shape_forge: &ShapeForge,
methods: &FunctionMethods,
string_forge: &StringForge,
shape_forge: &ShapeForge,
) { ) {
let proto_ptr = P::as_ptr(&self.function_proto) as *mut JsObject; let proto_ptr = P::as_ptr(&self.function_proto) as *mut JsObject;
let proto = unsafe { &mut *proto_ptr }; let proto = unsafe { &mut *proto_ptr };
@@ -672,13 +560,8 @@ impl BuiltinWorld {
} }
pub fn bind_method( pub fn bind_method(
&self, &self, proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &StringForge, method_name: &str,
proto: &mut JsObject, native_fn_ptr: *const (), arg_count: u8,
shape_forge: &ShapeForge,
string_forge: &StringForge,
method_name: &str,
native_fn_ptr: *const (),
arg_count: u8,
) -> Result<(), String> { ) -> Result<(), String> {
Self::bind_method_static( Self::bind_method_static(
proto, proto,
@@ -692,13 +575,8 @@ impl BuiltinWorld {
} }
pub fn bind_method_static( pub fn bind_method_static(
proto: &mut JsObject, proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &StringForge, method_name: &str,
shape_forge: &ShapeForge, native_fn_ptr: *const (), arg_count: u8, wrapper_proto: JsValue,
string_forge: &StringForge,
method_name: &str,
native_fn_ptr: *const (),
arg_count: u8,
wrapper_proto: JsValue,
) -> Result<(), String> { ) -> Result<(), String> {
let si = string_forge.intern(method_name).0; let si = string_forge.intern(method_name).0;
let wrapper_proto_ptr = if wrapper_proto.is_object() { let wrapper_proto_ptr = if wrapper_proto.is_object() {
@@ -748,10 +626,7 @@ mod tests {
&w.symbol_proto, &w.symbol_proto,
]; ];
for p in protos { for p in protos {
assert!( assert!(p.shape_id() > EMPTY_SHAPE_ID, "proto should have a non-empty shape");
p.shape_id() > EMPTY_SHAPE_ID,
"proto should have a non-empty shape"
);
} }
} }
@@ -803,9 +678,6 @@ mod tests {
w.object_constructor.shape_id() > EMPTY_SHAPE_ID, w.object_constructor.shape_id() > EMPTY_SHAPE_ID,
"constructor should have prototype + name shape" "constructor should have prototype + name shape"
); );
assert!( assert!(w.object_proto.shape_id() > EMPTY_SHAPE_ID, "prototype should have constructor shape");
w.object_proto.shape_id() > EMPTY_SHAPE_ID,
"prototype should have constructor shape"
);
} }
} }
+5 -16
View File
@@ -13,15 +13,11 @@ pub struct CodeForge {
impl CodeForge { impl CodeForge {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self { map: DashMap::new() }
map: DashMap::new(),
}
} }
pub fn get_or_compile( pub fn get_or_compile(
&self, &self, program: &oxide_parser::Program, compiler: &Compiler,
program: &oxide_parser::Program,
compiler: &Compiler,
) -> Result<Arc<CompiledModule>, String> { ) -> Result<Arc<CompiledModule>, String> {
let hash = structural_hash(program); let hash = structural_hash(program);
@@ -81,17 +77,10 @@ mod tests {
let allocator = Allocator::default(); let allocator = Allocator::default();
let program = oxide_parser::parse(&allocator, "1 + 2").expect("parse failed"); let program = oxide_parser::parse(&allocator, "1 + 2").expect("parse failed");
let first = forge let first = forge.get_or_compile(&program, &compiler).expect("first compile");
.get_or_compile(&program, &compiler) let second = forge.get_or_compile(&program, &compiler).expect("second compile");
.expect("first compile");
let second = forge
.get_or_compile(&program, &compiler)
.expect("second compile");
assert_eq!( assert_eq!((first.bytecode.len(), first.n_registers), (second.bytecode.len(), second.n_registers));
(first.bytecode.len(), first.n_registers),
(second.bytecode.len(), second.n_registers)
);
} }
#[test] #[test]
+3 -9
View File
@@ -96,21 +96,15 @@ impl OxideKernel {
let nan_shape = shape_forge.make_shape(EMPTY_SHAPE_ID, si_nan); let nan_shape = shape_forge.make_shape(EMPTY_SHAPE_ID, si_nan);
global_obj.set_shape_id(nan_shape); global_obj.set_shape_id(nan_shape);
global_obj global_obj.ensure_hash_props().push(Box::new(JsValue::float(f64::NAN)));
.ensure_hash_props()
.push(Box::new(JsValue::float(f64::NAN)));
let undef_shape = shape_forge.make_shape(nan_shape, si_undef); let undef_shape = shape_forge.make_shape(nan_shape, si_undef);
global_obj.set_shape_id(undef_shape); global_obj.set_shape_id(undef_shape);
global_obj global_obj.ensure_hash_props().push(Box::new(JsValue::undefined()));
.ensure_hash_props()
.push(Box::new(JsValue::undefined()));
let inf_shape = shape_forge.make_shape(undef_shape, si_infinity); let inf_shape = shape_forge.make_shape(undef_shape, si_infinity);
global_obj.set_shape_id(inf_shape); global_obj.set_shape_id(inf_shape);
global_obj global_obj.ensure_hash_props().push(Box::new(JsValue::float(f64::INFINITY)));
.ensure_hash_props()
.push(Box::new(JsValue::float(f64::INFINITY)));
let global_object = P::new(global_obj); let global_object = P::new(global_obj);
+1 -3
View File
@@ -18,9 +18,7 @@ pub struct PropForge {
impl PropForge { impl PropForge {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self { templates: DashMap::new() }
templates: DashMap::new(),
}
} }
pub fn get_template(&self, shape_id: ShapeId) -> Option<Arc<PropTemplate>> { pub fn get_template(&self, shape_id: ShapeId) -> Option<Arc<PropTemplate>> {
+1 -2
View File
@@ -98,8 +98,7 @@ impl StringForge {
return; return;
} }
let mut new_map: HashMap<String, u32, FxBuildHasher> = let mut new_map: HashMap<String, u32, FxBuildHasher> = HashMap::with_hasher(FxBuildHasher::default());
HashMap::with_hasher(FxBuildHasher::default());
let mut new_entries = Vec::with_capacity(inner.entries.len() - dead_count); let mut new_entries = Vec::with_capacity(inner.entries.len() - dead_count);
for entry in inner.entries.drain(..) { for entry in inner.entries.drain(..) {
+1 -4
View File
@@ -7,10 +7,7 @@ pub use oxc_allocator::Allocator;
pub use oxc_ast::ast::*; pub use oxc_ast::ast::*;
pub use oxc_span::Span; pub use oxc_span::Span;
pub fn parse<'a>( pub fn parse<'a>(allocator: &'a Allocator, source: &'a str) -> Result<Program<'a>, Vec<OxideError>> {
allocator: &'a Allocator,
source: &'a str,
) -> Result<Program<'a>, Vec<OxideError>> {
use oxc_parser::Parser; use oxc_parser::Parser;
use oxc_span::SourceType; use oxc_span::SourceType;
+3 -12
View File
@@ -13,10 +13,7 @@ fn parse_empty_string() {
let result = oxide_parser::parse(&allocator, ""); let result = oxide_parser::parse(&allocator, "");
assert!(result.is_ok(), "empty string should parse"); assert!(result.is_ok(), "empty string should parse");
let program = result.unwrap(); let program = result.unwrap();
assert!( assert!(program.body.is_empty(), "empty program should have empty body");
program.body.is_empty(),
"empty program should have empty body"
);
} }
#[test] #[test]
@@ -25,10 +22,7 @@ fn parse_function_declaration() {
let result = oxide_parser::parse(&allocator, "function foo() { return 42; }"); let result = oxide_parser::parse(&allocator, "function foo() { return 42; }");
assert!(result.is_ok(), "function declaration should parse"); assert!(result.is_ok(), "function declaration should parse");
let program = result.unwrap(); let program = result.unwrap();
assert!( assert!(!program.body.is_empty(), "program with function should not be empty");
!program.body.is_empty(),
"program with function should not be empty"
);
} }
#[test] #[test]
@@ -37,10 +31,7 @@ fn parse_syntax_error() {
let result = oxide_parser::parse(&allocator, "function("); let result = oxide_parser::parse(&allocator, "function(");
assert!(result.is_err(), "syntax error should return Err"); assert!(result.is_err(), "syntax error should return Err");
let errors = result.unwrap_err(); let errors = result.unwrap_err();
assert!( assert!(!errors.is_empty(), "syntax error should produce at least one error");
!errors.is_empty(),
"syntax error should produce at least one error"
);
} }
#[test] #[test]
+15 -63
View File
@@ -192,18 +192,14 @@ Test262Error.prototype.constructor = Test262Error;
let full_source = format!("{prelude}\n{assert_js}"); let full_source = format!("{prelude}\n{assert_js}");
let alloc = oxide_parser::Allocator::default(); let alloc = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&alloc, &full_source) let program =
.map_err(|e| format!("harness parse error: {}", e[0].message))?; oxide_parser::parse(&alloc, &full_source).map_err(|e| format!("harness parse error: {}", e[0].message))?;
Compiler::new().compile(&program) Compiler::new().compile(&program)
} }
fn run_test( fn run_test(
path: &Path, path: &Path, source: &str, meta: &TestMeta, kernel: &Arc<OxideKernel>, harness_module: &CompiledModule,
source: &str,
meta: &TestMeta,
kernel: &Arc<OxideKernel>,
harness_module: &CompiledModule,
) -> TestResult { ) -> TestResult {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
@@ -215,21 +211,13 @@ fn run_test(
Ok(r) => r, Ok(r) => r,
Err(_panic) => { Err(_panic) => {
let dur = start.elapsed().as_millis() as u64; let dur = start.elapsed().as_millis() as u64;
TestResult::fail( TestResult::fail(path.to_path_buf(), dur, "engine panic (unsupported feature)")
path.to_path_buf(),
dur,
"engine panic (unsupported feature)",
)
} }
} }
} }
fn run_test_inner( fn run_test_inner(
path: &Path, path: &Path, source: &str, meta: &TestMeta, kernel: &Arc<OxideKernel>, harness_module: &CompiledModule,
source: &str,
meta: &TestMeta,
kernel: &Arc<OxideKernel>,
harness_module: &CompiledModule,
) -> TestResult { ) -> TestResult {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
@@ -308,11 +296,7 @@ fn run_test_inner(
if e.contains(&neg.error_type) { if e.contains(&neg.error_type) {
return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}")); return TestResult::pass(path.to_path_buf(), dur, format!("expected: {e}"));
} }
TestResult::fail( TestResult::fail(path.to_path_buf(), dur, format!("expected {} error, got: {e}", neg.error_type))
path.to_path_buf(),
dur,
format!("expected {} error, got: {e}", neg.error_type),
)
} else if e.contains("not yet implemented") } else if e.contains("not yet implemented")
|| e.contains("not supported") || e.contains("not supported")
|| e.contains("unsupported") || e.contains("unsupported")
@@ -350,10 +334,7 @@ fn categorize_fail(msg: &str) -> String {
} else if reason.contains("is not defined") { } else if reason.contains("is not defined") {
"compile: not defined".into() "compile: not defined".into()
} else { } else {
format!( format!("compile: other ({})", reason.chars().take(60).collect::<String>())
"compile: other ({})",
reason.chars().take(60).collect::<String>()
)
} }
} else if msg.contains("parse error:") { } else if msg.contains("parse error:") {
"parse error".into() "parse error".into()
@@ -372,10 +353,7 @@ fn categorize_fail(msg: &str) -> String {
} else if reason.contains("unsupported") { } else if reason.contains("unsupported") {
"vm: unsupported".into() "vm: unsupported".into()
} else { } else {
format!( format!("vm: other ({})", reason.chars().take(60).collect::<String>())
"vm: other ({})",
reason.chars().take(60).collect::<String>()
)
} }
} else if msg.contains("engine panic") { } else if msg.contains("engine panic") {
"engine panic".into() "engine panic".into()
@@ -426,11 +404,7 @@ fn run_tests() -> bool {
std::process::exit(1); std::process::exit(1);
} }
let filter = if args.len() > 2 { let filter = if args.len() > 2 { Some(args[2].clone()) } else { None };
Some(args[2].clone())
} else {
None
};
eprintln!("discovering tests in: {}", test262_root.display()); eprintln!("discovering tests in: {}", test262_root.display());
let paths = discover_tests(&test262_root); let paths = discover_tests(&test262_root);
@@ -476,10 +450,7 @@ fn run_tests() -> bool {
let meta = match parse_meta(&source) { let meta = match parse_meta(&source) {
Some(m) => m, Some(m) => m,
None => { None => {
results.push(TestResult::skip( results.push(TestResult::skip(path.clone(), String::from("no YAML metadata")));
path.clone(),
String::from("no YAML metadata"),
));
stats.skip += 1; stats.skip += 1;
continue; continue;
} }
@@ -496,10 +467,7 @@ fn run_tests() -> bool {
let path_str = path.to_string_lossy(); let path_str = path.to_string_lossy();
if path_str.contains("staging") { if path_str.contains("staging") {
results.push(TestResult::skip( results.push(TestResult::skip(path.clone(), "staging tests excluded".into()));
path.clone(),
"staging tests excluded".into(),
));
stats.skip += 1; stats.skip += 1;
continue; continue;
} }
@@ -534,30 +502,14 @@ fn run_tests() -> bool {
println!("═══════════════════════════════════════"); println!("═══════════════════════════════════════");
println!(" total : {}", results.len()); println!(" total : {}", results.len());
let total = results.len() as f64; let total = results.len() as f64;
println!( println!(" pass : {} ({:.1}%)", stats.pass, stats.pass as f64 / total * 100.0);
" pass : {} ({:.1}%)", println!(" fail : {} ({:.1}%)", stats.fail, stats.fail as f64 / total * 100.0);
stats.pass, println!(" skip : {} ({:.1}%)", stats.skip, stats.skip as f64 / total * 100.0);
stats.pass as f64 / total * 100.0
);
println!(
" fail : {} ({:.1}%)",
stats.fail,
stats.fail as f64 / total * 100.0
);
println!(
" skip : {} ({:.1}%)",
stats.skip,
stats.skip as f64 / total * 100.0
);
println!(" time : {:?}", Duration::from_millis(stats.total_ms)); println!(" time : {:?}", Duration::from_millis(stats.total_ms));
println!( println!(
" pass% : {:.1}% (of ran: {:.1}%)", " pass% : {:.1}% (of ran: {:.1}%)",
stats.pass as f64 / total * 100.0, stats.pass as f64 / total * 100.0,
if ran > 0 { if ran > 0 { stats.pass as f64 / ran as f64 * 100.0 } else { 0.0 }
stats.pass as f64 / ran as f64 * 100.0
} else {
0.0
}
); );
if !stats.fail_categories.is_empty() { if !stats.fail_categories.is_empty() {
println!(" --- FAIL categories ---"); println!(" --- FAIL categories ---");
+4 -15
View File
@@ -83,12 +83,7 @@ impl JsObject {
} }
} }
pub fn new_array( pub fn new_array(shape_id: ShapeId, proto: JsValue, n_elements: usize, _bump: &bumpalo::Bump) -> Self {
shape_id: ShapeId,
proto: JsValue,
n_elements: usize,
_bump: &bumpalo::Bump,
) -> Self {
let mut obj = Self { let mut obj = Self {
header: (shape_id & 0x00FF_FFFF) | (1 << 30) | (1 << 29), header: (shape_id & 0x00FF_FFFF) | (1 << 30) | (1 << 29),
native_arg_count: 0, native_arg_count: 0,
@@ -192,9 +187,7 @@ impl JsObject {
return JsValue::undefined(); return JsValue::undefined();
} }
let vec = unsafe { &*(self.hash_props as *const Vec<Box<JsValue>>) }; let vec = unsafe { &*(self.hash_props as *const Vec<Box<JsValue>>) };
vec.get(position.to_u32() as usize) vec.get(position.to_u32() as usize).map(|b| **b).unwrap_or(JsValue::undefined())
.map(|b| **b)
.unwrap_or(JsValue::undefined())
} }
/// Set property value at position index. Vec auto-grows if needed. /// Set property value at position index. Vec auto-grows if needed.
@@ -226,8 +219,7 @@ impl JsObject {
return None; return None;
} }
let vec = unsafe { &*(self.hash_props as *const Vec<Box<JsValue>>) }; let vec = unsafe { &*(self.hash_props as *const Vec<Box<JsValue>>) };
vec.get(position.to_u32() as usize) vec.get(position.to_u32() as usize).map(|b| &**b as *const JsValue)
.map(|b| &**b as *const JsValue)
} }
/// Get the length of hash_props vec (returns 0 if not allocated). /// Get the length of hash_props vec (returns 0 if not allocated).
@@ -336,10 +328,7 @@ mod tests {
#[test] #[test]
fn object_size_bounds() { fn object_size_bounds() {
let sz = std::mem::size_of::<JsObject>(); let sz = std::mem::size_of::<JsObject>();
assert!( assert!(sz >= 40 && sz <= 56, "JsObject layout mismatch - expected 40-56B, got {sz}B");
sz >= 40 && sz <= 56,
"JsObject layout mismatch - expected 40-56B, got {sz}B"
);
} }
#[test] #[test]
+2 -10
View File
@@ -124,10 +124,7 @@ impl JsValue {
pub fn string(index: u32, hash: u16) -> Self { pub fn string(index: u32, hash: u16) -> Self {
let payload = ((hash as u64) << STRING_HASH_SHIFT) | (index as u64); let payload = ((hash as u64) << STRING_HASH_SHIFT) | (index as u64);
debug_assert!( debug_assert!(payload & !(STRING_HASH_MASK | STRING_INDEX_MASK) == 0, "string payload overflow");
payload & !(STRING_HASH_MASK | STRING_INDEX_MASK) == 0,
"string payload overflow"
);
Self(make_tag(TAG_STRING) | payload) Self(make_tag(TAG_STRING) | payload)
} }
@@ -288,12 +285,7 @@ impl fmt::Debug for JsValue {
} else if self.is_object() { } else if self.is_object() {
write!(f, "JsValue(Object({:p}))", self.as_ptr()) write!(f, "JsValue(Object({:p}))", self.as_ptr())
} else if self.is_string() { } else if self.is_string() {
write!( write!(f, "JsValue(String(idx={}, hash={:#06x}))", self.as_string_index(), self.as_string_hash())
f,
"JsValue(String(idx={}, hash={:#06x}))",
self.as_string_index(),
self.as_string_hash()
)
} else if self.is_symbol() { } else if self.is_symbol() {
write!(f, "JsValue(Symbol(idx={}))", self.as_symbol_index()) write!(f, "JsValue(Symbol(idx={}))", self.as_symbol_index())
} else { } else {
+1 -8
View File
@@ -44,12 +44,5 @@ pub fn bind_array(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
); );
let ctor_ptr = kernel.builtin_world().array_constructor.as_ptr() as *mut JsObject; let ctor_ptr = kernel.builtin_world().array_constructor.as_ptr() as *mut JsObject;
bind_constructor_hash!( bind_constructor_hash!(kernel, global, "Array", ctor_ptr, crate::builtins::array::array_constructor, 1);
kernel,
global,
"Array",
ctor_ptr,
crate::builtins::array::array_constructor,
1
);
} }
+4 -23
View File
@@ -11,36 +11,17 @@ pub fn bind_boolean(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let proto_ptr = kernel.builtin_world().boolean_proto.as_ptr() as *mut JsObject; let proto_ptr = kernel.builtin_world().boolean_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr }; let proto = unsafe { &mut *proto_ptr };
configure_native_constructor( configure_native_constructor(ctor, crate::builtins::boolean::boolean_constructor as *const (), 1);
ctor,
crate::builtins::boolean::boolean_constructor as *const (),
1,
);
apply_binding_table( apply_binding_table(
kernel.builtin_world(), kernel.builtin_world(),
proto, proto,
kernel, kernel,
&[ &[
( ("valueOf", crate::builtins::boolean::boolean_prototype_value_of as *const (), 0),
"valueOf", ("toString", crate::builtins::boolean::boolean_prototype_to_string as *const (), 0),
crate::builtins::boolean::boolean_prototype_value_of as *const (),
0,
),
(
"toString",
crate::builtins::boolean::boolean_prototype_to_string as *const (),
0,
),
], ],
); );
bind_constructor_hash!( bind_constructor_hash!(kernel, global, "Boolean", ctor_ptr, crate::builtins::boolean::boolean_constructor, 1);
kernel,
global,
"Boolean",
ctor_ptr,
crate::builtins::boolean::boolean_constructor,
1
);
} }
+34 -170
View File
@@ -11,11 +11,7 @@ pub fn bind_date(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let proto_ptr = kernel.builtin_world().date_proto.as_ptr() as *mut JsObject; let proto_ptr = kernel.builtin_world().date_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr }; let proto = unsafe { &mut *proto_ptr };
configure_native_constructor( configure_native_constructor(ctor, crate::builtins::date::date_constructor as *const (), 7);
ctor,
crate::builtins::date::date_constructor as *const (),
7,
);
apply_binding_table( apply_binding_table(
kernel.builtin_world(), kernel.builtin_world(),
@@ -33,171 +29,39 @@ pub fn bind_date(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
proto, proto,
kernel, kernel,
&[ &[
( ("getTime", crate::builtins::date::date_get_time as *const (), 0),
"getTime", ("getFullYear", crate::builtins::date::date_get_full_year as *const (), 0),
crate::builtins::date::date_get_time as *const (), ("getMonth", crate::builtins::date::date_get_month as *const (), 0),
0, ("getDate", crate::builtins::date::date_get_date as *const (), 0),
), ("getDay", crate::builtins::date::date_get_day as *const (), 0),
( ("getHours", crate::builtins::date::date_get_hours as *const (), 0),
"getFullYear", ("getMinutes", crate::builtins::date::date_get_minutes as *const (), 0),
crate::builtins::date::date_get_full_year as *const (), ("getSeconds", crate::builtins::date::date_get_seconds as *const (), 0),
0, ("getMilliseconds", crate::builtins::date::date_get_milliseconds as *const (), 0),
), ("getUTCFullYear", crate::builtins::date::date_get_utc_full_year as *const (), 0),
( ("getUTCMonth", crate::builtins::date::date_get_utc_month as *const (), 0),
"getMonth", ("getUTCDate", crate::builtins::date::date_get_utc_date as *const (), 0),
crate::builtins::date::date_get_month as *const (), ("getUTCDay", crate::builtins::date::date_get_utc_day as *const (), 0),
0, ("getUTCHours", crate::builtins::date::date_get_utc_hours as *const (), 0),
), ("getUTCMinutes", crate::builtins::date::date_get_utc_minutes as *const (), 0),
( ("getUTCSeconds", crate::builtins::date::date_get_utc_seconds as *const (), 0),
"getDate", ("getUTCMilliseconds", crate::builtins::date::date_get_utc_milliseconds as *const (), 0),
crate::builtins::date::date_get_date as *const (), ("getTimezoneOffset", crate::builtins::date::date_get_timezone_offset as *const (), 0),
0, ("setTime", crate::builtins::date::date_set_time as *const (), 1),
), ("setFullYear", crate::builtins::date::date_set_full_year as *const (), 3),
( ("setMonth", crate::builtins::date::date_set_month as *const (), 2),
"getDay", ("setDate", crate::builtins::date::date_set_date as *const (), 1),
crate::builtins::date::date_get_day as *const (), ("setHours", crate::builtins::date::date_set_hours as *const (), 4),
0, ("setMinutes", crate::builtins::date::date_set_minutes as *const (), 3),
), ("setSeconds", crate::builtins::date::date_set_seconds as *const (), 2),
( ("setMilliseconds", crate::builtins::date::date_set_milliseconds as *const (), 1),
"getHours", ("toISOString", crate::builtins::date::date_to_iso_string as *const (), 0),
crate::builtins::date::date_get_hours as *const (), ("toJSON", crate::builtins::date::date_to_json as *const (), 1),
0, ("toString", crate::builtins::date::date_to_string as *const (), 0),
), ("toDateString", crate::builtins::date::date_to_date_string as *const (), 0),
( ("toTimeString", crate::builtins::date::date_to_time_string as *const (), 0),
"getMinutes", ("toUTCString", crate::builtins::date::date_to_utc_string as *const (), 0),
crate::builtins::date::date_get_minutes as *const (), ("valueOf", crate::builtins::date::date_value_of as *const (), 0),
0,
),
(
"getSeconds",
crate::builtins::date::date_get_seconds as *const (),
0,
),
(
"getMilliseconds",
crate::builtins::date::date_get_milliseconds as *const (),
0,
),
(
"getUTCFullYear",
crate::builtins::date::date_get_utc_full_year as *const (),
0,
),
(
"getUTCMonth",
crate::builtins::date::date_get_utc_month as *const (),
0,
),
(
"getUTCDate",
crate::builtins::date::date_get_utc_date as *const (),
0,
),
(
"getUTCDay",
crate::builtins::date::date_get_utc_day as *const (),
0,
),
(
"getUTCHours",
crate::builtins::date::date_get_utc_hours as *const (),
0,
),
(
"getUTCMinutes",
crate::builtins::date::date_get_utc_minutes as *const (),
0,
),
(
"getUTCSeconds",
crate::builtins::date::date_get_utc_seconds as *const (),
0,
),
(
"getUTCMilliseconds",
crate::builtins::date::date_get_utc_milliseconds as *const (),
0,
),
(
"getTimezoneOffset",
crate::builtins::date::date_get_timezone_offset as *const (),
0,
),
(
"setTime",
crate::builtins::date::date_set_time as *const (),
1,
),
(
"setFullYear",
crate::builtins::date::date_set_full_year as *const (),
3,
),
(
"setMonth",
crate::builtins::date::date_set_month as *const (),
2,
),
(
"setDate",
crate::builtins::date::date_set_date as *const (),
1,
),
(
"setHours",
crate::builtins::date::date_set_hours as *const (),
4,
),
(
"setMinutes",
crate::builtins::date::date_set_minutes as *const (),
3,
),
(
"setSeconds",
crate::builtins::date::date_set_seconds as *const (),
2,
),
(
"setMilliseconds",
crate::builtins::date::date_set_milliseconds as *const (),
1,
),
(
"toISOString",
crate::builtins::date::date_to_iso_string as *const (),
0,
),
(
"toJSON",
crate::builtins::date::date_to_json as *const (),
1,
),
(
"toString",
crate::builtins::date::date_to_string as *const (),
0,
),
(
"toDateString",
crate::builtins::date::date_to_date_string as *const (),
0,
),
(
"toTimeString",
crate::builtins::date::date_to_time_string as *const (),
0,
),
(
"toUTCString",
crate::builtins::date::date_to_utc_string as *const (),
0,
),
(
"valueOf",
crate::builtins::date::date_value_of as *const (),
0,
),
], ],
); );
+7 -21
View File
@@ -7,20 +7,13 @@ use oxide_types::object::JsObject;
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
fn bind_error_subtype_constructor( fn bind_error_subtype_constructor(
kernel: &Arc<OxideKernel>, kernel: &Arc<OxideKernel>, global: &mut JsObject, name: &str, proto_ptr: *mut JsObject, ctor_fn: *const (),
global: &mut JsObject,
name: &str,
proto_ptr: *mut JsObject,
ctor_fn: *const (),
) { ) {
let sf = kernel.string_forge().as_ref(); let sf = kernel.string_forge().as_ref();
let sh = kernel.shape_forge().as_ref(); let sh = kernel.shape_forge().as_ref();
let function_proto_ptr = kernel.builtin_world().function_proto.as_ptr() as *mut JsObject; let function_proto_ptr = kernel.builtin_world().function_proto.as_ptr() as *mut JsObject;
let mut ctor = Box::new(JsObject::new_empty( let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto_ptr)));
EMPTY_SHAPE_ID,
JsValue::from_js_object(function_proto_ptr),
));
ctor.set_function(true); ctor.set_function(true);
ctor.set_native_fn(Some(ctor_fn)); ctor.set_native_fn(Some(ctor_fn));
ctor.set_native_arg_count(1); ctor.set_native_arg_count(1);
@@ -32,25 +25,19 @@ fn bind_error_subtype_constructor(
let ctor_shape1 = sh.make_shape(EMPTY_SHAPE_ID, si_prototype); let ctor_shape1 = sh.make_shape(EMPTY_SHAPE_ID, si_prototype);
let ctor_shape2 = sh.make_shape(ctor_shape1, si_name); let ctor_shape2 = sh.make_shape(ctor_shape1, si_name);
ctor.set_shape_id(ctor_shape2); ctor.set_shape_id(ctor_shape2);
ctor.ensure_hash_props() ctor.ensure_hash_props().push(Box::new(JsValue::from_js_object(proto_ptr)));
.push(Box::new(JsValue::from_js_object(proto_ptr))); ctor.ensure_hash_props().push(Box::new(JsValue::string(name_si, 0)));
ctor.ensure_hash_props()
.push(Box::new(JsValue::string(name_si, 0)));
let ctor_ptr = Box::into_raw(ctor); let ctor_ptr = Box::into_raw(ctor);
let proto = unsafe { &mut *proto_ptr }; let proto = unsafe { &mut *proto_ptr };
let proto_ctor_shape = sh.make_shape(proto.shape_id(), sf.intern("constructor").0); let proto_ctor_shape = sh.make_shape(proto.shape_id(), sf.intern("constructor").0);
proto.set_shape_id(proto_ctor_shape); proto.set_shape_id(proto_ctor_shape);
proto proto.ensure_hash_props().push(Box::new(JsValue::from_js_object(ctor_ptr)));
.ensure_hash_props()
.push(Box::new(JsValue::from_js_object(ctor_ptr)));
let global_shape = sh.make_shape(global.shape_id(), name_si); let global_shape = sh.make_shape(global.shape_id(), name_si);
global.set_shape_id(global_shape); global.set_shape_id(global_shape);
global global.ensure_hash_props().push(Box::new(JsValue::from_js_object(ctor_ptr)));
.ensure_hash_props()
.push(Box::new(JsValue::from_js_object(ctor_ptr)));
global.bump_generation(); global.bump_generation();
} }
@@ -74,8 +61,7 @@ pub fn bind_error(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let si_err = kernel.string_forge().intern("Error").0; let si_err = kernel.string_forge().intern("Error").0;
let err_shape = kernel.shape_forge().make_shape(global.shape_id(), si_err); let err_shape = kernel.shape_forge().make_shape(global.shape_id(), si_err);
let err_val = let err_val = JsValue::from_js_object(kernel.builtin_world().error_constructor.as_ptr() as *mut JsObject);
JsValue::from_js_object(kernel.builtin_world().error_constructor.as_ptr() as *mut JsObject);
global.set_shape_id(err_shape); global.set_shape_id(err_shape);
global.ensure_hash_props().push(Box::new(err_val)); global.ensure_hash_props().push(Box::new(err_val));
global.bump_generation(); global.bump_generation();
+1 -5
View File
@@ -15,11 +15,7 @@ pub fn bind_json(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
kernel, kernel,
&[ &[
("parse", crate::builtins::json::json_parse as *const (), 1), ("parse", crate::builtins::json::json_parse as *const (), 1),
( ("stringify", crate::builtins::json::json_stringify as *const (), 1),
"stringify",
crate::builtins::json::json_stringify as *const (),
1,
),
], ],
); );
+1 -8
View File
@@ -27,12 +27,5 @@ pub fn bind_map(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
], ],
); );
bind_constructor_hash!( bind_constructor_hash!(kernel, global, "Map", ctor_ptr, crate::builtins::map::map_constructor, 1);
kernel,
global,
"Map",
ctor_ptr,
crate::builtins::map::map_constructor,
1
);
} }
+2 -6
View File
@@ -66,18 +66,14 @@ pub fn bind_math(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
("SQRT2", std::f64::consts::SQRT_2), ("SQRT2", std::f64::consts::SQRT_2),
] { ] {
let si = kernel.string_forge().as_ref().intern(name).0; let si = kernel.string_forge().as_ref().intern(name).0;
let sh_c = kernel let sh_c = kernel.shape_forge().as_ref().make_shape(math.shape_id(), si);
.shape_forge()
.as_ref()
.make_shape(math.shape_id(), si);
math.set_shape_id(sh_c); math.set_shape_id(sh_c);
math.ensure_hash_props().push(Box::new(JsValue::float(val))); math.ensure_hash_props().push(Box::new(JsValue::float(val)));
} }
let si_m = kernel.string_forge().intern("Math").0; let si_m = kernel.string_forge().intern("Math").0;
let m_shape = kernel.shape_forge().make_shape(global.shape_id(), si_m); let m_shape = kernel.shape_forge().make_shape(global.shape_id(), si_m);
let m_val = let m_val = JsValue::from_js_object(kernel.builtin_world().math_object.as_ptr() as *mut JsObject);
JsValue::from_js_object(kernel.builtin_world().math_object.as_ptr() as *mut JsObject);
global.set_shape_id(m_shape); global.set_shape_id(m_shape);
global.ensure_hash_props().push(Box::new(m_val)); global.ensure_hash_props().push(Box::new(m_val));
global.bump_generation(); global.bump_generation();
+3 -9
View File
@@ -28,9 +28,7 @@ pub fn bind_number(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let si_num = kernel.string_forge().intern("Number").0; let si_num = kernel.string_forge().intern("Number").0;
let num_shape = kernel.shape_forge().make_shape(global.shape_id(), si_num); let num_shape = kernel.shape_forge().make_shape(global.shape_id(), si_num);
let num_val = JsValue::from_js_object( let num_val = JsValue::from_js_object(kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject);
kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject
);
global.set_shape_id(num_shape); global.set_shape_id(num_shape);
global.ensure_hash_props().push(Box::new(num_val)); global.ensure_hash_props().push(Box::new(num_val));
global.bump_generation(); global.bump_generation();
@@ -38,9 +36,7 @@ pub fn bind_number(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
{ {
let num_ctor_ptr = kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject; let num_ctor_ptr = kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject;
let num_ctor = unsafe { &mut *num_ctor_ptr }; let num_ctor = unsafe { &mut *num_ctor_ptr };
num_ctor.set_native_fn(Some( num_ctor.set_native_fn(Some(crate::builtins::number::number_constructor as *const ()));
crate::builtins::number::number_constructor as *const (),
));
num_ctor.set_native_arg_count(1); num_ctor.set_native_arg_count(1);
for (name, value) in [ for (name, value) in [
@@ -55,9 +51,7 @@ pub fn bind_number(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
] { ] {
num_ctor.ensure_hash_props().push(Box::new(value)); num_ctor.ensure_hash_props().push(Box::new(value));
let prop_si = kernel.string_forge().intern(name).0; let prop_si = kernel.string_forge().intern(name).0;
let next_shape = kernel let next_shape = kernel.shape_forge().make_shape(num_ctor.shape_id(), prop_si);
.shape_forge()
.make_shape(num_ctor.shape_id(), prop_si);
num_ctor.set_shape_id(next_shape); num_ctor.set_shape_id(next_shape);
} }
} }
+5 -15
View File
@@ -11,8 +11,7 @@ pub fn bind_object(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
create: crate::builtins::object::object_create as *const (), create: crate::builtins::object::object_create as *const (),
assign: crate::builtins::object::object_assign as *const (), assign: crate::builtins::object::object_assign as *const (),
define_property: crate::builtins::object::object_define_property as *const (), define_property: crate::builtins::object::object_define_property as *const (),
get_own_property_descriptor: crate::builtins::object::object_get_own_property_descriptor get_own_property_descriptor: crate::builtins::object::object_get_own_property_descriptor as *const (),
as *const (),
freeze: crate::builtins::object::object_freeze as *const (), freeze: crate::builtins::object::object_freeze as *const (),
seal: crate::builtins::object::object_seal as *const (), seal: crate::builtins::object::object_seal as *const (),
prevent_extensions: crate::builtins::object::object_prevent_extensions as *const (), prevent_extensions: crate::builtins::object::object_prevent_extensions as *const (),
@@ -27,19 +26,10 @@ pub fn bind_object(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
entries: crate::builtins::object::object_entries as *const (), entries: crate::builtins::object::object_entries as *const (),
values: crate::builtins::object::object_values as *const (), values: crate::builtins::object::object_values as *const (),
}; };
kernel.builtin_world().bind_object_methods( kernel
&methods, .builtin_world()
kernel.string_forge().as_ref(), .bind_object_methods(&methods, kernel.string_forge().as_ref(), kernel.shape_forge().as_ref());
kernel.shape_forge().as_ref(),
);
let ctor_ptr = kernel.builtin_world().object_constructor.as_ptr() as *mut JsObject; let ctor_ptr = kernel.builtin_world().object_constructor.as_ptr() as *mut JsObject;
bind_constructor!( bind_constructor!(kernel, global, "Object", ctor_ptr, crate::builtins::object::object_constructor, 1);
kernel,
global,
"Object",
ctor_ptr,
crate::builtins::object::object_constructor,
1
);
} }
+3 -18
View File
@@ -12,11 +12,7 @@ pub fn bind_regexp(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let proto_ptr = kernel.builtin_world().regexp_proto.as_ptr() as *mut JsObject; let proto_ptr = kernel.builtin_world().regexp_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr }; let proto = unsafe { &mut *proto_ptr };
configure_native_constructor( configure_native_constructor(ctor, crate::builtins::regexp::regexp_constructor as *const (), 2);
ctor,
crate::builtins::regexp::regexp_constructor as *const (),
2,
);
apply_binding_table( apply_binding_table(
kernel.builtin_world(), kernel.builtin_world(),
@@ -25,20 +21,9 @@ pub fn bind_regexp(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
&[ &[
("exec", crate::builtins::regexp::regexp_exec as *const (), 1), ("exec", crate::builtins::regexp::regexp_exec as *const (), 1),
("test", crate::builtins::regexp::regexp_test as *const (), 1), ("test", crate::builtins::regexp::regexp_test as *const (), 1),
( ("toString", crate::builtins::regexp::regexp_to_string as *const (), 0),
"toString",
crate::builtins::regexp::regexp_to_string as *const (),
0,
),
], ],
); );
bind_constructor_hash!( bind_constructor_hash!(kernel, global, "RegExp", ctor_ptr, crate::builtins::regexp::regexp_constructor, 2);
kernel,
global,
"RegExp",
ctor_ptr,
crate::builtins::regexp::regexp_constructor,
2
);
} }
+1 -8
View File
@@ -26,12 +26,5 @@ pub fn bind_set(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
], ],
); );
bind_constructor_hash!( bind_constructor_hash!(kernel, global, "Set", ctor_ptr, crate::builtins::set::set_constructor, 1);
kernel,
global,
"Set",
ctor_ptr,
crate::builtins::set::set_constructor,
1
);
} }
+1 -3
View File
@@ -41,9 +41,7 @@ pub fn bind_string(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let si_str = kernel.string_forge().intern("String").0; let si_str = kernel.string_forge().intern("String").0;
let str_shape = kernel.shape_forge().make_shape(global.shape_id(), si_str); let str_shape = kernel.shape_forge().make_shape(global.shape_id(), si_str);
let str_val = JsValue::from_js_object( let str_val = JsValue::from_js_object(kernel.builtin_world().string_constructor.as_ptr() as *mut JsObject);
kernel.builtin_world().string_constructor.as_ptr() as *mut JsObject
);
global.set_shape_id(str_shape); global.set_shape_id(str_shape);
global.ensure_hash_props().push(Box::new(str_val)); global.ensure_hash_props().push(Box::new(str_val));
global.bump_generation(); global.bump_generation();
+4 -24
View File
@@ -13,21 +13,13 @@ pub fn bind_symbol(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let proto_ptr = kernel.builtin_world().symbol_proto.as_ptr() as *mut JsObject; let proto_ptr = kernel.builtin_world().symbol_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr }; let proto = unsafe { &mut *proto_ptr };
configure_native_constructor( configure_native_constructor(ctor, crate::builtins::symbol::symbol_constructor as *const (), 1);
ctor,
crate::builtins::symbol::symbol_constructor as *const (),
1,
);
apply_binding_table( apply_binding_table(
kernel.builtin_world(), kernel.builtin_world(),
proto, proto,
kernel, kernel,
&[( &[("toString", crate::builtins::symbol::symbol_to_string as *const (), 0)],
"toString",
crate::builtins::symbol::symbol_to_string as *const (),
0,
)],
); );
for (name, val) in [ for (name, val) in [
@@ -55,22 +47,10 @@ pub fn bind_symbol(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
bind_well_known_symbol(kernel, ctor, name, val); bind_well_known_symbol(kernel, ctor, name, val);
} }
bind_constructor_hash!( bind_constructor_hash!(kernel, global, "Symbol", ctor_ptr, crate::builtins::symbol::symbol_constructor, 1);
kernel,
global,
"Symbol",
ctor_ptr,
crate::builtins::symbol::symbol_constructor,
1
);
} }
fn bind_well_known_symbol( fn bind_well_known_symbol(kernel: &Arc<OxideKernel>, ctor: &mut JsObject, name: &str, val: JsValue) {
kernel: &Arc<OxideKernel>,
ctor: &mut JsObject,
name: &str,
val: JsValue,
) {
let si = kernel.string_forge().intern(name).0; let si = kernel.string_forge().intern(name).0;
let shape_id = kernel.shape_forge().make_shape(ctor.shape_id(), si); let shape_id = kernel.shape_forge().make_shape(ctor.shape_id(), si);
ctor.set_shape_id(shape_id); ctor.set_shape_id(shape_id);
+3 -15
View File
@@ -49,20 +49,13 @@ macro_rules! bind_constructor_hash {
}}; }};
} }
pub(crate) fn configure_native_constructor( pub(crate) fn configure_native_constructor(ctor: &mut JsObject, native_fn: *const (), arg_count: u8) {
ctor: &mut JsObject,
native_fn: *const (),
arg_count: u8,
) {
ctor.set_native_fn(Some(native_fn)); ctor.set_native_fn(Some(native_fn));
ctor.set_native_arg_count(arg_count); ctor.set_native_arg_count(arg_count);
} }
pub(crate) fn apply_binding_table( pub(crate) fn apply_binding_table(
world: &BuiltinWorld, world: &BuiltinWorld, target: &mut JsObject, kernel: &Arc<OxideKernel>, bindings: &[(&'static str, *const (), u8)],
target: &mut JsObject,
kernel: &Arc<OxideKernel>,
bindings: &[(&'static str, *const (), u8)],
) { ) {
let shape_forge = kernel.shape_forge().as_ref(); let shape_forge = kernel.shape_forge().as_ref();
let string_forge = kernel.string_forge().as_ref(); let string_forge = kernel.string_forge().as_ref();
@@ -71,12 +64,7 @@ pub(crate) fn apply_binding_table(
} }
} }
pub(crate) fn bind_global_value( pub(crate) fn bind_global_value(kernel: &Arc<OxideKernel>, global: &mut JsObject, name: &str, value: JsValue) {
kernel: &Arc<OxideKernel>,
global: &mut JsObject,
name: &str,
value: JsValue,
) {
let si = kernel.string_forge().intern(name).0; let si = kernel.string_forge().intern(name).0;
let shape = kernel.shape_forge().make_shape(global.shape_id(), si); let shape = kernel.shape_forge().make_shape(global.shape_id(), si);
global.set_shape_id(shape); global.set_shape_id(shape);
+35 -145
View File
@@ -37,20 +37,11 @@ fn get_this_array_ref(val: JsValue) -> Result<*mut JsObject, JsValue> {
fn create_new_array(vm: &mut Vm, n: usize) -> *mut JsObject { fn create_new_array(vm: &mut Vm, n: usize) -> *mut JsObject {
let proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject; let proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
vm.epoch().alloc(JsObject::new_array( vm.epoch()
EMPTY_SHAPE_ID, .alloc(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n, vm.epoch().bump()))
JsValue::from_js_object(proto),
n,
vm.epoch().bump(),
))
} }
fn invoke_native_callback( fn invoke_native_callback(vm: &mut Vm, callback_val: JsValue, this_val: JsValue, cb_args: &[JsValue]) -> NativeResult {
vm: &mut Vm,
callback_val: JsValue,
this_val: JsValue,
cb_args: &[JsValue],
) -> NativeResult {
if !callback_val.is_object() { if !callback_val.is_object() {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
@@ -85,33 +76,24 @@ pub fn array_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(args[1]); let val = vm.reg(args[1]);
if val.is_int() { if val.is_int() {
let n = val.as_int().max(0) as usize; let n = val.as_int().max(0) as usize;
let arr = vm.epoch().alloc(JsObject::new_array( let arr = vm
EMPTY_SHAPE_ID, .epoch()
proto_val, .alloc(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n, vm.epoch().bump()));
n,
vm.epoch().bump(),
));
return Ok(JsValue::from_js_object(arr)); return Ok(JsValue::from_js_object(arr));
} }
if val.is_double() { if val.is_double() {
let n = val.as_double() as usize; let n = val.as_double() as usize;
let arr = vm.epoch().alloc(JsObject::new_array( let arr = vm
EMPTY_SHAPE_ID, .epoch()
proto_val, .alloc(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n, vm.epoch().bump()));
n,
vm.epoch().bump(),
));
return Ok(JsValue::from_js_object(arr)); return Ok(JsValue::from_js_object(arr));
} }
} }
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 }; let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
let arr = vm.epoch().alloc(JsObject::new_array( let arr = vm
EMPTY_SHAPE_ID, .epoch()
proto_val, .alloc(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n_elems, vm.epoch().bump()));
n_elems,
vm.epoch().bump(),
));
for i in 0..n_elems { for i in 0..n_elems {
unsafe { unsafe {
(*arr).set_prop_at(i, vm.reg(args[1 + i])); (*arr).set_prop_at(i, vm.reg(args[1 + i]));
@@ -359,8 +341,7 @@ pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr = unsafe { &*arr_ptr }; let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize; let n = arr.prop_count() as usize;
let depth = if args.len() > 1 { let depth = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32).max(1) (coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32).max(1) as usize
as usize
} else { } else {
1 1
}; };
@@ -407,19 +388,10 @@ pub fn array_for_each(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !callback_val.is_object() { if !callback_val.is_object() {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
let _ = invoke_native_callback( let _ = invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]);
vm,
callback_val,
this_val,
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
);
} }
Ok(JsValue::undefined()) Ok(JsValue::undefined())
} }
@@ -433,20 +405,11 @@ pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !callback_val.is_object() { if !callback_val.is_object() {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
let new_arr = create_new_array(vm, n); let new_arr = create_new_array(vm, n);
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback( match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
vm,
callback_val,
this_val,
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
Ok(mapped) => unsafe { Ok(mapped) => unsafe {
(*new_arr).set_prop_at(i, mapped); (*new_arr).set_prop_at(i, mapped);
}, },
@@ -468,20 +431,11 @@ pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !callback_val.is_object() { if !callback_val.is_object() {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
let mut kept: Vec<JsValue> = Vec::new(); let mut kept: Vec<JsValue> = Vec::new();
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback( match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
vm,
callback_val,
this_val,
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
Ok(result_val) => { Ok(result_val) => {
let sf = vm.kernel().string_forge().as_ref(); let sf = vm.kernel().string_forge().as_ref();
if coercion::to_boolean(result_val, sf) { if coercion::to_boolean(result_val, sf) {
@@ -548,19 +502,10 @@ pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !callback_val.is_object() { if !callback_val.is_object() {
return Ok(JsValue::undefined()); return Ok(JsValue::undefined());
} }
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback( match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
vm,
callback_val,
this_val,
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
Ok(result_val) => { Ok(result_val) => {
let sf = vm.kernel().string_forge().as_ref(); let sf = vm.kernel().string_forge().as_ref();
if coercion::to_boolean(result_val, sf) { if coercion::to_boolean(result_val, sf) {
@@ -582,19 +527,10 @@ pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !callback_val.is_object() { if !callback_val.is_object() {
return Ok(JsValue::bool(false)); return Ok(JsValue::bool(false));
} }
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback( match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
vm,
callback_val,
this_val,
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
Ok(result_val) => { Ok(result_val) => {
let sf = vm.kernel().string_forge().as_ref(); let sf = vm.kernel().string_forge().as_ref();
if coercion::to_boolean(result_val, sf) { if coercion::to_boolean(result_val, sf) {
@@ -616,19 +552,10 @@ pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !callback_val.is_object() { if !callback_val.is_object() {
return Ok(JsValue::bool(false)); return Ok(JsValue::bool(false));
} }
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback( match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
vm,
callback_val,
this_val,
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
Ok(result_val) => { Ok(result_val) => {
let sf = vm.kernel().string_forge().as_ref(); let sf = vm.kernel().string_forge().as_ref();
if !coercion::to_boolean(result_val, sf) { if !coercion::to_boolean(result_val, sf) {
@@ -650,20 +577,11 @@ pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !callback_val.is_object() { if !callback_val.is_object() {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
let mut flat: Vec<JsValue> = Vec::new(); let mut flat: Vec<JsValue> = Vec::new();
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback( match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
vm,
callback_val,
this_val,
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
Ok(result) => { Ok(result) => {
if result.is_object() { if result.is_object() {
let r_ptr = result.as_js_object_ptr(); let r_ptr = result.as_js_object_ptr();
@@ -730,14 +648,9 @@ pub fn array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args); let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &mut *arr_ptr }; let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count() as usize; let len = arr.prop_count() as usize;
let value = if args.len() > 1 { let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
vm.reg(args[1])
} else {
JsValue::undefined()
};
let start = if args.len() > 2 { let start = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32).max(0) (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32).max(0) as usize
as usize
} else { } else {
0 0
}; };
@@ -771,8 +684,7 @@ pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult {
0 0
}; };
let end = if args.len() > 3 { let end = if args.len() > 3 {
(coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32 as usize) (coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32 as usize).min(len)
.min(len)
} else { } else {
len len
}; };
@@ -810,11 +722,7 @@ pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args); let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &*arr_ptr }; let arr = unsafe { &*arr_ptr };
let len = arr.prop_count() as i32; let len = arr.prop_count() as i32;
let search = if args.len() > 1 { let search = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
vm.reg(args[1])
} else {
JsValue::undefined()
};
for i in (0..len).rev() { for i in (0..len).rev() {
if coercion::strict_equality(arr.get_prop_at(i), search) { if coercion::strict_equality(arr.get_prop_at(i), search) {
return Ok(JsValue::int(i)); return Ok(JsValue::int(i));
@@ -829,19 +737,10 @@ pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult {
return Ok(JsValue::int(-1)); return Ok(JsValue::int(-1));
} }
let callback_val = vm.reg(args[1]); let callback_val = vm.reg(args[1]);
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
for i in 0..n { for i in 0..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback( match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
vm,
callback_val,
this_val,
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
Ok(r) => { Ok(r) => {
if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) { if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) {
return Ok(JsValue::int(i as i32)); return Ok(JsValue::int(i as i32));
@@ -862,19 +761,10 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult {
return Ok(JsValue::undefined()); return Ok(JsValue::undefined());
} }
let callback_val = vm.reg(args[1]); let callback_val = vm.reg(args[1]);
let this_val = if args.len() > 2 { let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
vm.reg(args[2])
} else {
JsValue::undefined()
};
for i in (0..n).rev() { for i in (0..n).rev() {
let elem = unsafe { (*arr_ptr).get_prop_at(i) }; let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback( match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i), vm.reg(args[0])]) {
vm,
callback_val,
this_val,
&[elem, JsValue::int(i), vm.reg(args[0])],
) {
Ok(r) => { Ok(r) => {
if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) { if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) {
return Ok(elem); return Ok(elem);
+1 -5
View File
@@ -13,11 +13,7 @@ pub fn boolean_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
} else if arg.is_bool() { } else if arg.is_bool() {
arg.as_bool() arg.as_bool()
} else if arg.is_string() { } else if arg.is_string() {
let s = vm let s = vm.kernel().string_forge().lookup(arg.as_string_index()).unwrap_or_default();
.kernel()
.string_forge()
.lookup(arg.as_string_index())
.unwrap_or_default();
!s.is_empty() !s.is_empty()
} else if arg.is_object() { } else if arg.is_object() {
true true
+27 -124
View File
@@ -30,10 +30,7 @@ fn is_date(vm: &Vm, obj: &JsObject) -> bool {
fn ensure_date(vm: &mut Vm, obj: &JsObject) -> NativeResult { fn ensure_date(vm: &mut Vm, obj: &JsObject) -> NativeResult {
if !is_date(vm, obj) { if !is_date(vm, obj) {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
vm,
"called on incompatible receiver",
));
} }
Ok(JsValue::undefined()) Ok(JsValue::undefined())
} }
@@ -114,11 +111,7 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
} else { } else {
let val = vm.reg(args[1]); let val = vm.reg(args[1]);
if val.is_string() { if val.is_string() {
let s = vm let s = vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default();
.kernel()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"]; let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
let mut ts = f64::NAN; let mut ts = f64::NAN;
for fmt in &formats { for fmt in &formats {
@@ -129,10 +122,7 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
} }
if ts.is_nan() { if ts.is_nan() {
if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") { if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") {
if let Some(ndt) = nd if let Some(ndt) = nd.and_hms_opt(0, 0, 0).and_then(|n| n.and_local_timezone(Utc).earliest()) {
.and_hms_opt(0, 0, 0)
.and_then(|n| n.and_local_timezone(Utc).earliest())
{
ts = ndt.timestamp_millis() as f64; ts = ndt.timestamp_millis() as f64;
} }
} }
@@ -192,11 +182,7 @@ pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !val.is_string() { if !val.is_string() {
return Ok(JsValue::float(f64::NAN)); return Ok(JsValue::float(f64::NAN));
} }
let s = vm let s = vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default();
.kernel()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"]; let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
let mut ts = f64::NAN; let mut ts = f64::NAN;
for fmt in &formats { for fmt in &formats {
@@ -207,10 +193,7 @@ pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
} }
if ts.is_nan() { if ts.is_nan() {
if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") { if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") {
if let Some(ndt) = nd if let Some(ndt) = nd.and_hms_opt(0, 0, 0).and_then(|n| n.and_local_timezone(Utc).earliest()) {
.and_hms_opt(0, 0, 0)
.and_then(|n| n.and_local_timezone(Utc).earliest())
{
ts = ndt.timestamp_millis() as f64; ts = ndt.timestamp_millis() as f64;
} }
} }
@@ -227,10 +210,7 @@ macro_rules! make_getter {
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] }); let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() { if !raw.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
vm,
"called on incompatible receiver",
));
} }
let obj = unsafe { &*raw.as_js_object_ptr() }; let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?; ensure_date(vm, obj)?;
@@ -251,10 +231,7 @@ macro_rules! make_utc_getter {
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] }); let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() { if !raw.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
vm,
"called on incompatible receiver",
));
} }
let obj = unsafe { &*raw.as_js_object_ptr() }; let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?; ensure_date(vm, obj)?;
@@ -270,83 +247,27 @@ macro_rules! make_utc_getter {
}; };
} }
make_getter!( make_getter!(date_get_time, |dt: DateTime<Utc>| dt.timestamp_millis() as f64, f64::NAN);
date_get_time, make_getter!(date_get_full_year, |dt: DateTime<Utc>| dt.year() as f64, f64::NAN);
|dt: DateTime<Utc>| dt.timestamp_millis() as f64, make_getter!(date_get_month, |dt: DateTime<Utc>| dt.month0() as f64, f64::NAN);
f64::NAN
);
make_getter!(
date_get_full_year,
|dt: DateTime<Utc>| dt.year() as f64,
f64::NAN
);
make_getter!(
date_get_month,
|dt: DateTime<Utc>| dt.month0() as f64,
f64::NAN
);
make_getter!(date_get_date, |dt: DateTime<Utc>| dt.day() as f64, f64::NAN); make_getter!(date_get_date, |dt: DateTime<Utc>| dt.day() as f64, f64::NAN);
make_getter!( make_getter!(date_get_day, |dt: DateTime<Utc>| dt.weekday().num_days_from_sunday() as f64, f64::NAN);
date_get_day, make_getter!(date_get_hours, |dt: DateTime<Utc>| dt.hour() as f64, f64::NAN);
|dt: DateTime<Utc>| dt.weekday().num_days_from_sunday() as f64, make_getter!(date_get_minutes, |dt: DateTime<Utc>| dt.minute() as f64, f64::NAN);
f64::NAN make_getter!(date_get_seconds, |dt: DateTime<Utc>| dt.second() as f64, f64::NAN);
); make_getter!(date_get_milliseconds, |dt: DateTime<Utc>| dt.timestamp_subsec_millis() as f64, f64::NAN);
make_getter!(
date_get_hours,
|dt: DateTime<Utc>| dt.hour() as f64,
f64::NAN
);
make_getter!(
date_get_minutes,
|dt: DateTime<Utc>| dt.minute() as f64,
f64::NAN
);
make_getter!(
date_get_seconds,
|dt: DateTime<Utc>| dt.second() as f64,
f64::NAN
);
make_getter!(
date_get_milliseconds,
|dt: DateTime<Utc>| dt.timestamp_subsec_millis() as f64,
f64::NAN
);
make_utc_getter!( make_utc_getter!(date_get_utc_full_year, |ndt: NaiveDateTime| ndt.date().year() as f64, f64::NAN);
date_get_utc_full_year, make_utc_getter!(date_get_utc_month, |ndt: NaiveDateTime| ndt.date().month0() as f64, f64::NAN);
|ndt: NaiveDateTime| ndt.date().year() as f64, make_utc_getter!(date_get_utc_date, |ndt: NaiveDateTime| ndt.date().day() as f64, f64::NAN);
f64::NAN
);
make_utc_getter!(
date_get_utc_month,
|ndt: NaiveDateTime| ndt.date().month0() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_date,
|ndt: NaiveDateTime| ndt.date().day() as f64,
f64::NAN
);
make_utc_getter!( make_utc_getter!(
date_get_utc_day, date_get_utc_day,
|ndt: NaiveDateTime| ndt.date().weekday().num_days_from_sunday() as f64, |ndt: NaiveDateTime| ndt.date().weekday().num_days_from_sunday() as f64,
f64::NAN f64::NAN
); );
make_utc_getter!( make_utc_getter!(date_get_utc_hours, |ndt: NaiveDateTime| ndt.time().hour() as f64, f64::NAN);
date_get_utc_hours, make_utc_getter!(date_get_utc_minutes, |ndt: NaiveDateTime| ndt.time().minute() as f64, f64::NAN);
|ndt: NaiveDateTime| ndt.time().hour() as f64, make_utc_getter!(date_get_utc_seconds, |ndt: NaiveDateTime| ndt.time().second() as f64, f64::NAN);
f64::NAN
);
make_utc_getter!(
date_get_utc_minutes,
|ndt: NaiveDateTime| ndt.time().minute() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_seconds,
|ndt: NaiveDateTime| ndt.time().second() as f64,
f64::NAN
);
make_utc_getter!( make_utc_getter!(
date_get_utc_milliseconds, date_get_utc_milliseconds,
|ndt: NaiveDateTime| ndt.time().nanosecond() as f64 / 1_000_000.0, |ndt: NaiveDateTime| ndt.time().nanosecond() as f64 / 1_000_000.0,
@@ -356,10 +277,7 @@ make_utc_getter!(
pub fn date_get_timezone_offset(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn date_get_timezone_offset(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] }); let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() { if !raw.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
vm,
"called on incompatible receiver",
));
} }
let obj = unsafe { &*raw.as_js_object_ptr() }; let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?; ensure_date(vm, obj)?;
@@ -382,10 +300,7 @@ fn get_opt_arg(vm: &Vm, args: &[u8], idx: usize, default: u32) -> u32 {
pub fn date_set_time(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn date_set_time(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] }); let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() { if !raw.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
vm,
"called on incompatible receiver",
));
} }
let obj = unsafe { &mut *raw.as_js_object_ptr() }; let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?; ensure_date(vm, obj)?;
@@ -438,10 +353,7 @@ pub fn date_set_month(vm: &mut Vm, args: &[u8]) -> NativeResult {
None => return Ok(JsValue::float(f64::NAN)), None => return Ok(JsValue::float(f64::NAN)),
}; };
let d = get_opt_arg(vm, args, 2, dt.day0()); let d = get_opt_arg(vm, args, 2, dt.day0());
let nd = dt let nd = dt.with_month0(v as u32).and_then(|x| x.with_day0(d)).unwrap_or(dt);
.with_month0(v as u32)
.and_then(|x| x.with_day0(d))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64; let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts); set_timestamp(obj, ts);
Ok(JsValue::float(ts)) Ok(JsValue::float(ts))
@@ -563,10 +475,7 @@ pub fn date_set_milliseconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
fn date_to_string_inner(vm: &mut Vm, args: &[u8], format_str: &str, invalid: &str) -> NativeResult { fn date_to_string_inner(vm: &mut Vm, args: &[u8], format_str: &str, invalid: &str) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] }); let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() { if !raw.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
vm,
"called on incompatible receiver",
));
} }
let obj = unsafe { &*raw.as_js_object_ptr() }; let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?; ensure_date(vm, obj)?;
@@ -588,10 +497,7 @@ pub fn date_to_iso_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn date_to_json(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn date_to_json(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] }); let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() { if !raw.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
vm,
"called on incompatible receiver",
));
} }
let obj = unsafe { &*raw.as_js_object_ptr() }; let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?; ensure_date(vm, obj)?;
@@ -625,10 +531,7 @@ pub fn date_to_utc_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn date_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn date_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] }); let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() { if !raw.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
vm,
"called on incompatible receiver",
));
} }
let obj = unsafe { &*raw.as_js_object_ptr() }; let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?; ensure_date(vm, obj)?;
+8 -26
View File
@@ -9,16 +9,10 @@ use crate::coercion;
use crate::native::NativeResult; use crate::native::NativeResult;
use crate::vm::Vm; use crate::vm::Vm;
fn create_error_object( fn create_error_object(vm: &mut Vm, error_proto_ptr: *mut JsObject, name: &str, message: &str) -> JsValue {
vm: &mut Vm, let obj = vm
error_proto_ptr: *mut JsObject, .epoch()
name: &str, .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(error_proto_ptr)));
message: &str,
) -> JsValue {
let obj = vm.epoch().alloc(JsObject::new_empty(
EMPTY_SHAPE_ID,
JsValue::from_js_object(error_proto_ptr),
));
let sf = Arc::clone(vm.kernel().string_forge()); let sf = Arc::clone(vm.kernel().string_forge());
let sh = Arc::clone(vm.kernel().shape_forge()); let sh = Arc::clone(vm.kernel().shape_forge());
@@ -132,18 +126,13 @@ pub fn error_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
.map(|b| **b) .map(|b| **b)
.unwrap_or(JsValue::undefined()); .unwrap_or(JsValue::undefined());
( (
vm.lookup_str(name_val) vm.lookup_str(name_val).unwrap_or_else(|| "Error".to_string()),
.unwrap_or_else(|| "Error".to_string()),
vm.lookup_str(msg_val).unwrap_or_default(), vm.lookup_str(msg_val).unwrap_or_default(),
) )
} else { } else {
("Error".to_string(), String::new()) ("Error".to_string(), String::new())
}; };
let result = if msg.is_empty() { let result = if msg.is_empty() { name } else { format!("{}: {}", name, msg) };
name
} else {
format!("{}: {}", name, msg)
};
let si = vm.kernel().string_forge().intern(&result).0; let si = vm.kernel().string_forge().intern(&result).0;
Ok(JsValue::string(si, 0)) Ok(JsValue::string(si, 0))
} }
@@ -184,14 +173,7 @@ mod tests {
}); });
let stack = error_stack_getter(&mut vm, &[0]).unwrap(); let stack = error_stack_getter(&mut vm, &[0]).unwrap();
let stack_str = vm let stack_str = vm.kernel().string_forge().lookup(stack.as_string_index()).unwrap_or_default();
.kernel() assert!(stack_str.contains("foo"), "expected function name in stack: {stack_str}");
.string_forge()
.lookup(stack.as_string_index())
.unwrap_or_default();
assert!(
stack_str.contains("foo"),
"expected function name in stack: {stack_str}"
);
} }
} }
+7 -33
View File
@@ -5,12 +5,7 @@ use oxide_types::value::JsValue;
use crate::native::{NativeFn, NativeResult}; use crate::native::{NativeFn, NativeResult};
use crate::vm::Vm; use crate::vm::Vm;
fn invoke_target( fn invoke_target(vm: &mut Vm, target_val: JsValue, this_val: JsValue, arg_regs: &[u8]) -> NativeResult {
vm: &mut Vm,
target_val: JsValue,
this_val: JsValue,
arg_regs: &[u8],
) -> NativeResult {
if !target_val.is_object() { if !target_val.is_object() {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
@@ -79,11 +74,7 @@ pub fn function_call(vm: &mut Vm, args: &[u8]) -> NativeResult {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
let target_val = vm.reg(args[0]); let target_val = vm.reg(args[0]);
let this_val = if args.len() > 1 { let this_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
vm.reg(args[1])
} else {
JsValue::undefined()
};
let arg_regs: Vec<u8> = args.iter().skip(2).copied().collect(); let arg_regs: Vec<u8> = args.iter().skip(2).copied().collect();
invoke_target(vm, target_val, this_val, &arg_regs) invoke_target(vm, target_val, this_val, &arg_regs)
} }
@@ -93,11 +84,7 @@ pub fn function_apply(vm: &mut Vm, args: &[u8]) -> NativeResult {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
let target_val = vm.reg(args[0]); let target_val = vm.reg(args[0]);
let this_val = if args.len() > 1 { let this_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
vm.reg(args[1])
} else {
JsValue::undefined()
};
let arg_regs: Vec<u8>; let arg_regs: Vec<u8>;
if args.len() > 2 { if args.len() > 2 {
@@ -139,15 +126,9 @@ pub fn function_bind(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !target_val.is_object() { if !target_val.is_object() {
return Err(JsValue::undefined()); return Err(JsValue::undefined());
} }
let bound_this = if args.len() > 1 { let bound_this = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
vm.reg(args[1])
} else {
JsValue::undefined()
};
let wrapper = vm let wrapper = vm.epoch().alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
unsafe { unsafe {
(*wrapper).set_function(true); (*wrapper).set_function(true);
(*wrapper).set_native_fn(Some(bind_dispatcher as *const ())); (*wrapper).set_native_fn(Some(bind_dispatcher as *const ()));
@@ -189,20 +170,13 @@ pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
.unwrap_or_else(|| { .unwrap_or_else(|| {
let sub_idx = func.sub_module_index(); let sub_idx = func.sub_module_index();
if sub_idx > 0 && (sub_idx as usize) <= vm.sub_modules.len() { if sub_idx > 0 && (sub_idx as usize) <= vm.sub_modules.len() {
vm.sub_modules[sub_idx as usize - 1] vm.sub_modules[sub_idx as usize - 1].function_name.clone().unwrap_or_default()
.function_name
.clone()
.unwrap_or_default()
} else { } else {
String::new() String::new()
} }
}); });
let body = if func.native_fn().is_some() { let body = if func.native_fn().is_some() { "[native code]" } else { "[bytecode]" };
"[native code]"
} else {
"[bytecode]"
};
let result = if name.is_empty() { let result = if name.is_empty() {
format!("function () {{ {body} }}") format!("function () {{ {body} }}")
} else { } else {
+1 -4
View File
@@ -2,9 +2,6 @@ use std::sync::Arc;
use oxide_kernel::kernel::OxideKernel; use oxide_kernel::kernel::OxideKernel;
pub fn register_global_properties( pub fn register_global_properties(_kernel: &Arc<OxideKernel>, _bump: &bumpalo::Bump) -> Result<(), String> {
_kernel: &Arc<OxideKernel>,
_bump: &bumpalo::Bump,
) -> Result<(), String> {
Ok(()) Ok(())
} }
+8 -30
View File
@@ -11,25 +11,19 @@ use crate::vm::Vm;
pub fn json_parse(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn json_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 { if args.len() < 2 {
return Err(crate::builtins::error::create_syntax_error( return Err(crate::builtins::error::create_syntax_error(vm, "JSON.parse requires 1 argument"));
vm,
"JSON.parse requires 1 argument",
));
} }
let val = vm.reg(args[1]); let val = vm.reg(args[1]);
if !val.is_string() { if !val.is_string() {
return Err(crate::builtins::error::create_syntax_error( return Err(crate::builtins::error::create_syntax_error(vm, "JSON.parse: argument is not a string"));
vm,
"JSON.parse: argument is not a string",
));
} }
let text = { let text = {
let si = val.as_string_index(); let si = val.as_string_index();
vm.kernel().string_forge().lookup(si).unwrap_or_default() vm.kernel().string_forge().lookup(si).unwrap_or_default()
}; };
let parsed: serde_json::Value = serde_json::from_str(&text) let parsed: serde_json::Value =
.map_err(|e| crate::builtins::error::create_syntax_error(vm, &format!("{}", e)))?; serde_json::from_str(&text).map_err(|e| crate::builtins::error::create_syntax_error(vm, &format!("{}", e)))?;
Ok(value_to_jsvalue(vm, &parsed)) Ok(value_to_jsvalue(vm, &parsed))
} }
@@ -65,8 +59,7 @@ fn value_to_jsvalue(vm: &mut Vm, val: &serde_json::Value) -> JsValue {
} }
serde_json::Value::Object(map) => { serde_json::Value::Object(map) => {
let object_proto = vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject; let object_proto = vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject;
let mut obj = let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
for (key, val) in map { for (key, val) in map {
let si = vm.kernel().string_forge().intern(key).0; let si = vm.kernel().string_forge().intern(key).0;
let jsv = value_to_jsvalue(vm, val); let jsv = value_to_jsvalue(vm, val);
@@ -95,12 +88,7 @@ pub fn json_stringify(vm: &mut Vm, args: &[u8]) -> NativeResult {
Ok(vm.intern(&output)) Ok(vm.intern(&output))
} }
fn jsvalue_to_json( fn jsvalue_to_json(vm: &Vm, val: JsValue, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> {
vm: &Vm,
val: JsValue,
visited: &mut HashSet<*const u8>,
out: &mut String,
) -> Result<(), ()> {
if val.is_null() { if val.is_null() {
out.push_str("null"); out.push_str("null");
} else if val.is_undefined() { } else if val.is_undefined() {
@@ -169,12 +157,7 @@ fn stringify_string(s: &str, out: &mut String) {
out.push('"'); out.push('"');
} }
fn stringify_object( fn stringify_object(vm: &Vm, obj: &JsObject, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> {
vm: &Vm,
obj: &JsObject,
visited: &mut HashSet<*const u8>,
out: &mut String,
) -> Result<(), ()> {
out.push('{'); out.push('{');
let keys = walk_own_keys(vm, obj); let keys = walk_own_keys(vm, obj);
let mut first = true; let mut first = true;
@@ -204,12 +187,7 @@ fn stringify_object(
Ok(()) Ok(())
} }
fn stringify_array( fn stringify_array(vm: &Vm, obj: &JsObject, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> {
vm: &Vm,
obj: &JsObject,
visited: &mut HashSet<*const u8>,
out: &mut String,
) -> Result<(), ()> {
out.push('['); out.push('[');
let len = obj.prop_vec_len(); let len = obj.prop_vec_len();
for i in 0..len { for i in 0..len {
+5 -20
View File
@@ -8,37 +8,22 @@ use crate::vm::Vm;
const MAP_PROP_INDEX: u8 = 0; const MAP_PROP_INDEX: u8 = 0;
fn get_map_inner( fn get_map_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut indexmap::IndexMap<SetKey, JsValue>, JsValue> {
vm: &mut Vm,
this_val: JsValue,
) -> Result<*mut indexmap::IndexMap<SetKey, JsValue>, JsValue> {
if !this_val.is_object() { if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on non-Map object"));
vm,
"called on non-Map object",
));
} }
let map_ptr = this_val.as_js_object_ptr(); let map_ptr = this_val.as_js_object_ptr();
if map_ptr.is_null() { if map_ptr.is_null() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "Map internal state invalid"));
vm,
"Map internal state invalid",
));
} }
let map_obj = unsafe { &*map_ptr }; let map_obj = unsafe { &*map_ptr };
let inner_val = map_obj.get_prop_at(MAP_PROP_INDEX); let inner_val = map_obj.get_prop_at(MAP_PROP_INDEX);
if !inner_val.is_object() { if !inner_val.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "Map internal state invalid"));
vm,
"Map internal state invalid",
));
} }
let inner_ptr = inner_val.as_js_object_ptr() as *mut indexmap::IndexMap<SetKey, JsValue>; let inner_ptr = inner_val.as_js_object_ptr() as *mut indexmap::IndexMap<SetKey, JsValue>;
if inner_ptr.is_null() { if inner_ptr.is_null() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "Map internal state invalid"));
vm,
"Map internal state invalid",
));
} }
Ok(inner_ptr) Ok(inner_ptr)
} }
+3 -15
View File
@@ -18,16 +18,8 @@ fn arg1(vm: &Vm, args: &[u8]) -> f64 {
fn arg2(vm: &Vm, args: &[u8]) -> (f64, f64) { fn arg2(vm: &Vm, args: &[u8]) -> (f64, f64) {
( (
if args.len() > 1 { if args.len() > 1 { num(vm, args[1]) } else { f64::NAN },
num(vm, args[1]) if args.len() > 2 { num(vm, args[2]) } else { f64::NAN },
} else {
f64::NAN
},
if args.len() > 2 {
num(vm, args[2])
} else {
f64::NAN
},
) )
} }
@@ -82,11 +74,7 @@ pub fn math_atan2(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn math_round(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn math_round(vm: &mut Vm, args: &[u8]) -> NativeResult {
let x = arg1(vm, args); let x = arg1(vm, args);
let r = if x < 0.0 { let r = if x < 0.0 { (x - 0.5).ceil() } else { (x + 0.5).floor() };
(x - 0.5).ceil()
} else {
(x + 0.5).floor()
};
Ok(JsValue::float(r)) Ok(JsValue::float(r))
} }
+3 -14
View File
@@ -177,10 +177,7 @@ pub fn number_is_integer(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn number_is_safe_integer(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn number_is_safe_integer(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] }); let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] });
let n = coercion::to_number(val, vm.kernel().string_forge().as_ref()); let n = coercion::to_number(val, vm.kernel().string_forge().as_ref());
let safe = n.trunc() == n let safe = n.trunc() == n && n.is_finite() && n >= -9007199254740991i64 as f64 && n <= 9007199254740991i64 as f64;
&& n.is_finite()
&& n >= -9007199254740991i64 as f64
&& n <= 9007199254740991i64 as f64;
Ok(JsValue::bool(safe)) Ok(JsValue::bool(safe))
} }
@@ -196,11 +193,7 @@ pub fn number_to_exponential(vm: &mut Vm, args: &[u8]) -> NativeResult {
return Ok(vm.intern("NaN")); return Ok(vm.intern("NaN"));
} }
if n.is_infinite() { if n.is_infinite() {
return Ok(vm.intern(if n.is_sign_positive() { return Ok(vm.intern(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
"Infinity"
} else {
"-Infinity"
}));
} }
let formatted = format!("{:.digits$e}", n); let formatted = format!("{:.digits$e}", n);
Ok(vm.intern(&formatted)) Ok(vm.intern(&formatted))
@@ -218,11 +211,7 @@ pub fn number_to_precision(vm: &mut Vm, args: &[u8]) -> NativeResult {
return Ok(vm.intern("NaN")); return Ok(vm.intern("NaN"));
} }
if n.is_infinite() { if n.is_infinite() {
return Ok(vm.intern(if n.is_sign_positive() { return Ok(vm.intern(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
"Infinity"
} else {
"-Infinity"
}));
} }
let formatted = format!("{:.precision$}", n); let formatted = format!("{:.precision$}", n);
Ok(vm.intern(&formatted)) Ok(vm.intern(&formatted))
+5 -10
View File
@@ -38,9 +38,7 @@ pub(crate) fn walk_own_keys(vm: &Vm, obj: &JsObject) -> Vec<(u32, u32)> {
} }
pub fn object_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult { pub fn object_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let obj = vm let obj = vm.epoch().alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
Ok(JsValue::from_js_object(obj)) Ok(JsValue::from_js_object(obj))
} }
@@ -95,9 +93,7 @@ pub fn object_create(vm: &mut Vm, args: &[u8]) -> NativeResult {
if !proto_val.is_null() && !proto_val.is_object() { if !proto_val.is_null() && !proto_val.is_object() {
return Ok(JsValue::undefined()); return Ok(JsValue::undefined());
} }
let obj = vm let obj = vm.epoch().alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
Ok(JsValue::from_js_object(obj)) Ok(JsValue::from_js_object(obj))
} }
@@ -210,10 +206,9 @@ pub fn object_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeRes
let sf_ptr = vm.kernel().string_forge().as_ref() as *const StringForge; let sf_ptr = vm.kernel().string_forge().as_ref() as *const StringForge;
let sh_ptr = vm.kernel().shape_forge().as_ref() as *const ShapeForge; let sh_ptr = vm.kernel().shape_forge().as_ref() as *const ShapeForge;
let desc_proto = vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject; let desc_proto = vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject;
let desc = vm.epoch().alloc(JsObject::new_empty( let desc = vm
EMPTY_SHAPE_ID, .epoch()
JsValue::from_js_object(desc_proto), .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(desc_proto)));
));
let true_val = JsValue::bool(true); let true_val = JsValue::bool(true);
let sf = unsafe { &*sf_ptr }; let sf = unsafe { &*sf_ptr };
let sh = unsafe { &*sh_ptr }; let sh = unsafe { &*sh_ptr };
+8 -36
View File
@@ -19,13 +19,7 @@ fn get_regexp_ptr(vm: &Vm, args: &[u8]) -> Result<*mut JsObject, JsValue> {
} }
let ptr = this_val.as_js_object_ptr(); let ptr = this_val.as_js_object_ptr();
if ptr.is_null() { if ptr.is_null() {
return Err(JsValue::string( return Err(JsValue::string(vm.kernel().string_forge().intern("TypeError: null object").0, 0));
vm.kernel()
.string_forge()
.intern("TypeError: null object")
.0,
0,
));
} }
Ok(ptr) Ok(ptr)
} }
@@ -101,10 +95,7 @@ pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
} }
Err(e) => { Err(e) => {
let msg = format!("SyntaxError: Invalid regular expression: {}", e); let msg = format!("SyntaxError: Invalid regular expression: {}", e);
return Err(JsValue::string( return Err(JsValue::string(vm.kernel().string_forge().intern(&msg).0, 0));
vm.kernel().string_forge().intern(&msg).0,
0,
));
} }
} }
@@ -128,13 +119,7 @@ pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult {
let fn_ptr = match re.native_fn() { let fn_ptr = match re.native_fn() {
None => { None => {
return Err(JsValue::string( return Err(JsValue::string(vm.kernel().string_forge().intern("TypeError: invalid RegExp").0, 0));
vm.kernel()
.string_forge()
.intern("TypeError: invalid RegExp")
.0,
0,
));
} }
Some(p) => p, Some(p) => p,
}; };
@@ -144,8 +129,7 @@ pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult {
vm.kernel().string_forge().as_ref(), vm.kernel().string_forge().as_ref(),
vm.reg(if args.len() > 1 { args[1] } else { args[0] }), vm.reg(if args.len() > 1 { args[1] } else { args[0] }),
); );
let last_index = let last_index = coercion::to_number(get_prop(re, 0), vm.kernel().string_forge().as_ref()) as usize;
coercion::to_number(get_prop(re, 0), vm.kernel().string_forge().as_ref()) as usize;
let is_global = get_prop(re, 3).as_bool(); let is_global = get_prop(re, 3).as_bool();
if is_global { if is_global {
@@ -163,13 +147,7 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult {
let re = unsafe { &*re_ptr }; let re = unsafe { &*re_ptr };
match re.native_fn() { match re.native_fn() {
None => { None => {
return Err(JsValue::string( return Err(JsValue::string(vm.kernel().string_forge().intern("TypeError: invalid RegExp").0, 0));
vm.kernel()
.string_forge()
.intern("TypeError: invalid RegExp")
.0,
0,
));
} }
Some(p) => p, Some(p) => p,
} }
@@ -200,25 +178,19 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult {
if let Some(m) = match_result { if let Some(m) = match_result {
let mut match_obj = JsObject::new_empty( let mut match_obj = JsObject::new_empty(
EMPTY_SHAPE_ID, EMPTY_SHAPE_ID,
JsValue::from_js_object( JsValue::from_js_object(vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject),
vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject
),
); );
let full_match = &haystack[m.start()..m.end()]; let full_match = &haystack[m.start()..m.end()];
let full_si = vm.kernel().string_forge().intern(full_match).0; let full_si = vm.kernel().string_forge().intern(full_match).0;
match_obj match_obj.ensure_hash_props().push(Box::new(JsValue::string(full_si, 0)));
.ensure_hash_props()
.push(Box::new(JsValue::string(full_si, 0)));
let captures = regex.captures(&haystack[m.start()..m.end()]); let captures = regex.captures(&haystack[m.start()..m.end()]);
if let Some(caps) = &captures { if let Some(caps) = &captures {
for i in 1..caps.len() { for i in 1..caps.len() {
let cap_str = caps.get(i).map(|cm| cm.as_str()).unwrap_or(""); let cap_str = caps.get(i).map(|cm| cm.as_str()).unwrap_or("");
let si = vm.kernel().string_forge().intern(cap_str).0; let si = vm.kernel().string_forge().intern(cap_str).0;
match_obj match_obj.ensure_hash_props().push(Box::new(JsValue::string(si, 0)));
.ensure_hash_props()
.push(Box::new(JsValue::string(si, 0)));
} }
} }
+6 -24
View File
@@ -25,10 +25,7 @@ impl PartialEq for SetKey {
} }
return a == b; return a == b;
} }
unsafe { unsafe { std::mem::transmute::<JsValue, u64>(self.0) == std::mem::transmute::<JsValue, u64>(other.0) }
std::mem::transmute::<JsValue, u64>(self.0)
== std::mem::transmute::<JsValue, u64>(other.0)
}
} }
} }
@@ -51,37 +48,22 @@ impl Hash for SetKey {
} }
} }
fn get_set_inner( fn get_set_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut indexmap::IndexSet<SetKey>, JsValue> {
vm: &mut Vm,
this_val: JsValue,
) -> Result<*mut indexmap::IndexSet<SetKey>, JsValue> {
if !this_val.is_object() { if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "called on non-Set object"));
vm,
"called on non-Set object",
));
} }
let set_ptr = this_val.as_js_object_ptr(); let set_ptr = this_val.as_js_object_ptr();
if set_ptr.is_null() { if set_ptr.is_null() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "Set internal state invalid"));
vm,
"Set internal state invalid",
));
} }
let set_obj = unsafe { &*set_ptr }; let set_obj = unsafe { &*set_ptr };
let inner_val = set_obj.get_prop_at(SET_PROP_INDEX); let inner_val = set_obj.get_prop_at(SET_PROP_INDEX);
if !inner_val.is_object() { if !inner_val.is_object() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "Set internal state invalid"));
vm,
"Set internal state invalid",
));
} }
let inner_ptr = inner_val.as_js_object_ptr() as *mut indexmap::IndexSet<SetKey>; let inner_ptr = inner_val.as_js_object_ptr() as *mut indexmap::IndexSet<SetKey>;
if inner_ptr.is_null() { if inner_ptr.is_null() {
return Err(crate::builtins::error::create_type_error( return Err(crate::builtins::error::create_type_error(vm, "Set internal state invalid"));
vm,
"Set internal state invalid",
));
} }
Ok(inner_ptr) Ok(inner_ptr)
} }
+17 -56
View File
@@ -14,12 +14,9 @@ fn this_string(vm: &Vm, args: &[u8]) -> String {
fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue { fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
let proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject; let proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
let n = parts.len(); let n = parts.len();
let arr = vm.epoch().alloc(JsObject::new_array( let arr =
EMPTY_SHAPE_ID, vm.epoch()
JsValue::from_js_object(proto), .alloc(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n, vm.epoch().bump()));
n,
vm.epoch().bump(),
));
unsafe { unsafe {
for (i, s) in parts.iter().enumerate() { for (i, s) in parts.iter().enumerate() {
let sv = vm.intern(s); let sv = vm.intern(s);
@@ -62,8 +59,7 @@ pub fn string_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
} }
let search = as_string(vm, vm.reg(args[1])); let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 { let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize) (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize).min(s.len())
.min(s.len())
} else { } else {
0 0
}; };
@@ -90,8 +86,7 @@ pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult {
} }
let search = as_string(vm, vm.reg(args[1])); let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 { let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize) (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize).min(s.len())
.min(s.len())
} else { } else {
0 0
}; };
@@ -102,9 +97,7 @@ pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult {
let haystack = &s.as_bytes()[pos..]; let haystack = &s.as_bytes()[pos..];
if search.len() == 1 { if search.len() == 1 {
Ok(JsValue::bool( Ok(JsValue::bool(memchr(search.as_bytes()[0], haystack).is_some()))
memchr(search.as_bytes()[0], haystack).is_some(),
))
} else { } else {
Ok(JsValue::bool(s[pos..].contains(&search))) Ok(JsValue::bool(s[pos..].contains(&search)))
} }
@@ -221,8 +214,7 @@ pub fn string_trim(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn string_repeat(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn string_repeat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args); let s = this_string(vm, args);
let n = if args.len() > 1 { let n = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize) (coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize).min(10000)
.min(10000)
} else { } else {
1 1
}; };
@@ -236,11 +228,7 @@ pub fn string_pad_start(vm: &mut Vm, args: &[u8]) -> NativeResult {
} else { } else {
s.len() s.len()
}; };
let pad = if args.len() > 2 { let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() };
as_string(vm, vm.reg(args[2]))
} else {
" ".to_string()
};
if s.len() >= target || pad.is_empty() { if s.len() >= target || pad.is_empty() {
return Ok(vm.intern(&s)); return Ok(vm.intern(&s));
} }
@@ -259,11 +247,7 @@ pub fn string_pad_end(vm: &mut Vm, args: &[u8]) -> NativeResult {
} else { } else {
s.len() s.len()
}; };
let pad = if args.len() > 2 { let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() };
as_string(vm, vm.reg(args[2]))
} else {
" ".to_string()
};
if s.len() >= target || pad.is_empty() { if s.len() >= target || pad.is_empty() {
return Ok(vm.intern(&s)); return Ok(vm.intern(&s));
} }
@@ -282,8 +266,7 @@ pub fn string_starts_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
} }
let search = as_string(vm, vm.reg(args[1])); let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 { let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize) (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize).min(s.len())
.min(s.len())
} else { } else {
0 0
}; };
@@ -297,8 +280,7 @@ pub fn string_ends_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
} }
let search = as_string(vm, vm.reg(args[1])); let search = as_string(vm, vm.reg(args[1]));
let end_pos = if args.len() > 2 { let end_pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize) (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize).min(s.len())
.min(s.len())
} else { } else {
s.len() s.len()
}; };
@@ -349,11 +331,7 @@ pub fn string_replace(vm: &mut Vm, args: &[u8]) -> NativeResult {
return Ok(vm.intern(&s)); return Ok(vm.intern(&s));
} }
let pattern_val = vm.reg(args[1]); let pattern_val = vm.reg(args[1]);
let replacement = if args.len() > 2 { let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() };
as_string(vm, vm.reg(args[2]))
} else {
String::new()
};
if is_regexp_obj(pattern_val, vm) { if is_regexp_obj(pattern_val, vm) {
let re_ptr = pattern_val.as_js_object_ptr(); let re_ptr = pattern_val.as_js_object_ptr();
@@ -387,19 +365,13 @@ pub fn string_match_fn(vm: &mut Vm, args: &[u8]) -> NativeResult {
None => return Ok(JsValue::undefined()), None => return Ok(JsValue::undefined()),
}; };
let regex = unsafe { &*(fn_ptr as *const regex::Regex) }; let regex = unsafe { &*(fn_ptr as *const regex::Regex) };
let matches: Vec<String> = regex let matches: Vec<String> = regex.find_iter(&s).map(|m| m.as_str().to_string()).collect();
.find_iter(&s)
.map(|m| m.as_str().to_string())
.collect();
return Ok(make_string_array(vm, &matches)); return Ok(make_string_array(vm, &matches));
} }
let pattern = as_string(vm, pattern_val); let pattern = as_string(vm, pattern_val);
if let Some(pos) = s.find(&pattern) { if let Some(pos) = s.find(&pattern) {
return Ok(make_string_array( return Ok(make_string_array(vm, &[s[pos..pos + pattern.len()].to_string()]));
vm,
&[s[pos..pos + pattern.len()].to_string()],
));
} }
Ok(JsValue::undefined()) Ok(JsValue::undefined())
} }
@@ -467,11 +439,7 @@ pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn string_normalize(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn string_normalize(vm: &mut Vm, args: &[u8]) -> NativeResult {
use unicode_normalization::UnicodeNormalization; use unicode_normalization::UnicodeNormalization;
let s = this_string(vm, args); let s = this_string(vm, args);
let form = if args.len() > 1 { let form = if args.len() > 1 { as_string(vm, vm.reg(args[1])) } else { "NFC".to_string() };
as_string(vm, vm.reg(args[1]))
} else {
"NFC".to_string()
};
let result: String = match form.as_str() { let result: String = match form.as_str() {
"NFD" => s.nfd().collect(), "NFD" => s.nfd().collect(),
"NFKC" => s.nfkc().collect(), "NFKC" => s.nfkc().collect(),
@@ -495,10 +463,7 @@ pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
None => return Ok(JsValue::undefined()), None => return Ok(JsValue::undefined()),
}; };
let regex = unsafe { &*(fn_ptr as *const regex::Regex) }; let regex = unsafe { &*(fn_ptr as *const regex::Regex) };
let matches: Vec<String> = regex let matches: Vec<String> = regex.find_iter(&s).map(|m| m.as_str().to_string()).collect();
.find_iter(&s)
.map(|m| m.as_str().to_string())
.collect();
return Ok(make_string_array(vm, &matches)); return Ok(make_string_array(vm, &matches));
} }
Ok(JsValue::undefined()) Ok(JsValue::undefined())
@@ -510,11 +475,7 @@ pub fn string_replace_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
return Ok(vm.intern(&s)); return Ok(vm.intern(&s));
} }
let pattern_val = vm.reg(args[1]); let pattern_val = vm.reg(args[1]);
let replacement = if args.len() > 2 { let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() };
as_string(vm, vm.reg(args[2]))
} else {
String::new()
};
if is_regexp_obj(pattern_val, vm) { if is_regexp_obj(pattern_val, vm) {
let re_ptr = pattern_val.as_js_object_ptr(); let re_ptr = pattern_val.as_js_object_ptr();
let re = unsafe { &*re_ptr }; let re = unsafe { &*re_ptr };
+2 -9
View File
@@ -18,10 +18,7 @@ pub fn symbol_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let sp = vm.kernel().builtin_world().symbol_proto.as_ptr() as *mut JsObject; let sp = vm.kernel().builtin_world().symbol_proto.as_ptr() as *mut JsObject;
if std::ptr::eq(proto_ptr, sp) { if std::ptr::eq(proto_ptr, sp) {
return Err(JsValue::string( return Err(JsValue::string(
vm.kernel() vm.kernel().string_forge().intern("TypeError: Symbol is not a constructor").0,
.string_forge()
.intern("TypeError: Symbol is not a constructor")
.0,
0, 0,
)); ));
} }
@@ -55,11 +52,7 @@ pub fn symbol_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
} }
let idx = this_val.as_symbol_index(); let idx = this_val.as_symbol_index();
let desc = vm let desc = vm.symbol_descriptions.get(idx as usize).cloned().unwrap_or_default();
.symbol_descriptions
.get(idx as usize)
.cloned()
.unwrap_or_default();
let result = format!("Symbol({})", desc); let result = format!("Symbol({})", desc);
let si = vm.kernel().string_forge().intern(&result).0; let si = vm.kernel().string_forge().intern(&result).0;
Ok(JsValue::string(si, 0)) Ok(JsValue::string(si, 0))
+6 -18
View File
@@ -25,9 +25,7 @@ pub fn to_number(val: JsValue, string_forge: &StringForge) -> f64 {
return f64::NAN; return f64::NAN;
} }
if val.is_string() { if val.is_string() {
let s = string_forge let s = string_forge.lookup(val.as_string_index()).unwrap_or_default();
.lookup(val.as_string_index())
.unwrap_or_default();
return s.parse::<f64>().unwrap_or(f64::NAN); return s.parse::<f64>().unwrap_or(f64::NAN);
} }
if val.is_object() { if val.is_object() {
@@ -68,9 +66,7 @@ pub fn to_string(string_forge: &StringForge, val: JsValue) -> String {
return "undefined".to_string(); return "undefined".to_string();
} }
if val.is_string() { if val.is_string() {
return string_forge return string_forge.lookup(val.as_string_index()).unwrap_or_default();
.lookup(val.as_string_index())
.unwrap_or_default();
} }
if val.is_object() { if val.is_object() {
return "[object]".to_string(); return "[object]".to_string();
@@ -93,9 +89,7 @@ pub fn to_boolean(val: JsValue, string_forge: &StringForge) -> bool {
return !(d == 0.0 || d == -0.0 || d.is_nan()); return !(d == 0.0 || d == -0.0 || d.is_nan());
} }
if val.is_string() { if val.is_string() {
let s = string_forge let s = string_forge.lookup(val.as_string_index()).unwrap_or_default();
.lookup(val.as_string_index())
.unwrap_or_default();
return !s.is_empty(); return !s.is_empty();
} }
if val.is_object() { if val.is_object() {
@@ -190,12 +184,8 @@ fn strict_double_eq(a: f64, b: f64) -> bool {
pub fn relational_compare(string_forge: &StringForge, lhs: JsValue, rhs: JsValue) -> Option<bool> { pub fn relational_compare(string_forge: &StringForge, lhs: JsValue, rhs: JsValue) -> Option<bool> {
if lhs.is_string() && rhs.is_string() { if lhs.is_string() && rhs.is_string() {
let ls = string_forge let ls = string_forge.lookup(lhs.as_string_index()).unwrap_or_default();
.lookup(lhs.as_string_index()) let rs = string_forge.lookup(rhs.as_string_index()).unwrap_or_default();
.unwrap_or_default();
let rs = string_forge
.lookup(rhs.as_string_index())
.unwrap_or_default();
return Some(ls < rs); return Some(ls < rs);
} }
let l = to_number(lhs, string_forge); let l = to_number(lhs, string_forge);
@@ -288,9 +278,7 @@ pub fn to_object(val: JsValue, vm: &mut Vm) -> Result<JsValue, &'static str> {
} }
let proto_ptr = &*vm.object_prototype as *const JsObject as *mut JsObject; let proto_ptr = &*vm.object_prototype as *const JsObject as *mut JsObject;
let proto_val = JsValue::from_js_object(proto_ptr); let proto_val = JsValue::from_js_object(proto_ptr);
let obj = vm let obj = vm.epoch.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
.epoch
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
let obj_val = JsValue::from_js_object(obj); let obj_val = JsValue::from_js_object(obj);
let obj_ref = unsafe { &mut *obj }; let obj_ref = unsafe { &mut *obj };
obj_ref.ensure_hash_props().push(Box::new(val)); obj_ref.ensure_hash_props().push(Box::new(val));
+2 -11
View File
@@ -5,11 +5,7 @@ use oxide_types::value::JsValue;
impl Vm { impl Vm {
#[inline(always)] #[inline(always)]
pub(crate) fn build_native_args( pub(crate) fn build_native_args(first_arg_reg: u8, arg_count: usize, this_reg: u8) -> ([u8; 257], usize) {
first_arg_reg: u8,
arg_count: usize,
this_reg: u8,
) -> ([u8; 257], usize) {
let mut args_buf = [0u8; 257]; let mut args_buf = [0u8; 257];
args_buf[0] = this_reg; args_buf[0] = this_reg;
let n = arg_count.min(256); let n = arg_count.min(256);
@@ -21,12 +17,7 @@ impl Vm {
#[inline(always)] #[inline(always)]
pub(crate) fn dispatch_native_call( pub(crate) fn dispatch_native_call(
&mut self, &mut self, obj: &JsObject, callee: JsValue, this_reg: u8, first_arg_reg: u8, arg_count: usize,
obj: &JsObject,
callee: JsValue,
this_reg: u8,
first_arg_reg: u8,
arg_count: usize,
) -> Result<(), String> { ) -> Result<(), String> {
let (args_buf, len) = Self::build_native_args(first_arg_reg, arg_count, this_reg); let (args_buf, len) = Self::build_native_args(first_arg_reg, arg_count, this_reg);
let args_slice = &args_buf[..len]; let args_slice = &args_buf[..len];
+6 -30
View File
@@ -5,61 +5,37 @@ use oxide_types::value::JsValue;
impl Vm { impl Vm {
#[inline(always)] #[inline(always)]
pub(crate) fn dispatch_eq(&mut self, rd: usize, a: usize, b: usize) { pub(crate) fn dispatch_eq(&mut self, rd: usize, a: usize, b: usize) {
let eq = coercion::abstract_eq( let eq = coercion::abstract_eq(self.regs[a], self.regs[b], self.kernel.string_forge().as_ref());
self.regs[a],
self.regs[b],
self.kernel.string_forge().as_ref(),
);
self.regs[rd] = JsValue::bool(eq); self.regs[rd] = JsValue::bool(eq);
} }
#[inline(always)] #[inline(always)]
pub(crate) fn dispatch_neq(&mut self, rd: usize, a: usize, b: usize) { pub(crate) fn dispatch_neq(&mut self, rd: usize, a: usize, b: usize) {
let ne = !coercion::abstract_eq( let ne = !coercion::abstract_eq(self.regs[a], self.regs[b], self.kernel.string_forge().as_ref());
self.regs[a],
self.regs[b],
self.kernel.string_forge().as_ref(),
);
self.regs[rd] = JsValue::bool(ne); self.regs[rd] = JsValue::bool(ne);
} }
#[inline(always)] #[inline(always)]
pub(crate) fn dispatch_lt(&mut self, rd: usize, a: usize, b: usize) { pub(crate) fn dispatch_lt(&mut self, rd: usize, a: usize, b: usize) {
let rel = coercion::relational_compare( let rel = coercion::relational_compare(self.kernel.string_forge().as_ref(), self.regs[a], self.regs[b]);
self.kernel.string_forge().as_ref(),
self.regs[a],
self.regs[b],
);
self.regs[rd] = JsValue::bool(rel.unwrap_or(false)); self.regs[rd] = JsValue::bool(rel.unwrap_or(false));
} }
#[inline(always)] #[inline(always)]
pub(crate) fn dispatch_gt(&mut self, rd: usize, a: usize, b: usize) { pub(crate) fn dispatch_gt(&mut self, rd: usize, a: usize, b: usize) {
let rel = coercion::relational_compare( let rel = coercion::relational_compare(self.kernel.string_forge().as_ref(), self.regs[b], self.regs[a]);
self.kernel.string_forge().as_ref(),
self.regs[b],
self.regs[a],
);
self.regs[rd] = JsValue::bool(rel.unwrap_or(false)); self.regs[rd] = JsValue::bool(rel.unwrap_or(false));
} }
#[inline(always)] #[inline(always)]
pub(crate) fn dispatch_lte(&mut self, rd: usize, a: usize, b: usize) { pub(crate) fn dispatch_lte(&mut self, rd: usize, a: usize, b: usize) {
let rel = coercion::relational_compare( let rel = coercion::relational_compare(self.kernel.string_forge().as_ref(), self.regs[b], self.regs[a]);
self.kernel.string_forge().as_ref(),
self.regs[b],
self.regs[a],
);
self.regs[rd] = JsValue::bool(!rel.unwrap_or(true)); self.regs[rd] = JsValue::bool(!rel.unwrap_or(true));
} }
#[inline(always)] #[inline(always)]
pub(crate) fn dispatch_gte(&mut self, rd: usize, a: usize, b: usize) { pub(crate) fn dispatch_gte(&mut self, rd: usize, a: usize, b: usize) {
let rel = coercion::relational_compare( let rel = coercion::relational_compare(self.kernel.string_forge().as_ref(), self.regs[a], self.regs[b]);
self.kernel.string_forge().as_ref(),
self.regs[a],
self.regs[b],
);
self.regs[rd] = JsValue::bool(!rel.unwrap_or(true)); self.regs[rd] = JsValue::bool(!rel.unwrap_or(true));
} }
+24 -73
View File
@@ -6,64 +6,33 @@ use crate::coercion;
use crate::vm::Vm; use crate::vm::Vm;
impl Vm { impl Vm {
pub(crate) fn dispatch_member_op( pub(crate) fn dispatch_member_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
&mut self,
op: OpCode,
rd: usize,
a: usize,
b: usize,
) -> Result<(), String> {
match op { match op {
OpCode::MEMBER_INC => self.dispatch_member_inc(rd, a, b), OpCode::MEMBER_INC => self.dispatch_member_inc(rd, a, b),
OpCode::MEMBER_DEC => self.dispatch_member_dec(rd, a, b), OpCode::MEMBER_DEC => self.dispatch_member_dec(rd, a, b),
OpCode::DYN_MEMBER_INC => self.dispatch_dyn_member_inc(rd, a, b), OpCode::DYN_MEMBER_INC => self.dispatch_dyn_member_inc(rd, a, b),
OpCode::DYN_MEMBER_DEC => self.dispatch_dyn_member_dec(rd, a, b), OpCode::DYN_MEMBER_DEC => self.dispatch_dyn_member_dec(rd, a, b),
OpCode::COMPOUND_MEMBER_ADD => self.dispatch_compound_member_add(rd, a, b), OpCode::COMPOUND_MEMBER_ADD => self.dispatch_compound_member_add(rd, a, b),
OpCode::COMPOUND_MEMBER_SUB => self.dispatch_compound_member_numeric( OpCode::COMPOUND_MEMBER_SUB => {
rd, self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_SUB on non-object", |l, r| l - r)
a, }
b, OpCode::COMPOUND_MEMBER_MUL => {
"COMPOUND_MEMBER_SUB on non-object", self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_MUL on non-object", |l, r| l * r)
|l, r| l - r, }
), OpCode::COMPOUND_MEMBER_DIV => {
OpCode::COMPOUND_MEMBER_MUL => self.dispatch_compound_member_numeric( self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_DIV on non-object", |l, r| l / r)
rd, }
a, OpCode::COMPOUND_MEMBER_MOD => {
b, self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_MOD on non-object", |l, r| l % r)
"COMPOUND_MEMBER_MUL on non-object", }
|l, r| l * r, OpCode::COMPOUND_MEMBER_EXP => {
), self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_EXP on non-object", |l, r| l.powf(r))
OpCode::COMPOUND_MEMBER_DIV => self.dispatch_compound_member_numeric( }
rd,
a,
b,
"COMPOUND_MEMBER_DIV on non-object",
|l, r| l / r,
),
OpCode::COMPOUND_MEMBER_MOD => self.dispatch_compound_member_numeric(
rd,
a,
b,
"COMPOUND_MEMBER_MOD on non-object",
|l, r| l % r,
),
OpCode::COMPOUND_MEMBER_EXP => self.dispatch_compound_member_numeric(
rd,
a,
b,
"COMPOUND_MEMBER_EXP on non-object",
|l, r| l.powf(r),
),
_ => unreachable!("non-member opcode passed to dispatch_member_op"), _ => unreachable!("non-member opcode passed to dispatch_member_op"),
} }
} }
fn member_target( fn member_target(&mut self, rd: usize, b: usize, error_msg: &str) -> Result<(*mut JsObject, u32), String> {
&mut self,
rd: usize,
b: usize,
error_msg: &str,
) -> Result<(*mut JsObject, u32), String> {
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject; let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
if obj_ptr.is_null() { if obj_ptr.is_null() {
self.raise_type_error(error_msg)?; self.raise_type_error(error_msg)?;
@@ -74,8 +43,7 @@ impl Vm {
} }
fn dispatch_member_inc(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { fn dispatch_member_inc(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_INC on non-object") let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_INC on non-object") else {
else {
return Ok(()); return Ok(());
}; };
let obj = unsafe { &mut *obj_ptr }; let obj = unsafe { &mut *obj_ptr };
@@ -88,8 +56,7 @@ impl Vm {
} }
fn dispatch_member_dec(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { fn dispatch_member_dec(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_DEC on non-object") let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_DEC on non-object") else {
else {
return Ok(()); return Ok(());
}; };
let obj = unsafe { &mut *obj_ptr }; let obj = unsafe { &mut *obj_ptr };
@@ -109,9 +76,7 @@ impl Vm {
} }
let obj = unsafe { &mut *obj_ptr }; let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]); let prop_name_si = self.property_key_si(self.regs[a]);
let prop_val = self let prop_val = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref()); let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n + 1.0); let new_val = JsValue::float(n + 1.0);
self.set_or_create_prop_value(obj, prop_name_si, new_val); self.set_or_create_prop_value(obj, prop_name_si, new_val);
@@ -127,9 +92,7 @@ impl Vm {
} }
let obj = unsafe { &mut *obj_ptr }; let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]); let prop_name_si = self.property_key_si(self.regs[a]);
let prop_val = self let prop_val = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref()); let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
let new_val = JsValue::float(n - 1.0); let new_val = JsValue::float(n - 1.0);
self.set_or_create_prop_value(obj, prop_name_si, new_val); self.set_or_create_prop_value(obj, prop_name_si, new_val);
@@ -137,15 +100,8 @@ impl Vm {
Ok(()) Ok(())
} }
fn dispatch_compound_member_add( fn dispatch_compound_member_add(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
&mut self, let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "COMPOUND_MEMBER_ADD on non-object") else {
rd: usize,
a: usize,
b: usize,
) -> Result<(), String> {
let Ok((obj_ptr, prop_name_si)) =
self.member_target(rd, b, "COMPOUND_MEMBER_ADD on non-object")
else {
return Ok(()); return Ok(());
}; };
let obj = unsafe { &mut *obj_ptr }; let obj = unsafe { &mut *obj_ptr };
@@ -166,12 +122,7 @@ impl Vm {
} }
fn dispatch_compound_member_numeric<F>( fn dispatch_compound_member_numeric<F>(
&mut self, &mut self, rd: usize, a: usize, b: usize, error_msg: &str, op: F,
rd: usize,
a: usize,
b: usize,
error_msg: &str,
op: F,
) -> Result<(), String> ) -> Result<(), String>
where where
F: FnOnce(f64, f64) -> f64, F: FnOnce(f64, f64) -> f64,
+12 -51
View File
@@ -6,13 +6,7 @@ use oxide_types::value::JsValue;
use crate::vm::Vm; use crate::vm::Vm;
impl Vm { impl Vm {
pub(crate) fn dispatch_property_op( pub(crate) fn dispatch_property_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
&mut self,
op: OpCode,
rd: usize,
a: usize,
b: usize,
) -> Result<(), String> {
match op { match op {
OpCode::IC_GET_PROP => self.dispatch_ic_get_prop(a, b), OpCode::IC_GET_PROP => self.dispatch_ic_get_prop(a, b),
OpCode::IC_SET_PROP => self.dispatch_ic_set_prop(rd, a, b), OpCode::IC_SET_PROP => self.dispatch_ic_set_prop(rd, a, b),
@@ -70,21 +64,13 @@ impl Vm {
self.write_ic_back(obj.shape_id(), ptr); self.write_ic_back(obj.shape_id(), ptr);
self.regs[a] = unsafe { *ptr }; self.regs[a] = unsafe { *ptr };
} else { } else {
self.regs[a] = self self.regs[a] = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
} }
} else { } else {
self.regs[a] = self self.regs[a] = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
} }
} else if let Some(val) = self.resolve_property(obj, prop_name_si) { } else if let Some(val) = self.resolve_property(obj, prop_name_si) {
if let Some(pos) = self if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
if let Some(ptr) = obj.prop_ptr_at(pos) { if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr); self.write_ic_back(obj.shape_id(), ptr);
} }
@@ -117,20 +103,13 @@ impl Vm {
unsafe { unsafe {
*(cached_ptr as *mut JsValue) = self.regs[a]; *(cached_ptr as *mut JsValue) = self.regs[a];
} }
} else if let Some(pos) = self } else if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[a]); obj.set_prop_at(pos, self.regs[a]);
if let Some(ptr) = obj.prop_ptr_at(pos) { if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr); self.write_ic_back(obj.shape_id(), ptr);
} }
} else { } else {
let new_shape_id = self let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id); obj.set_shape_id(new_shape_id);
let new_pos = obj.push_prop(self.regs[a]); let new_pos = obj.push_prop(self.regs[a]);
obj.bump_generation(); obj.bump_generation();
@@ -159,9 +138,7 @@ impl Vm {
} }
let obj = unsafe { &*obj_ptr }; let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]); let prop_name_si = self.property_key_si(self.regs[b]);
self.regs[a] = self self.regs[a] = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
Ok(()) Ok(())
} }
@@ -173,17 +150,10 @@ impl Vm {
} }
let obj = unsafe { &mut *obj_ptr }; let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[b]); let prop_name_si = self.property_key_si(self.regs[b]);
if let Some(pos) = self if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[a]); obj.set_prop_at(pos, self.regs[a]);
} else { } else {
let new_shape_id = self let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id); obj.set_shape_id(new_shape_id);
obj.push_prop(self.regs[a]); obj.push_prop(self.regs[a]);
obj.bump_generation(); obj.bump_generation();
@@ -199,9 +169,7 @@ impl Vm {
} }
let obj = unsafe { &*obj_ptr }; let obj = unsafe { &*obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]); let prop_name_si = self.property_key_si(self.regs[a]);
self.regs[b] = self self.regs[b] = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
.resolve_property(obj, prop_name_si)
.unwrap_or(JsValue::undefined());
Ok(()) Ok(())
} }
@@ -213,17 +181,10 @@ impl Vm {
} }
let obj = unsafe { &mut *obj_ptr }; let obj = unsafe { &mut *obj_ptr };
let prop_name_si = self.property_key_si(self.regs[a]); let prop_name_si = self.property_key_si(self.regs[a]);
if let Some(pos) = self if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, self.regs[b]); obj.set_prop_at(pos, self.regs[b]);
} else { } else {
let new_shape_id = self let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id); obj.set_shape_id(new_shape_id);
obj.push_prop(self.regs[b]); obj.push_prop(self.regs[b]);
obj.bump_generation(); obj.bump_generation();
+60 -209
View File
@@ -275,11 +275,7 @@ impl Vm {
.resolve_property(obj, message_si) .resolve_property(obj, message_si)
.and_then(|v| self.lookup_str(v)) .and_then(|v| self.lookup_str(v))
.unwrap_or_default(); .unwrap_or_default();
return if message.is_empty() { return if message.is_empty() { name } else { format!("{name}: {message}") };
name
} else {
format!("{name}: {message}")
};
} }
format!("{val}") format!("{val}")
} }
@@ -307,8 +303,7 @@ impl Vm {
let pat_val = JsValue::string(pat_si, 0); let pat_val = JsValue::string(pat_si, 0);
let flags_val = JsValue::string(flags_si, 0); let flags_val = JsValue::string(flags_si, 0);
let ctor_ptr = let ctor_ptr = self.kernel.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
self.kernel.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &*ctor_ptr }; let ctor = unsafe { &*ctor_ptr };
let Some(native_fn) = ctor.native_fn() else { let Some(native_fn) = ctor.native_fn() else {
return Err("SyntaxError: RegExp constructor unavailable".into()); return Err("SyntaxError: RegExp constructor unavailable".into());
@@ -378,19 +373,10 @@ impl Vm {
self.kernel.string_forge().intern(&key).0 self.kernel.string_forge().intern(&key).0
} }
pub(crate) fn template_prop_ptr( pub(crate) fn template_prop_ptr(&self, obj: &JsObject, template: &PropTemplate) -> Option<*const JsValue> {
&self,
obj: &JsObject,
template: &PropTemplate,
) -> Option<*const JsValue> {
let pos = template.position as usize; let pos = template.position as usize;
obj.hash_props_vec().and_then(|vec| { obj.hash_props_vec()
if pos < vec.len() { .and_then(|vec| if pos < vec.len() { Some(&*vec[pos] as *const JsValue) } else { None })
Some(&*vec[pos] as *const JsValue)
} else {
None
}
})
} }
pub(crate) fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> { pub(crate) fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
@@ -398,11 +384,7 @@ impl Vm {
if obj.is_array() && prop_name_si == length_si { if obj.is_array() && prop_name_si == length_si {
return Some(JsValue::int(obj.prop_count() as i32)); return Some(JsValue::int(obj.prop_count() as i32));
} }
if let Some(pos) = self if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
let val = obj.get_prop_at(pos); let val = obj.get_prop_at(pos);
if !val.is_undefined() || obj.prop_vec_len() > pos as usize { if !val.is_undefined() || obj.prop_vec_len() > pos as usize {
return Some(val); return Some(val);
@@ -411,11 +393,7 @@ impl Vm {
let mut proto = obj.proto(); let mut proto = obj.proto();
while proto.is_object() { while proto.is_object() {
let proto_obj = unsafe { &*proto.as_js_object_ptr() }; let proto_obj = unsafe { &*proto.as_js_object_ptr() };
if let Some(pos) = self if let Some(pos) = self.kernel.shape_forge().lookup_position(proto_obj.shape_id(), prop_name_si) {
.kernel
.shape_forge()
.lookup_position(proto_obj.shape_id(), prop_name_si)
{
let val = proto_obj.get_prop_at(pos); let val = proto_obj.get_prop_at(pos);
if !val.is_undefined() || proto_obj.prop_vec_len() > pos as usize { if !val.is_undefined() || proto_obj.prop_vec_len() > pos as usize {
return Some(val); return Some(val);
@@ -438,21 +416,15 @@ impl Vm {
unsafe { *(cached_ptr as *const JsValue) } unsafe { *(cached_ptr as *const JsValue) }
} else if let Some(template) = self.kernel.prop_forge().get_template(obj.shape_id()) { } else if let Some(template) = self.kernel.prop_forge().get_template(obj.shape_id()) {
if template.prop_name != prop_name_si { if template.prop_name != prop_name_si {
self.resolve_property(obj, prop_name_si) self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined())
.unwrap_or(JsValue::undefined())
} else if let Some(ptr) = self.template_prop_ptr(obj, &template) { } else if let Some(ptr) = self.template_prop_ptr(obj, &template) {
self.write_ic_back(obj.shape_id(), ptr); self.write_ic_back(obj.shape_id(), ptr);
unsafe { *ptr } unsafe { *ptr }
} else { } else {
self.resolve_property(obj, prop_name_si) self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined())
.unwrap_or(JsValue::undefined())
} }
} else if let Some(val) = self.resolve_property(obj, prop_name_si) { } else if let Some(val) = self.resolve_property(obj, prop_name_si) {
if let Some(pos) = self if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
if let Some(ptr) = obj.prop_ptr_at(pos) { if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr); self.write_ic_back(obj.shape_id(), ptr);
} }
@@ -464,26 +436,16 @@ impl Vm {
} }
pub(crate) fn set_member_prop( pub(crate) fn set_member_prop(
&mut self, &mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue,
obj: &mut JsObject,
prop_name_si: u32,
val: JsValue,
) -> Result<(), String> { ) -> Result<(), String> {
if let Some(pos) = self if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, val); obj.set_prop_at(pos, val);
// IC write-back: 3 extension words (shape_id + ptr_lo + ptr_hi) // IC write-back: 3 extension words (shape_id + ptr_lo + ptr_hi)
if let Some(ptr) = obj.prop_ptr_at(pos) { if let Some(ptr) = obj.prop_ptr_at(pos) {
self.write_ic_back(obj.shape_id(), ptr); self.write_ic_back(obj.shape_id(), ptr);
} }
} else { } else {
let new_shape_id = self let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id); obj.set_shape_id(new_shape_id);
let new_pos = obj.push_prop(val); let new_pos = obj.push_prop(val);
obj.bump_generation(); obj.bump_generation();
@@ -503,23 +465,11 @@ impl Vm {
Ok(()) Ok(())
} }
pub(crate) fn set_or_create_prop_value( pub(crate) fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
&mut self, if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
obj: &mut JsObject,
prop_name_si: u32,
val: JsValue,
) {
if let Some(pos) = self
.kernel
.shape_forge()
.lookup_position(obj.shape_id(), prop_name_si)
{
obj.set_prop_at(pos, val); obj.set_prop_at(pos, val);
} else { } else {
let new_shape_id = self let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
.kernel
.shape_forge()
.make_shape(obj.shape_id(), prop_name_si);
obj.set_shape_id(new_shape_id); obj.set_shape_id(new_shape_id);
obj.push_prop(val); obj.push_prop(val);
obj.bump_generation(); obj.bump_generation();
@@ -527,10 +477,7 @@ impl Vm {
} }
pub(crate) fn write_ic_back(&mut self, shape_id: u32, ptr: *const JsValue) { pub(crate) fn write_ic_back(&mut self, shape_id: u32, ptr: *const JsValue) {
debug_assert!( debug_assert!(self.pc >= 3, "IC write-back requires 3 extension words before pc");
self.pc >= 3,
"IC write-back requires 3 extension words before pc"
);
self.bytecode[self.pc - 3] = shape_id & 0x00FF_FFFF; self.bytecode[self.pc - 3] = shape_id & 0x00FF_FFFF;
self.bytecode[self.pc - 2] = ptr as u32; self.bytecode[self.pc - 2] = ptr as u32;
self.bytecode[self.pc - 1] = (ptr as u64 >> 32) as u32; self.bytecode[self.pc - 1] = (ptr as u64 >> 32) as u32;
@@ -572,11 +519,7 @@ impl Vm {
for (name, reg) in &module.builtin_reg_map { for (name, reg) in &module.builtin_reg_map {
let si = self.kernel.string_forge().intern(name.as_str()).0; let si = self.kernel.string_forge().intern(name.as_str()).0;
let global = self.kernel.global_object(); let global = self.kernel.global_object();
if let Some(pos) = self if let Some(pos) = self.kernel.shape_forge().lookup_position(global.shape_id(), si) {
.kernel
.shape_forge()
.lookup_position(global.shape_id(), si)
{
self.regs[*reg as usize] = global.get_prop_at(pos); self.regs[*reg as usize] = global.get_prop_at(pos);
} }
} }
@@ -587,11 +530,7 @@ impl Vm {
/// Call a bytecode function from native code (D-09). /// Call a bytecode function from native code (D-09).
/// Stub: sub_module storage not yet wired (plan 12.1-03). /// Stub: sub_module storage not yet wired (plan 12.1-03).
#[allow(dead_code)] #[allow(dead_code)]
pub fn call_bytecode_func( pub fn call_bytecode_func(&mut self, _callback_obj: &JsObject, _args_regs: &[u8]) -> Result<JsValue, String> {
&mut self,
_callback_obj: &JsObject,
_args_regs: &[u8],
) -> Result<JsValue, String> {
Err("bytecode function calls not yet supported".into()) Err("bytecode function calls not yet supported".into())
} }
@@ -715,8 +654,7 @@ impl Vm {
} }
OpCode::JMP_IF_FALSE => { OpCode::JMP_IF_FALSE => {
let cond = let cond = coercion::to_boolean(self.regs[rd], self.kernel.string_forge().as_ref());
coercion::to_boolean(self.regs[rd], self.kernel.string_forge().as_ref());
if !cond { if !cond {
let offset = opcode::offset16(instr) as isize; let offset = opcode::offset16(instr) as isize;
self.pc = ((self.pc as isize) + offset - 1) as usize; self.pc = ((self.pc as isize) + offset - 1) as usize;
@@ -724,8 +662,7 @@ impl Vm {
} }
OpCode::JMP_IF_TRUE => { OpCode::JMP_IF_TRUE => {
let cond = let cond = coercion::to_boolean(self.regs[rd], self.kernel.string_forge().as_ref());
coercion::to_boolean(self.regs[rd], self.kernel.string_forge().as_ref());
if cond { if cond {
let offset = opcode::offset16(instr) as isize; let offset = opcode::offset16(instr) as isize;
self.pc = ((self.pc as isize) + offset - 1) as usize; self.pc = ((self.pc as isize) + offset - 1) as usize;
@@ -763,13 +700,7 @@ impl Vm {
let arg_count = (ext & 0xFF) as usize; let arg_count = (ext & 0xFF) as usize;
if obj.native_fn().is_some() { if obj.native_fn().is_some() {
match self.dispatch_native_call( match self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count) {
obj,
callee,
this_reg,
first_arg_reg,
arg_count,
) {
Ok(()) => continue, Ok(()) => continue,
Err(e) => return Err(e), Err(e) => return Err(e),
} }
@@ -785,9 +716,7 @@ impl Vm {
} }
if self.frames.len() >= self.kernel.config.max_call_depth { if self.frames.len() >= self.kernel.config.max_call_depth {
return Err( return Err("RangeError: Maximum call stack size exceeded".into());
"RangeError: Maximum call stack size exceeded".into()
);
} }
// Clone sub_module data before mutably borrowing self // Clone sub_module data before mutably borrowing self
@@ -795,13 +724,10 @@ impl Vm {
let sub_n_args = self.sub_modules[sub_idx].n_args as usize; let sub_n_args = self.sub_modules[sub_idx].n_args as usize;
let sub_n_registers = self.sub_modules[sub_idx].n_registers; let sub_n_registers = self.sub_modules[sub_idx].n_registers;
let sub_constants = self.sub_modules[sub_idx].constants.clone(); let sub_constants = self.sub_modules[sub_idx].constants.clone();
let sub_param_base = let sub_param_base = self.sub_modules[sub_idx].param_base as usize;
self.sub_modules[sub_idx].param_base as usize;
let sub_is_arrow = self.sub_modules[sub_idx].is_arrow; let sub_is_arrow = self.sub_modules[sub_idx].is_arrow;
let caller_reg_limit = self.active_reg_limit.max(1); let caller_reg_limit = self.active_reg_limit.max(1);
let saved_regs = self.regs[..caller_reg_limit as usize] let saved_regs = self.regs[..caller_reg_limit as usize].to_vec().into_boxed_slice();
.to_vec()
.into_boxed_slice();
let saved_this = self.regs[254]; let saved_this = self.regs[254];
let saved_new_target = self.regs[255]; let saved_new_target = self.regs[255];
@@ -812,21 +738,14 @@ impl Vm {
} }
// Set this (reg 254 convention). // Set this (reg 254 convention).
// Arrow functions use captured lexical `this` (D-01). // Arrow functions use captured lexical `this` (D-01).
self.regs[254] = if sub_is_arrow { self.regs[254] = if sub_is_arrow { obj.captured_this() } else { callee };
obj.captured_this()
} else {
callee
};
self.regs[255] = JsValue::undefined(); self.regs[255] = JsValue::undefined();
let converted_sub_constants = let converted_sub_constants = self.convert_constants(&sub_constants)?;
self.convert_constants(&sub_constants)?;
// Save current bytecode/constants // Save current bytecode/constants
self.saved_bytecode_stack self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode));
.push(std::mem::take(&mut self.bytecode)); self.saved_constants_stack.push(std::mem::take(&mut self.constants));
self.saved_constants_stack
.push(std::mem::take(&mut self.constants));
// Push call frame // Push call frame
self.frames.push(CallFrame { self.frames.push(CallFrame {
@@ -850,10 +769,8 @@ impl Vm {
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map { for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map {
let si = self.kernel.string_forge().intern(name.as_str()).0; let si = self.kernel.string_forge().intern(name.as_str()).0;
let global = self.kernel.global_object(); let global = self.kernel.global_object();
if let Some(pos) = self if let Some(pos) =
.kernel self.kernel.shape_forge().lookup_position(global.shape_id(), si)
.shape_forge()
.lookup_position(global.shape_id(), si)
{ {
self.regs[*reg as usize] = global.get_prop_at(pos); self.regs[*reg as usize] = global.get_prop_at(pos);
} }
@@ -886,11 +803,7 @@ impl Vm {
} }
let obj = unsafe { &*obj_ptr }; let obj = unsafe { &*obj_ptr };
if !obj.is_function() || obj.native_fn().is_none() { if !obj.is_function() || obj.native_fn().is_none() {
throw_err!( throw_err!(self, TypeError, "CALL_NATIVE target is not a native function");
self,
TypeError,
"CALL_NATIVE target is not a native function"
);
} }
let ext = self.bytecode[self.pc]; let ext = self.bytecode[self.pc];
@@ -906,11 +819,7 @@ impl Vm {
let constructor = self.regs[constructor_reg]; let constructor = self.regs[constructor_reg];
if !constructor.is_object() { if !constructor.is_object() {
throw_err!( throw_err!(self, TypeError, "NEW_EXPRESSION: constructor is not an object");
self,
TypeError,
"NEW_EXPRESSION: constructor is not an object"
);
} }
let ctor_ptr = constructor.as_js_object_ptr(); let ctor_ptr = constructor.as_js_object_ptr();
if ctor_ptr.is_null() { if ctor_ptr.is_null() {
@@ -918,19 +827,11 @@ impl Vm {
} }
let ctor_obj = unsafe { &*ctor_ptr }; let ctor_obj = unsafe { &*ctor_ptr };
if !ctor_obj.is_function() { if !ctor_obj.is_function() {
throw_err!( throw_err!(self, TypeError, "NEW_EXPRESSION: constructor is not a function");
self,
TypeError,
"NEW_EXPRESSION: constructor is not a function"
);
} }
// Arrow functions cannot be used as constructors (D-03) // Arrow functions cannot be used as constructors (D-03)
if ctor_obj.is_arrow() { if ctor_obj.is_arrow() {
throw_err!( throw_err!(self, TypeError, "arrow functions cannot be used as constructors");
self,
TypeError,
"arrow functions cannot be used as constructors"
);
} }
// Read extension word for arg_count // Read extension word for arg_count
@@ -940,10 +841,9 @@ impl Vm {
// Create new empty object // Create new empty object
let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject; let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject;
let new_obj = self.epoch.alloc(JsObject::new_empty( let new_obj = self
EMPTY_SHAPE_ID, .epoch
JsValue::from_js_object(proto_ptr), .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
));
// Look up constructor.prototype and set as proto of new object // Look up constructor.prototype and set as proto of new object
let proto_si = self.kernel.string_forge().intern("prototype").0; let proto_si = self.kernel.string_forge().intern("prototype").0;
@@ -967,8 +867,7 @@ impl Vm {
} }
let args_slice = &args_buf[..arg_count + 1]; let args_slice = &args_buf[..arg_count + 1];
let func: NativeFn = let func: NativeFn = unsafe { std::mem::transmute(ctor_obj.native_fn().unwrap()) };
unsafe { std::mem::transmute(ctor_obj.native_fn().unwrap()) };
self.regs[254] = constructor; self.regs[254] = constructor;
match func(self, args_slice) { match func(self, args_slice) {
Ok(val) => { Ok(val) => {
@@ -1036,16 +935,14 @@ impl Vm {
OpCode::NEW_OBJECT => { OpCode::NEW_OBJECT => {
let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject; let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject;
let obj = self.epoch.alloc(JsObject::new_empty( let obj = self
EMPTY_SHAPE_ID, .epoch
JsValue::from_js_object(proto_ptr), .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
));
self.regs[rd] = JsValue::object(obj as *mut u8); self.regs[rd] = JsValue::object(obj as *mut u8);
} }
OpCode::NEW_ARRAY => { OpCode::NEW_ARRAY => {
let proto_ptr = let proto_ptr = self.kernel.builtin_world().array_proto.as_ptr() as *mut JsObject;
self.kernel.builtin_world().array_proto.as_ptr() as *mut JsObject;
let n = opcode::imm16(instr) as usize; let n = opcode::imm16(instr) as usize;
let bump = self.epoch.bump(); let bump = self.epoch.bump();
let obj = self.epoch.alloc(JsObject::new_array( let obj = self.epoch.alloc(JsObject::new_array(
@@ -1119,10 +1016,7 @@ impl Vm {
let reg = (seg & 0x7F) as u8; let reg = (seg & 0x7F) as u8;
let val = self.regs[reg as usize]; let val = self.regs[reg as usize];
let s = if val.is_string() { let s = if val.is_string() {
self.kernel() self.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} else { } else {
format!("{}", val) format!("{}", val)
}; };
@@ -1133,11 +1027,8 @@ impl Vm {
if const_idx < self.constants.len() { if const_idx < self.constants.len() {
let val = self.constants[const_idx]; let val = self.constants[const_idx];
if val.is_string() { if val.is_string() {
let s = self let s =
.kernel() self.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default();
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
result.push_str(&s); result.push_str(&s);
} }
} }
@@ -1164,11 +1055,7 @@ impl Vm {
let rhs_val = self.regs[b]; let rhs_val = self.regs[b];
if !rhs_val.is_object() { if !rhs_val.is_object() {
throw_err!( throw_err!(self, TypeError, "INSTANCEOF right-hand side is not callable");
self,
TypeError,
"INSTANCEOF right-hand side is not callable"
);
} }
if !lhs_val.is_object() { if !lhs_val.is_object() {
self.regs[rd] = JsValue::bool(false); self.regs[rd] = JsValue::bool(false);
@@ -1286,14 +1173,8 @@ impl Vm {
break; break;
} }
if let Some(shape) = self.kernel.shape_forge().get_shape(id) { if let Some(shape) = self.kernel.shape_forge().get_shape(id) {
if shape.property_name != u32::MAX if shape.property_name != u32::MAX && seen.insert(shape.property_name) {
&& seen.insert(shape.property_name) let hash = self.kernel.string_forge().get_hash(shape.property_name).unwrap_or(0);
{
let hash = self
.kernel
.string_forge()
.get_hash(shape.property_name)
.unwrap_or(0);
keys_vec.push(JsValue::string(shape.property_name, hash)); keys_vec.push(JsValue::string(shape.property_name, hash));
} }
cursor = shape.parent; cursor = shape.parent;
@@ -1304,10 +1185,7 @@ impl Vm {
current = cur.proto(); current = cur.proto();
} }
let iter = self.epoch.alloc(ForInIter { let iter = self.epoch.alloc(ForInIter { keys: keys_vec, index: 0 });
keys: keys_vec,
index: 0,
});
self.for_in_iters.push(iter as *mut u8); self.for_in_iters.push(iter as *mut u8);
} }
@@ -1480,35 +1358,18 @@ mod tests {
#[test] #[test]
fn for_of_opcodes_fail_explicitly() { fn for_of_opcodes_fail_explicitly() {
for (op, expected) in [ for (op, expected) in [
( (opcode::OpCode::FOR_OF_INIT, "opcode FOR_OF_INIT not yet implemented"),
opcode::OpCode::FOR_OF_INIT, (opcode::OpCode::FOR_OF_NEXT, "opcode FOR_OF_NEXT not yet implemented"),
"opcode FOR_OF_INIT not yet implemented", (opcode::OpCode::FOR_OF_DONE, "opcode FOR_OF_DONE not yet implemented"),
), (opcode::OpCode::FOR_OF_CLOSE, "opcode FOR_OF_CLOSE not yet implemented"),
(
opcode::OpCode::FOR_OF_NEXT,
"opcode FOR_OF_NEXT not yet implemented",
),
(
opcode::OpCode::FOR_OF_DONE,
"opcode FOR_OF_DONE not yet implemented",
),
(
opcode::OpCode::FOR_OF_CLOSE,
"opcode FOR_OF_CLOSE not yet implemented",
),
] { ] {
let module = CompiledModule { let module = CompiledModule {
bytecode: vec![ bytecode: vec![opcode::encode(op, 0, 0, 0), opcode::encode(opcode::OpCode::HALT, 0, 0, 0)],
opcode::encode(op, 0, 0, 0),
opcode::encode(opcode::OpCode::HALT, 0, 0, 0),
],
n_registers: 1, n_registers: 1,
..CompiledModule::new() ..CompiledModule::new()
}; };
let mut vm = Vm::new(); let mut vm = Vm::new();
let err = vm let err = vm.run(&module).expect_err("FOR_OF opcode should fail explicitly");
.run(&module)
.expect_err("FOR_OF opcode should fail explicitly");
assert_eq!(err, expected); assert_eq!(err, expected);
} }
} }
@@ -1522,13 +1383,8 @@ mod tests {
..CompiledModule::new() ..CompiledModule::new()
}; };
let mut vm = Vm::new(); let mut vm = Vm::new();
let err = vm let err = vm.run(&module).expect_err("invalid RegExp constant should fail explicitly");
.run(&module) assert!(err.contains("SyntaxError: Invalid regular expression"), "unexpected error: {err}");
.expect_err("invalid RegExp constant should fail explicitly");
assert!(
err.contains("SyntaxError: Invalid regular expression"),
"unexpected error: {err}"
);
} }
#[test] #[test]
@@ -1556,10 +1412,7 @@ mod tests {
let err = vm let err = vm
.run(&module) .run(&module)
.expect_err("invalid submodule RegExp constant should fail explicitly"); .expect_err("invalid submodule RegExp constant should fail explicitly");
assert!( assert!(err.contains("SyntaxError: Invalid regular expression"), "unexpected error: {err}");
err.contains("SyntaxError: Invalid regular expression"),
"unexpected error: {err}"
);
} }
#[test] #[test]
@@ -1586,9 +1439,7 @@ mod tests {
..CompiledModule::new() ..CompiledModule::new()
}; };
let mut vm = Vm::new(); let mut vm = Vm::new();
let err = vm let err = vm.run(&module).expect_err("unimplemented opcode should fail explicitly");
.run(&module)
.expect_err("unimplemented opcode should fail explicitly");
assert_eq!(err, "opcode PROFILE_TYPE not yet implemented"); assert_eq!(err, "opcode PROFILE_TYPE not yet implemented");
} }
} }
+1 -5
View File
@@ -27,11 +27,7 @@ pub struct VmGuard {
} }
impl VmPool { impl VmPool {
pub fn new( pub fn new(kernel: Arc<OxideKernel>, #[allow(dead_code)] min_size: usize, max_size: Option<usize>) -> Arc<Self> {
kernel: Arc<OxideKernel>,
#[allow(dead_code)] min_size: usize,
max_size: Option<usize>,
) -> Arc<Self> {
let pool = Arc::new(Self { let pool = Arc::new(Self {
kernel, kernel,
inner: Mutex::new(VmPoolInner { inner: Mutex::new(VmPoolInner {
+1 -4
View File
@@ -37,10 +37,7 @@ fn eval_val(source: &str) -> (Vm, Result<JsValue, String>) {
fn to_str(vm: &Vm, val: JsValue) -> String { fn to_str(vm: &Vm, val: JsValue) -> String {
if val.is_string() { if val.is_string() {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} else { } else {
format!("{}", val) format!("{}", val)
} }
+1 -5
View File
@@ -24,11 +24,7 @@ fn bench_1m_property_reads() {
} }
let elapsed = start.elapsed(); let elapsed = start.elapsed();
let ns_per = elapsed.as_nanos() as f64 / N as f64; let ns_per = elapsed.as_nanos() as f64 / N as f64;
println!( println!("1M IC property reads: {:.2}ms ({:.0} ns/read)", elapsed.as_secs_f64() * 1000.0, ns_per);
"1M IC property reads: {:.2}ms ({:.0} ns/read)",
elapsed.as_secs_f64() * 1000.0,
ns_per
);
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
println!(" (debug build -- skipping timing assertion)"); println!(" (debug build -- skipping timing assertion)");
} else { } else {
@@ -5,11 +5,8 @@ use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<(Vm, JsValue), String> { fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = vm.run(&module)?; let result = vm.run(&module)?;
Ok((vm, result)) Ok((vm, result))
@@ -1,28 +1,20 @@
use oxide_compiler::compiler::Compiler; use oxide_compiler::compiler::Compiler;
use oxide_types::object::JsObject; use oxide_types::object::JsObject;
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
use oxide_vm::builtins::boolean::{ use oxide_vm::builtins::boolean::{boolean_constructor, boolean_prototype_to_string, boolean_prototype_value_of};
boolean_constructor, boolean_prototype_to_string, boolean_prototype_value_of,
};
use oxide_vm::vm::Vm; use oxide_vm::vm::Vm;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
fn str_val(vm: &Vm, val: JsValue) -> String { fn str_val(vm: &Vm, val: JsValue) -> String {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} }
// -- direct native fn tests -- // -- direct native fn tests --
+4 -14
View File
@@ -3,19 +3,13 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<oxide_types::value::JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<oxide_types::value::JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
fn str_val(vm: &Vm, val: oxide_types::value::JsValue) -> String { fn str_val(vm: &Vm, val: oxide_types::value::JsValue) -> String {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} }
// -- static methods -- // -- static methods --
@@ -92,11 +86,7 @@ fn date_set_time() {
#[test] #[test]
fn date_set_full_year() { fn date_set_full_year() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let r = eval( let r = eval(&mut vm, "var d = new Date(2020, 0, 1); d.setFullYear(2025); d.getFullYear()").unwrap();
&mut vm,
"var d = new Date(2020, 0, 1); d.setFullYear(2025); d.getFullYear()",
)
.unwrap();
assert_eq!(r.as_double(), 2025.0); assert_eq!(r.as_double(), 2025.0);
} }
+4 -21
View File
@@ -10,9 +10,7 @@ fn make_vm() -> Vm {
fn eval(source: &str) -> Result<JsValue, String> { fn eval(source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new() let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
.compile(&program)
.map_err(|e| format!("Compile: {}", e))?;
let mut vm = make_vm(); let mut vm = make_vm();
vm.run(&module) vm.run(&module)
} }
@@ -140,22 +138,7 @@ fn reference_error_is_defined() {
#[test] #[test]
fn error_subtype_constructors_produce_named_objects() { fn error_subtype_constructors_produce_named_objects() {
assert_eq!( assert_eq!(format!("{}", eval("new Error('boom').name == 'Error'").unwrap()), "true");
format!("{}", eval("new Error('boom').name == 'Error'").unwrap()), assert_eq!(format!("{}", eval("new TypeError('boom').name == 'TypeError'").unwrap()), "true");
"true" assert_eq!(format!("{}", eval("new SyntaxError('boom').name == 'SyntaxError'").unwrap()), "true");
);
assert_eq!(
format!(
"{}",
eval("new TypeError('boom').name == 'TypeError'").unwrap()
),
"true"
);
assert_eq!(
format!(
"{}",
eval("new SyntaxError('boom').name == 'SyntaxError'").unwrap()
),
"true"
);
} }
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
@@ -38,11 +35,7 @@ fn function_to_string_includes_function() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval(&mut vm, "Math.max.toString()").unwrap(); let result = eval(&mut vm, "Math.max.toString()").unwrap();
assert!(result.is_string()); assert!(result.is_string());
let rendered = vm let rendered = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default();
.kernel()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
assert!(rendered.contains("function"), "got: {}", rendered); assert!(rendered.contains("function"), "got: {}", rendered);
} }
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<oxide_types::value::JsValue, String> { fn eval(source: &str) -> Result<oxide_types::value::JsValue, String> {
let allocator = Allocator::default(); let allocator = Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new(); let mut vm = Vm::new();
vm.run(&module) vm.run(&module)
} }
+5 -17
View File
@@ -1,25 +1,17 @@
use oxide_compiler::compiler::Compiler; use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
use oxide_vm::builtins::map::{ use oxide_vm::builtins::map::{map_clear, map_constructor as new_map, map_delete, map_get, map_has, map_set, map_size};
map_clear, map_constructor as new_map, map_delete, map_get, map_has, map_set, map_size,
};
use oxide_vm::vm::Vm; use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
fn str_val(vm: &Vm, val: JsValue) -> String { fn str_val(vm: &Vm, val: JsValue) -> String {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} }
// -- direct native fn tests -- // -- direct native fn tests --
@@ -101,10 +93,6 @@ fn map_new_has() {
#[test] #[test]
fn map_new_delete() { fn map_new_delete() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let r = eval( let r = eval(&mut vm, "var m = new Map(); m.set('x', 10); m.delete('x'); m.has('x')").unwrap();
&mut vm,
"var m = new Map(); m.set('x', 10); m.delete('x'); m.has('x')",
)
.unwrap();
assert!(!r.as_bool()); assert!(!r.as_bool());
} }
+2 -5
View File
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
+7 -29
View File
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
@@ -79,11 +76,7 @@ fn number_constructor() {
fn to_string_of_number() { fn to_string_of_number() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 42; n.toString()").unwrap(); let result = eval(&mut vm, "var n = 42; n.toString()").unwrap();
let s = vm let s = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default();
.kernel()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
assert_eq!(s, "42"); assert_eq!(s, "42");
} }
@@ -91,11 +84,7 @@ fn to_string_of_number() {
fn to_fixed_of_number() { fn to_fixed_of_number() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 3.14159; n.toFixed(2)").unwrap(); let result = eval(&mut vm, "var n = 3.14159; n.toFixed(2)").unwrap();
let s = vm let s = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default();
.kernel()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
assert_eq!(s, "3.14"); assert_eq!(s, "3.14");
} }
@@ -124,24 +113,13 @@ fn number_epsilon_is_positive() {
fn number_to_exponential() { fn number_to_exponential() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 123.456; n.toExponential(2)").unwrap(); let result = eval(&mut vm, "var n = 123.456; n.toExponential(2)").unwrap();
let s = vm let s = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default();
.kernel()
.string_forge()
.lookup(result.as_string_index())
.unwrap_or_default();
assert_eq!(s, "1.23e2"); assert_eq!(s, "1.23e2");
} }
#[test] #[test]
fn number_static_constants_remain_bound() { fn number_static_constants_remain_bound() {
let mut vm = Vm::new(); let mut vm = Vm::new();
assert_eq!( assert_eq!(eval(&mut vm, "Number.MAX_SAFE_INTEGER").unwrap().as_double(), 9007199254740991f64);
eval(&mut vm, "Number.MAX_SAFE_INTEGER") assert!(eval(&mut vm, "Number.POSITIVE_INFINITY > 0").unwrap().as_bool());
.unwrap()
.as_double(),
9007199254740991f64
);
assert!(eval(&mut vm, "Number.POSITIVE_INFINITY > 0")
.unwrap()
.as_bool());
} }
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<(Vm, JsValue), String> { fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = vm.run(&module)?; let result = vm.run(&module)?;
Ok((vm, result)) Ok((vm, result))
+4 -14
View File
@@ -4,19 +4,13 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
fn to_str(vm: &Vm, val: JsValue) -> String { fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} }
// --- RegExp constructor --- // --- RegExp constructor ---
@@ -154,11 +148,7 @@ fn regexp_exec_global_last_index_advances() {
#[test] #[test]
fn regexp_exec_global_resets_on_exhaustion() { fn regexp_exec_global_resets_on_exhaustion() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(&mut vm, "var re = /a/g; re.exec('a'); re.exec('a') === null").unwrap();
&mut vm,
"var re = /a/g; re.exec('a'); re.exec('a') === null",
)
.unwrap();
assert!(result.as_bool()); assert!(result.as_bool());
} }
+4 -13
View File
@@ -1,17 +1,12 @@
use oxide_compiler::compiler::Compiler; use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
use oxide_vm::builtins::set::{ use oxide_vm::builtins::set::{set_add, set_clear, set_constructor as new_set, set_delete, set_has, set_size};
set_add, set_clear, set_constructor as new_set, set_delete, set_has, set_size,
};
use oxide_vm::vm::Vm; use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
@@ -119,10 +114,6 @@ fn set_new_has_missing() {
#[test] #[test]
fn set_new_delete() { fn set_new_delete() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let r = eval( let r = eval(&mut vm, "var s = new Set(); s.add(1); s.add(2); s.delete(1); s.has(1)").unwrap();
&mut vm,
"var s = new Set(); s.add(1); s.add(2); s.delete(1); s.has(1)",
)
.unwrap();
assert!(!r.as_bool()); assert!(!r.as_bool());
} }
@@ -4,19 +4,13 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
fn to_str(vm: &Vm, val: JsValue) -> String { fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} }
#[test] #[test]
@@ -4,19 +4,13 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
fn to_str(vm: &Vm, val: JsValue) -> String { fn to_str(vm: &Vm, val: JsValue) -> String {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} }
// --- Symbol constructor --- // --- Symbol constructor ---
+8 -32
View File
@@ -38,26 +38,17 @@ fn eval_not_falsy() {
#[test] #[test]
fn test_same_value_signed_zero() { fn test_same_value_signed_zero() {
assert!(!coercion::same_value( assert!(!coercion::same_value(JsValue::float(0.0), JsValue::float(-0.0)));
JsValue::float(0.0),
JsValue::float(-0.0)
));
} }
#[test] #[test]
fn test_strict_equality_signed_zero() { fn test_strict_equality_signed_zero() {
assert!(coercion::strict_equality( assert!(coercion::strict_equality(JsValue::float(0.0), JsValue::float(-0.0)));
JsValue::float(0.0),
JsValue::float(-0.0)
));
} }
#[test] #[test]
fn test_same_value_nan() { fn test_same_value_nan() {
assert!(coercion::same_value( assert!(coercion::same_value(JsValue::float(f64::NAN), JsValue::float(f64::NAN)));
JsValue::float(f64::NAN),
JsValue::float(f64::NAN)
));
} }
#[test] #[test]
@@ -73,36 +64,21 @@ fn test_same_value_int_float_equal() {
#[test] #[test]
fn test_strict_equality_nan() { fn test_strict_equality_nan() {
assert!(!coercion::strict_equality( assert!(!coercion::strict_equality(JsValue::float(f64::NAN), JsValue::float(f64::NAN)));
JsValue::float(f64::NAN),
JsValue::float(f64::NAN)
));
} }
#[test] #[test]
fn test_strict_equality_type_mismatch() { fn test_strict_equality_type_mismatch() {
assert!(!coercion::strict_equality( assert!(!coercion::strict_equality(JsValue::int(1), JsValue::bool(true)));
JsValue::int(1),
JsValue::bool(true)
));
} }
#[test] #[test]
fn test_strict_equality_int_float_equal() { fn test_strict_equality_int_float_equal() {
assert!(coercion::strict_equality( assert!(coercion::strict_equality(JsValue::int(1), JsValue::float(1.0)));
JsValue::int(1), assert!(!coercion::strict_equality(JsValue::int(1), JsValue::float(2.0)));
JsValue::float(1.0)
));
assert!(!coercion::strict_equality(
JsValue::int(1),
JsValue::float(2.0)
));
} }
#[test] #[test]
fn test_strict_equality_null_undefined() { fn test_strict_equality_null_undefined() {
assert!(!coercion::strict_equality( assert!(!coercion::strict_equality(JsValue::null(), JsValue::undefined()));
JsValue::null(),
JsValue::undefined()
));
} }
+11 -44
View File
@@ -61,18 +61,12 @@ fn eval_while_result_undefined() {
#[test] #[test]
fn eval_for_basic() { fn eval_for_basic() {
assert_eq!( assert_eq!(eval("var r = 0; for (i = 0; i < 3; i = i + 1) { r = r + i; } r"), "3");
eval("var r = 0; for (i = 0; i < 3; i = i + 1) { r = r + i; } r"),
"3"
);
} }
#[test] #[test]
fn eval_for_no_test() { fn eval_for_no_test() {
assert_eq!( assert_eq!(eval("var r = 0; for (i = 0; ; i = i + 1) { if (i >= 3) { break; } r = r + i; } r"), "3");
eval("var r = 0; for (i = 0; ; i = i + 1) { if (i >= 3) { break; } r = r + i; } r"),
"3"
);
} }
#[test] #[test]
@@ -97,10 +91,7 @@ fn eval_ternary_nested() {
#[test] #[test]
fn eval_break_in_while() { fn eval_break_in_while() {
assert_eq!( assert_eq!(eval("var i = 0; while (true) { i = i + 1; if (i >= 3) { break; } } i"), "3");
eval("var i = 0; while (true) { i = i + 1; if (i >= 3) { break; } } i"),
"3"
);
} }
#[test] #[test]
@@ -155,10 +146,7 @@ fn eval_do_while_multi_iteration() {
#[test] #[test]
fn eval_do_while_break() { fn eval_do_while_break() {
assert_eq!( assert_eq!(eval("var i = 0; do { i = i + 1; if (i >= 3) break; } while (true); i"), "3");
eval("var i = 0; do { i = i + 1; if (i >= 3) break; } while (true); i"),
"3"
);
} }
#[test] #[test]
@@ -182,34 +170,22 @@ fn eval_for_in_empty_object() {
#[test] #[test]
fn eval_for_in_non_object_throws() { fn eval_for_in_non_object_throws() {
let result = eval("for (k in 42) {}"); let result = eval("for (k in 42) {}");
assert!( assert!(result.contains("TypeError"), "expected TypeError, got: {result}");
result.contains("TypeError"),
"expected TypeError, got: {result}"
);
} }
#[test] #[test]
fn eval_for_in_nested() { fn eval_for_in_nested() {
assert_eq!( assert_eq!(eval("var r=0; for (k in {a:{x:1}}) { for (j in {y:2}) { r=r+1; } } r"), "2");
eval("var r=0; for (k in {a:{x:1}}) { for (j in {y:2}) { r=r+1; } } r"),
"2"
);
} }
#[test] #[test]
fn eval_for_in_break() { fn eval_for_in_break() {
assert_eq!( assert_eq!(eval("var r=0; for (k in {a:1,b:2,c:3}) { if (k=='b') break; r=r+1; } r"), "1");
eval("var r=0; for (k in {a:1,b:2,c:3}) { if (k=='b') break; r=r+1; } r"),
"1"
);
} }
#[test] #[test]
fn eval_for_in_continue() { fn eval_for_in_continue() {
assert_eq!( assert_eq!(eval("var r=0; for (k in {a:1,b:2}) { if (k=='a') continue; r=r+1; } r"), "2");
eval("var r=0; for (k in {a:1,b:2}) { if (k=='a') continue; r=r+1; } r"),
"2"
);
} }
#[test] #[test]
@@ -220,10 +196,7 @@ fn eval_for_in_let_scoping() {
#[test] #[test]
fn eval_switch_basic_match() { fn eval_switch_basic_match() {
assert_eq!( assert_eq!(eval("var x=0;switch(2){case 1:x=10;case 2:x=20;break;default:x=30;}x"), "20");
eval("var x=0;switch(2){case 1:x=10;case 2:x=20;break;default:x=30;}x"),
"20"
);
} }
#[test] #[test]
@@ -243,18 +216,12 @@ fn eval_switch_no_match_no_default() {
#[test] #[test]
fn eval_switch_break() { fn eval_switch_break() {
assert_eq!( assert_eq!(eval("var x=0;switch(1){case 1:x=10;break;case 2:x=20;}x"), "10");
eval("var x=0;switch(1){case 1:x=10;break;case 2:x=20;}x"),
"10"
);
} }
#[test] #[test]
fn eval_break_in_switch_in_loop() { fn eval_break_in_switch_in_loop() {
assert_eq!( assert_eq!(eval("var r=0;while(true){switch(1){case 1:r=1;break;}r=2;break;}r"), "2");
eval("var r=0;while(true){switch(1){case 1:r=1;break;}r=2;break;}r"),
"2"
);
} }
#[test] #[test]
+8 -35
View File
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
@@ -61,11 +58,7 @@ fn fe_basic() {
#[test] #[test]
fn fe_with_params() { fn fe_with_params() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(&mut vm, "var mul = function(x,y) { return x * y; }; mul(6, 7)").unwrap();
&mut vm,
"var mul = function(x,y) { return x * y; }; mul(6, 7)",
)
.unwrap();
assert!((result.as_double() - 42.0).abs() < 0.0001); assert!((result.as_double() - 42.0).abs() < 0.0001);
} }
@@ -74,22 +67,14 @@ fn fe_with_params() {
#[test] #[test]
fn cross_func_call() { fn cross_func_call() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(&mut vm, "function a() { return 1; } function b() { return a() + 2; } b()").unwrap();
&mut vm,
"function a() { return 1; } function b() { return a() + 2; } b()",
)
.unwrap();
assert!((result.as_double() - 3.0).abs() < 0.0001); assert!((result.as_double() - 3.0).abs() < 0.0001);
} }
#[test] #[test]
fn two_funcs_called_from_global() { fn two_funcs_called_from_global() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(&mut vm, "function a() { return 1; } function b() { return 2; } a() + b()").unwrap();
&mut vm,
"function a() { return 1; } function b() { return 2; } a() + b()",
)
.unwrap();
assert!(result.is_double(), "expected double, got: {:?}", result); assert!(result.is_double(), "expected double, got: {:?}", result);
assert!((result.as_double() - 3.0).abs() < 0.0001); assert!((result.as_double() - 3.0).abs() < 0.0001);
} }
@@ -147,11 +132,7 @@ fn fd_calls_builtin_two_args() {
#[test] #[test]
fn fd_chain_call() { fn fd_chain_call() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(&mut vm, "function a() { return 10; } function b() { return a(); } b()").unwrap();
&mut vm,
"function a() { return 10; } function b() { return a(); } b()",
)
.unwrap();
assert_eq!(result.as_int(), 10); assert_eq!(result.as_int(), 10);
} }
@@ -177,11 +158,7 @@ fn fd_returns_new_array() {
fn this_in_function_reads_value() { fn this_in_function_reads_value() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval(&mut vm, "function f() { return this; } f()").unwrap(); let result = eval(&mut vm, "function f() { return this; } f()").unwrap();
assert!( assert!(result.is_object(), "expected object (function as this), got: {:?}", result);
result.is_object(),
"expected object (function as this), got: {:?}",
result
);
} }
#[test] #[test]
@@ -201,10 +178,6 @@ fn this_member_access() {
#[test] #[test]
fn this_in_constructor_sets_proto() { fn this_in_constructor_sets_proto() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(&mut vm, "function Ctor(m) { this.message = m; } Ctor.prototype = new Object(); 1").unwrap();
&mut vm,
"function Ctor(m) { this.message = m; } Ctor.prototype = new Object(); 1",
)
.unwrap();
assert_eq!(result.as_int(), 1); assert_eq!(result.as_int(), 1);
} }
+5 -19
View File
@@ -4,19 +4,13 @@ use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> { fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let module = Compiler::new()
.compile(&program)
.map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module) vm.run(&module)
} }
fn string_value(vm: &Vm, val: JsValue) -> String { fn string_value(vm: &Vm, val: JsValue) -> String {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} }
// -- JSON.parse -- // -- JSON.parse --
@@ -90,11 +84,7 @@ fn json_parse_null() {
#[test] #[test]
fn json_parse_nested() { fn json_parse_nested() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let result = eval( let result = eval(&mut vm, "JSON.parse('{\"a\":1,\"b\":[1,2],\"c\":{\"d\":\"hello\"}}')").unwrap();
&mut vm,
"JSON.parse('{\"a\":1,\"b\":[1,2],\"c\":{\"d\":\"hello\"}}')",
)
.unwrap();
assert!(result.is_object()); assert!(result.is_object());
} }
@@ -200,11 +190,7 @@ fn json_parse_stringify_roundtrip() {
#[test] #[test]
fn json_roundtrip_array() { fn json_roundtrip_array() {
let mut vm = Vm::new(); let mut vm = Vm::new();
let _ = eval( let _ = eval(&mut vm, "var arr = JSON.parse(JSON.stringify([1,2,3])); arr").unwrap();
&mut vm,
"var arr = JSON.parse(JSON.stringify([1,2,3])); arr",
)
.unwrap();
} }
// -- Edge cases -- // -- Edge cases --
+1 -4
View File
@@ -43,10 +43,7 @@ fn epoch_benchmark_1m_allocations() {
let elapsed = start.elapsed(); let elapsed = start.elapsed();
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
println!( println!("1M allocations took {}ms (debug build - skipping timing assertion)", elapsed.as_millis());
"1M allocations took {}ms (debug build - skipping timing assertion)",
elapsed.as_millis()
);
} else { } else {
assert!( assert!(
elapsed.as_millis() < 200, elapsed.as_millis() < 200,
+26 -131
View File
@@ -5,9 +5,7 @@ use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<JsValue, String> { fn eval(source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new() let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
.compile(&program)
.map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new(); let mut vm = Vm::new();
vm.run(&module) vm.run(&module)
} }
@@ -15,36 +13,21 @@ fn eval(source: &str) -> Result<JsValue, String> {
macro_rules! assert_bool { macro_rules! assert_bool {
($expr:expr, $expected:expr, $msg:expr) => { ($expr:expr, $expected:expr, $msg:expr) => {
let r = $expr.unwrap(); let r = $expr.unwrap();
assert!( assert!(r.is_bool() && r.as_bool() == $expected, "{}: expected {}, got {:?}", $msg, $expected, r);
r.is_bool() && r.as_bool() == $expected,
"{}: expected {}, got {:?}",
$msg,
$expected,
r
);
}; };
} }
macro_rules! assert_undefined { macro_rules! assert_undefined {
($expr:expr, $msg:expr) => { ($expr:expr, $msg:expr) => {
let r = $expr.unwrap(); let r = $expr.unwrap();
assert!( assert!(r.is_undefined(), "{}: expected undefined, got {:?}", $msg, r);
r.is_undefined(),
"{}: expected undefined, got {:?}",
$msg,
r
);
}; };
} }
// -- Proto chains: every (non-Object) constructor.prototype should inherit from Object.prototype -- // -- Proto chains: every (non-Object) constructor.prototype should inherit from Object.prototype --
#[test] #[test]
fn array_proto_chain() { fn array_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(Array.prototype) === Object.prototype"), true, "Array proto");
eval("Object.getPrototypeOf(Array.prototype) === Object.prototype"),
true,
"Array proto"
);
} }
#[test] #[test]
@@ -57,20 +40,12 @@ fn function_proto_chain() {
#[test] #[test]
fn string_proto_chain() { fn string_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(String.prototype) === Object.prototype"), true, "String proto");
eval("Object.getPrototypeOf(String.prototype) === Object.prototype"),
true,
"String proto"
);
} }
#[test] #[test]
fn number_proto_chain() { fn number_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(Number.prototype) === Object.prototype"), true, "Number proto");
eval("Object.getPrototypeOf(Number.prototype) === Object.prototype"),
true,
"Number proto"
);
} }
#[test] #[test]
@@ -84,56 +59,32 @@ fn boolean_proto_chain() {
#[test] #[test]
fn error_proto_chain() { fn error_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(Error.prototype) === Object.prototype"), true, "Error proto");
eval("Object.getPrototypeOf(Error.prototype) === Object.prototype"),
true,
"Error proto"
);
} }
#[test] #[test]
fn symbol_proto_chain() { fn symbol_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(Symbol.prototype) === Object.prototype"), true, "Symbol proto");
eval("Object.getPrototypeOf(Symbol.prototype) === Object.prototype"),
true,
"Symbol proto"
);
} }
#[test] #[test]
fn date_proto_chain() { fn date_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(Date.prototype) === Object.prototype"), true, "Date proto");
eval("Object.getPrototypeOf(Date.prototype) === Object.prototype"),
true,
"Date proto"
);
} }
#[test] #[test]
fn set_proto_chain() { fn set_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(Set.prototype) === Object.prototype"), true, "Set proto");
eval("Object.getPrototypeOf(Set.prototype) === Object.prototype"),
true,
"Set proto"
);
} }
#[test] #[test]
fn map_proto_chain() { fn map_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(Map.prototype) === Object.prototype"), true, "Map proto");
eval("Object.getPrototypeOf(Map.prototype) === Object.prototype"),
true,
"Map proto"
);
} }
#[test] #[test]
fn regexp_proto_chain() { fn regexp_proto_chain() {
assert_bool!( assert_bool!(eval("Object.getPrototypeOf(RegExp.prototype) === Object.prototype"), true, "RegExp proto");
eval("Object.getPrototypeOf(RegExp.prototype) === Object.prototype"),
true,
"RegExp proto"
);
} }
// -- Object.prototype is the root, its __proto__ is null -- // -- Object.prototype is the root, its __proto__ is null --
@@ -147,20 +98,12 @@ fn object_proto_is_root() {
#[test] #[test]
fn array_instanceof_object() { fn array_instanceof_object() {
assert_bool!(eval("[] instanceof Object"), true, "[] instanceof Object"); assert_bool!(eval("[] instanceof Object"), true, "[] instanceof Object");
assert_bool!( assert_bool!(eval("new Array() instanceof Object"), true, "new Array instanceof Object");
eval("new Array() instanceof Object"),
true,
"new Array instanceof Object"
);
} }
#[test] #[test]
fn function_instanceof_object() { fn function_instanceof_object() {
assert_bool!( assert_bool!(eval("(function() {}) instanceof Object"), true, "function instanceof Object");
eval("(function() {}) instanceof Object"),
true,
"function instanceof Object"
);
} }
#[test] #[test]
@@ -179,110 +122,62 @@ fn boolean_not_instanceof_function() {
#[test] #[test]
fn number_instanceof_object() { fn number_instanceof_object() {
assert_bool!( assert_bool!(eval("new Number(1) instanceof Object"), true, "new Number instanceof Object");
eval("new Number(1) instanceof Object"),
true,
"new Number instanceof Object"
);
} }
#[test] #[test]
fn boolean_instanceof_object() { fn boolean_instanceof_object() {
assert_bool!( assert_bool!(eval("new Boolean(true) instanceof Object"), true, "new Boolean instanceof Object");
eval("new Boolean(true) instanceof Object"),
true,
"new Boolean instanceof Object"
);
} }
#[test] #[test]
fn error_instanceof_object() { fn error_instanceof_object() {
assert_bool!( assert_bool!(eval("new Error() instanceof Object"), true, "new Error instanceof Object");
eval("new Error() instanceof Object"),
true,
"new Error instanceof Object"
);
} }
#[test] #[test]
fn date_instanceof_object() { fn date_instanceof_object() {
assert_bool!( assert_bool!(eval("new Date() instanceof Object"), true, "new Date instanceof Object");
eval("new Date() instanceof Object"),
true,
"new Date instanceof Object"
);
} }
#[test] #[test]
fn set_instanceof_object() { fn set_instanceof_object() {
assert_bool!( assert_bool!(eval("new Set() instanceof Object"), true, "new Set instanceof Object");
eval("new Set() instanceof Object"),
true,
"new Set instanceof Object"
);
} }
#[test] #[test]
fn map_instanceof_object() { fn map_instanceof_object() {
assert_bool!( assert_bool!(eval("new Map() instanceof Object"), true, "new Map instanceof Object");
eval("new Map() instanceof Object"),
true,
"new Map instanceof Object"
);
} }
#[test] #[test]
fn regexp_instanceof_object() { fn regexp_instanceof_object() {
assert_bool!(eval("/a/ instanceof Object"), true, "/a/ instanceof Object"); assert_bool!(eval("/a/ instanceof Object"), true, "/a/ instanceof Object");
assert_bool!( assert_bool!(eval("new RegExp('a') instanceof Object"), true, "new RegExp instanceof Object");
eval("new RegExp('a') instanceof Object"),
true,
"new RegExp instanceof Object"
);
} }
#[test] #[test]
fn object_instanceof_object() { fn object_instanceof_object() {
assert_bool!( assert_bool!(eval("({}) instanceof Object"), true, "({}) instanceof Object");
eval("({}) instanceof Object"),
true,
"({}) instanceof Object"
);
} }
// -- instanceof for native types should NOT respond to their own false positives -- // -- instanceof for native types should NOT respond to their own false positives --
#[test] #[test]
fn number_not_instanceof_array() { fn number_not_instanceof_array() {
assert_bool!( assert_bool!(eval("new Number(1) instanceof Array"), false, "Number instanceof Array");
eval("new Number(1) instanceof Array"),
false,
"Number instanceof Array"
);
} }
#[test] #[test]
fn object_not_instanceof_regexp() { fn object_not_instanceof_regexp() {
assert_bool!( assert_bool!(eval("({}) instanceof RegExp"), false, "({}) instanceof RegExp");
eval("({}) instanceof RegExp"),
false,
"({}) instanceof RegExp"
);
} }
// -- Primitives should NOT be instanceof their wrapper constructors (no autoboxing) -- // -- Primitives should NOT be instanceof their wrapper constructors (no autoboxing) --
#[test] #[test]
fn primitives_not_instanceof() { fn primitives_not_instanceof() {
assert_bool!(eval("1 instanceof Number"), false, "1 instanceof Number"); assert_bool!(eval("1 instanceof Number"), false, "1 instanceof Number");
assert_bool!( assert_bool!(eval("'hi' instanceof String"), false, "'hi' instanceof String");
eval("'hi' instanceof String"), assert_bool!(eval("true instanceof Boolean"), false, "true instanceof Boolean");
false,
"'hi' instanceof String"
);
assert_bool!(
eval("true instanceof Boolean"),
false,
"true instanceof Boolean"
);
} }
// -- VOID regression -- // -- VOID regression --
+10 -41
View File
@@ -42,11 +42,7 @@ fn eval_with_kernel(source: &str, kernel: Arc<OxideKernel>) -> String {
#[test] #[test]
fn regression_to_boolean_empty_string() { fn regression_to_boolean_empty_string() {
assert_eq!(eval("!''"), "true", "NOT empty string should be true"); assert_eq!(eval("!''"), "true", "NOT empty string should be true");
assert_eq!( assert_eq!(eval("if ('') { 1 } else { 2 }"), "2", "empty string should be falsy");
eval("if ('') { 1 } else { 2 }"),
"2",
"empty string should be falsy"
);
} }
#[test] #[test]
@@ -71,19 +67,13 @@ fn regression_rerun_clears_ic_cache() {
#[test] #[test]
fn regression_recursion_depth_limit() { fn regression_recursion_depth_limit() {
assert_eq!( assert_eq!(eval("function f(){f()} f()"), "vm error: RangeError: Maximum call stack size exceeded");
eval("function f(){f()} f()"),
"vm error: RangeError: Maximum call stack size exceeded"
);
} }
#[test] #[test]
fn regression_throw_statement_errors() { fn regression_throw_statement_errors() {
let result = eval("throw 'error'"); let result = eval("throw 'error'");
assert!( assert!(result.contains("uncaught"), "expected uncaught, got: {result}");
result.contains("uncaught"),
"expected uncaught, got: {result}"
);
assert!( assert!(
result.contains("uncaught Error"), result.contains("uncaught Error"),
"expected string throw to default to Error, got: {result}" "expected string throw to default to Error, got: {result}"
@@ -92,14 +82,8 @@ fn regression_throw_statement_errors() {
#[test] #[test]
fn regression_throw_statement_preserves_type_error_kind() { fn regression_throw_statement_preserves_type_error_kind() {
assert_eq!( assert_eq!(eval("try { throw new TypeError('boom') } catch (e) { e.name == 'TypeError' }"), "true");
eval("try { throw new TypeError('boom') } catch (e) { e.name == 'TypeError' }"), assert_eq!(eval("throw new TypeError('boom')"), "vm error: uncaught TypeError: {object}");
"true"
);
assert_eq!(
eval("throw new TypeError('boom')"),
"vm error: uncaught TypeError: {object}"
);
} }
#[test] #[test]
@@ -108,10 +92,7 @@ fn regression_throw_statement_preserves_syntax_error_kind() {
eval("try { throw new SyntaxError('boom') } catch (e) { e.name == 'SyntaxError' }"), eval("try { throw new SyntaxError('boom') } catch (e) { e.name == 'SyntaxError' }"),
"true" "true"
); );
assert_eq!( assert_eq!(eval("throw new SyntaxError('boom')"), "vm error: uncaught SyntaxError: {object}");
eval("throw new SyntaxError('boom')"),
"vm error: uncaught SyntaxError: {object}"
);
} }
#[test] #[test]
@@ -155,24 +136,12 @@ fn regression_new_expression_bytecode_constructor_error_is_catchable() {
#[test] #[test]
fn regression_delete_static_property_throws_type_error() { fn regression_delete_static_property_throws_type_error() {
assert_eq!( assert_eq!(eval("try { var o={x:1}; delete o.x; 0 } catch (e) { e.name == 'TypeError' }"), "true");
eval("try { var o={x:1}; delete o.x; 0 } catch (e) { e.name == 'TypeError' }"), assert_eq!(eval("var o={x:1}; delete o.x"), "vm error: uncaught TypeError: {object}");
"true"
);
assert_eq!(
eval("var o={x:1}; delete o.x"),
"vm error: uncaught TypeError: {object}"
);
} }
#[test] #[test]
fn regression_delete_dynamic_property_throws_type_error() { fn regression_delete_dynamic_property_throws_type_error() {
assert_eq!( assert_eq!(eval("try { var o={x:1}; delete o['x']; 0 } catch (e) { e.name == 'TypeError' }"), "true");
eval("try { var o={x:1}; delete o['x']; 0 } catch (e) { e.name == 'TypeError' }"), assert_eq!(eval("var o={x:1}; var k='x'; delete o[k]"), "vm error: uncaught TypeError: {object}");
"true"
);
assert_eq!(
eval("var o={x:1}; var k='x'; delete o[k]"),
"vm error: uncaught TypeError: {object}"
);
} }
+2 -8
View File
@@ -5,9 +5,7 @@ use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<JsValue, String> { fn eval(source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default(); let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
let module = Compiler::new() let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
.compile(&program)
.map_err(|e| format!("Compile: {}", e))?;
let mut vm = Vm::new(); let mut vm = Vm::new();
vm.run(&module) vm.run(&module)
} }
@@ -23,11 +21,7 @@ fn const_reassignment_throws() {
let result = eval("const x = 1; x = 2"); let result = eval("const x = 1; x = 2");
assert!(result.is_err()); assert!(result.is_err());
let err = result.unwrap_err(); let err = result.unwrap_err();
assert!( assert!(err.contains("TypeError") || err.contains("constant"), "got: {}", err);
err.contains("TypeError") || err.contains("constant"),
"got: {}",
err
);
} }
#[test] #[test]
+3 -13
View File
@@ -37,10 +37,7 @@ fn eval_val(source: &str) -> (Vm, Result<JsValue, String>) {
fn to_str(vm: &Vm, val: JsValue) -> String { fn to_str(vm: &Vm, val: JsValue) -> String {
if val.is_string() { if val.is_string() {
vm.kernel() vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default()
} else { } else {
format!("{}", val) format!("{}", val)
} }
@@ -99,11 +96,7 @@ fn tagged_template_basic() {
// This avoids bytecode function call complexity. // This avoids bytecode function call complexity.
let result = eval("Math.max`hello ${42} world`"); let result = eval("Math.max`hello ${42} world`");
// Math.max on the args should work - just verify it doesn't crash // Math.max on the args should work - just verify it doesn't crash
assert!( assert!(!result.starts_with("vm error:"), "Tagged template should not crash, got: {}", result);
!result.starts_with("vm error:"),
"Tagged template should not crash, got: {}",
result
);
} }
#[test] #[test]
@@ -111,8 +104,5 @@ fn tagged_template_compiles_no_error() {
// Verify that tagged templates compile without error (even if the tag // Verify that tagged templates compile without error (even if the tag
// function behavior isn't fully tested) // function behavior isn't fully tested)
let result = eval("function t(s,v){ return s[0]+v; } t`x${1}`"); let result = eval("function t(s,v){ return s[0]+v; } t`x${1}`");
assert!( assert!(!result.starts_with("compile error:"), "Tagged template should compile");
!result.starts_with("compile error:"),
"Tagged template should compile"
);
} }
+1 -3
View File
@@ -121,9 +121,7 @@ fn as_js_object_ptr_non_object_returns_null() {
assert!(JsValue::null().as_js_object_ptr().is_null()); assert!(JsValue::null().as_js_object_ptr().is_null());
assert!(JsValue::undefined().as_js_object_ptr().is_null()); assert!(JsValue::undefined().as_js_object_ptr().is_null());
let x = 42u8; let x = 42u8;
assert!(!JsValue::object(&x as *const u8) assert!(!JsValue::object(&x as *const u8).as_js_object_ptr().is_null());
.as_js_object_ptr()
.is_null());
} }
#[test] #[test]
+6 -24
View File
@@ -27,10 +27,7 @@ fn test_throw_caught_by_catch() {
#[test] #[test]
fn test_throw_without_catch() { fn test_throw_without_catch() {
let result = eval("throw 'err';"); let result = eval("throw 'err';");
assert!( assert!(result.contains("uncaught"), "expected uncaught, got: {result}");
result.contains("uncaught"),
"expected uncaught, got: {result}"
);
} }
#[test] #[test]
@@ -41,34 +38,22 @@ fn test_try_finally_normal_path() {
#[test] #[test]
fn test_try_finally_exception_path() { fn test_try_finally_exception_path() {
let result = eval("var x = 0; try { throw 1; } finally { x = 2; }"); let result = eval("var x = 0; try { throw 1; } finally { x = 2; }");
assert!( assert!(result.contains("uncaught"), "expected uncaught, got: {result}");
result.contains("uncaught"),
"expected uncaught, got: {result}"
);
} }
#[test] #[test]
fn test_nested_try_inner_catches() { fn test_nested_try_inner_catches() {
assert_eq!( assert_eq!(eval("try { try { throw 1; } catch(e) { e; } } catch(e) { 2; }"), "1");
eval("try { try { throw 1; } catch(e) { e; } } catch(e) { 2; }"),
"1"
);
} }
#[test] #[test]
fn test_nested_try_outer_catches() { fn test_nested_try_outer_catches() {
assert_eq!( assert_eq!(eval("try { try { throw 1; } catch(e) { throw 2; } } catch(e) { e; }"), "2");
eval("try { try { throw 1; } catch(e) { throw 2; } } catch(e) { e; }"),
"2"
);
} }
#[test] #[test]
fn test_try_catch_finally() { fn test_try_catch_finally() {
assert_eq!( assert_eq!(eval("var x = 0; try { x = 1; } catch(e) { x = 2; } finally { x = 3; } x;"), "3");
eval("var x = 0; try { x = 1; } catch(e) { x = 2; } finally { x = 3; } x;"),
"3"
);
} }
#[test] #[test]
@@ -92,8 +77,5 @@ fn test_throw_err_type_error_is_object() {
#[test] #[test]
fn test_uncaught_type_error_message() { fn test_uncaught_type_error_message() {
let result = eval("null.prop;"); let result = eval("null.prop;");
assert!( assert!(result.contains("TypeError"), "expected TypeError in message, got: {result}");
result.contains("TypeError"),
"expected TypeError in message, got: {result}"
);
} }
+16 -1
View File
@@ -1 +1,16 @@
max_width = 100 max_width = 120
use_small_heuristics = "Max"
fn_call_width = 90
attr_fn_like_width = 90
struct_lit_width = 30
struct_variant_width = 50
array_width = 90
chain_width = 80
single_line_if_else_max_width = 80
single_line_let_else_max_width = 80
fn_params_layout = "Compressed"
edition = "2021"
style_edition = "2021"