diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 992ad166..d5ebf6ea 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -340,6 +340,7 @@ impl Compiler { BinaryOperator::GreaterThan => OpCode::GT, BinaryOperator::LessEqualThan => OpCode::LTE, BinaryOperator::GreaterEqualThan => OpCode::GTE, + BinaryOperator::In => OpCode::IN, _ => return Err(format!("unsupported binary operator: {:?}", bin.operator)), }; let r = ctx.alloc_reg(); @@ -348,12 +349,23 @@ impl Compiler { } Expression::UnaryExpression(un) => { let arg = self.emit_expression(&un.argument, ctx)?; - if matches!(un.operator, UnaryOperator::UnaryNegation) { - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::NEG, r, arg, 0)); - Ok(r) - } else { - Err(format!("unsupported unary operator: {:?}", un.operator)) + match un.operator { + UnaryOperator::UnaryNegation => { + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::NEG, r, arg, 0)); + Ok(r) + } + UnaryOperator::Typeof => { + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::TYPEOF, r, arg, 0)); + Ok(r) + } + UnaryOperator::Void => { + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::VOID, r, arg, 0)); + Ok(r) + } + _ => Err(format!("unsupported unary operator: {:?}", un.operator)), } } Expression::StaticMemberExpression(member) => { diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index cf8364b0..f1a91d3e 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -426,6 +426,51 @@ impl Vm { } } + OpCode::TYPEOF => { + let val = self.regs[a]; + let result = if val.is_undefined() { + "undefined" + } else if val.is_null() { + "object" + } else if val.is_bool() { + "boolean" + } else if val.is_int() || val.is_double() { + "number" + } else if val.is_string() { + "string" + } else if val.is_object() { + let obj = unsafe { &*val.as_js_object_ptr() }; + if obj.is_function() { + "function" + } else { + "object" + } + } else { + "undefined" + }; + self.regs[rd] = self.intern(result); + } + + OpCode::VOID => { + self.regs[rd] = JsValue::undefined(); + } + + OpCode::IN => { + let key_val = self.regs[a]; + let obj_ptr = self.regs[b].as_object_ptr() as *mut JsObject; + if obj_ptr.is_null() { + return Err("IN right-hand side is not an object".into()); + } + let obj = unsafe { &*obj_ptr }; + let prop_name_si = if key_val.is_string() { + key_val.as_string_index() + } else { + return Err("IN key must be a string".into()); + }; + let found = self.resolve_property(obj, prop_name_si).is_some(); + self.regs[rd] = JsValue::bool(found); + } + _ => { if !op.is_implemented() { return Ok(JsValue::undefined());