From c9014d2ff1c84de038c605c18fa560b40726bae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20W=C3=BClker?= Date: Thu, 28 Mar 2024 23:37:41 +0100 Subject: [PATCH] js: Implement object creation in bytecode --- crates/js/src/bytecode/builder.rs | 16 +++++++- crates/js/src/bytecode/instruction.rs | 7 +++- crates/js/src/bytecode/vm.rs | 28 +++++++++++++- crates/js/src/parser/expressions/object.rs | 43 +++++++++++++++++++++- crates/js/src/value/object/mod.rs | 12 ++++++ 5 files changed, 101 insertions(+), 5 deletions(-) diff --git a/crates/js/src/bytecode/builder.rs b/crates/js/src/bytecode/builder.rs index d9db0249..ee4fe990 100644 --- a/crates/js/src/bytecode/builder.rs +++ b/crates/js/src/bytecode/builder.rs @@ -1,5 +1,5 @@ use super::{BasicBlock, BasicBlockExit, Instruction, Program}; -use crate::Value; +use crate::{value::object, Value}; #[derive(Clone, Copy, Debug)] pub struct Register(usize); @@ -296,4 +296,18 @@ impl<'a> BasicBlockBuilder<'a> { if_false, } } + + pub fn create_data_property_or_throw( + &mut self, + object: Register, + property_key: object::PropertyKey, + property_value: Register, + ) { + let instruction = Instruction::CreateDataPropertyOrThrow { + object, + property_key, + property_value, + }; + self.push_instruction(instruction); + } } diff --git a/crates/js/src/bytecode/instruction.rs b/crates/js/src/bytecode/instruction.rs index 3241c309..e2cd463b 100644 --- a/crates/js/src/bytecode/instruction.rs +++ b/crates/js/src/bytecode/instruction.rs @@ -1,5 +1,5 @@ use super::Register; -use crate::Value; +use crate::{value::object, Value}; #[derive(Clone, Copy, Debug)] pub struct VariableHandle(usize); @@ -141,4 +141,9 @@ pub enum Instruction { Throw { value: Register, }, + CreateDataPropertyOrThrow { + object: Register, + property_key: object::PropertyKey, + property_value: Register, + }, } diff --git a/crates/js/src/bytecode/vm.rs b/crates/js/src/bytecode/vm.rs index e0ba32fb..0064aaa1 100644 --- a/crates/js/src/bytecode/vm.rs +++ b/crates/js/src/bytecode/vm.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use super::{ BasicBlock, BasicBlockExit, Exception, Instruction, Program, Register, ThrowCompletionOr, }; -use crate::Value; +use crate::{value::Object, Value}; #[derive(Clone, Debug, Default)] pub struct Vm { @@ -65,6 +65,11 @@ impl Vm { &self.registers[register.index()] } + #[must_use] + fn register_mut(&mut self, register: Register) -> &mut Value { + &mut self.registers[register.index()] + } + fn set_register(&mut self, register: Register, value: Value) { self.registers[register.index()] = value; } @@ -115,6 +120,27 @@ impl Vm { let value = self.register(*value).clone(); return Err(Exception::new(value)); }, + Instruction::CreateDataPropertyOrThrow { + object, + property_key, + property_value, + } => { + // https://262.ecma-international.org/14.0/#sec-createdatapropertyorthrow + let property_value = self.register(*property_value).clone(); + + let Value::Object(object) = self.register_mut(*object) else { + panic!("Cannot create property on non-object"); + }; + + // 1. Let success be ? CreateDataProperty(O, P, V). + let success = Object::create_data_property(object, property_key, property_value)?; + + // 2. If success is false, throw a TypeError exception. + if !success { + // FIXME: This should be a TypeError + return Err(Exception::new(Value::Null)); + } + }, other => todo!("Implement instruction {other:?}"), } diff --git a/crates/js/src/parser/expressions/object.rs b/crates/js/src/parser/expressions/object.rs index 327341cc..aad648fc 100644 --- a/crates/js/src/parser/expressions/object.rs +++ b/crates/js/src/parser/expressions/object.rs @@ -7,6 +7,7 @@ use crate::{ tokenization::{Punctuator, SkipLineTerminators, Token, Tokenizer}, SyntaxError, }, + value::{object::PropertyKey, Object}, }; /// @@ -75,7 +76,45 @@ impl CompileToBytecode for ObjectLiteral { type Result = bytecode::Register; fn compile(&self, builder: &mut bytecode::ProgramBuilder) -> Self::Result { - _ = builder; - todo!() + let object = builder + .get_current_block() + .allocate_register_with_value(Object::default().into()); + + for property_definition in &self.property_definitions { + let property_register = property_definition.compile(builder); + builder.get_current_block().create_data_property_or_throw( + object, + PropertyKey::String(property_register.name), + property_register.value, + ); + } + + object + } +} + +#[derive(Debug)] +struct PropertyToBeCreated { + name: String, + value: bytecode::Register, +} + +impl CompileToBytecode for PropertyDefinition { + type Result = PropertyToBeCreated; + + fn compile(&self, builder: &mut bytecode::ProgramBuilder) -> Self::Result { + let mut builder = builder.get_current_block(); + + match self { + Self::IdentifierRef(identifier_reference) => { + let expr_value = builder.allocate_register(); + builder.load_variable(identifier_reference.clone(), expr_value); + + PropertyToBeCreated { + name: identifier_reference.clone(), + value: expr_value, + } + }, + } } } diff --git a/crates/js/src/value/object/mod.rs b/crates/js/src/value/object/mod.rs index c8fb9a70..56bc3235 100644 --- a/crates/js/src/value/object/mod.rs +++ b/crates/js/src/value/object/mod.rs @@ -4,6 +4,8 @@ mod vtable; use crate::bytecode::{Exception, ThrowCompletionOr}; +use self::vtable::ObjectMethods; + use super::Value; use std::{collections::HashMap, fmt, ptr}; @@ -239,3 +241,13 @@ impl fmt::Debug for Object { .finish() } } + +impl Default for Object { + fn default() -> Self { + Self { + extensible: true, + properties: HashMap::default(), + methods: ObjectMethods::ORDINARY, + } + } +}