diff --git a/crates/js/src/bytecode/builder.rs b/crates/js/src/bytecode/builder.rs index 96dc38bd..08eb8355 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::object, Value}; +use crate::{parser::identifiers::Identifier, value::object, Value}; #[derive(Clone, Copy, Debug)] pub struct Register(usize); @@ -313,6 +313,21 @@ impl<'a> BasicBlockBuilder<'a> { } } + pub fn member_access_with_identifier( + &mut self, + base: Register, + identifier: Identifier, + ) -> Register { + let dst = self.allocate_register(); + let instruction = Instruction::MemberAccessWithIdentifier { + base, + identifier, + dst, + }; + self.push_instruction(instruction); + dst + } + pub fn create_data_property_or_throw( &mut self, object: Register, diff --git a/crates/js/src/bytecode/instruction.rs b/crates/js/src/bytecode/instruction.rs index 254a7092..c4311e99 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::object, Value}; +use crate::{parser::identifiers::Identifier, value::object, Value}; #[derive(Clone, Copy, Debug)] pub struct VariableHandle(usize); @@ -151,6 +151,11 @@ pub enum Instruction { Throw { value: Register, }, + MemberAccessWithIdentifier { + base: Register, + identifier: Identifier, + dst: Register, + }, CreateDataPropertyOrThrow { object: Register, property_key: object::PropertyKey, diff --git a/crates/js/src/bytecode/vm.rs b/crates/js/src/bytecode/vm.rs index ba9163e2..d9364395 100644 --- a/crates/js/src/bytecode/vm.rs +++ b/crates/js/src/bytecode/vm.rs @@ -162,6 +162,19 @@ impl Vm { let value = self.register(*value).clone().get_value()?; return Err(Exception::new(value)); }, + Instruction::MemberAccessWithIdentifier { + base, + identifier, + dst, + } => { + // https://262.ecma-international.org/14.0/#sec-property-accessors-runtime-semantics-evaluation + let base_value = self.register(*base).get_value()?; + let result = Value::evaluate_property_access_with_identifier_key( + base_value, + identifier.clone(), + ); + self.set_register(*dst, result.into()); + }, other => todo!("Implement instruction {other:?}"), } diff --git a/crates/js/src/parser/expressions/member.rs b/crates/js/src/parser/expressions/member.rs index 76fcf151..4fae54d7 100644 --- a/crates/js/src/parser/expressions/member.rs +++ b/crates/js/src/parser/expressions/member.rs @@ -1,3 +1,4 @@ +//! use crate::{ bytecode::{self, CompileToBytecode}, parser::{ @@ -9,6 +10,7 @@ use crate::{ use super::{parse_primary_expression, Expression}; +/// #[derive(Clone, Debug)] pub struct MemberExpression { /// The element whose member is being accessed @@ -26,6 +28,7 @@ pub enum Member { } impl MemberExpression { + /// pub fn parse( tokenizer: &mut Tokenizer<'_>, ) -> Result { @@ -69,13 +72,19 @@ impl CompileToBytecode for MemberExpression { type Result = bytecode::Register; fn compile(&self, builder: &mut bytecode::ProgramBuilder) -> Self::Result { - _ = builder; - _ = self.base; - + // https://262.ecma-international.org/14.0/#sec-property-accessors-runtime-semantics-evaluation + let base = self.base.compile(builder); match &self.member { - Member::Identifier(ident) => _ = ident, - Member::Bracket(bracket) => _ = bracket, + Member::Identifier(ident) => { + let value = builder + .get_current_block() + .member_access_with_identifier(base, ident.clone()); + value + }, + Member::Bracket(bracket) => { + _ = bracket; + todo!("compile member expression"); + }, } - todo!("compile member expression") } } diff --git a/crates/js/src/parser/identifiers.rs b/crates/js/src/parser/identifiers.rs index a0302ffb..0f1f9ebe 100644 --- a/crates/js/src/parser/identifiers.rs +++ b/crates/js/src/parser/identifiers.rs @@ -1,5 +1,7 @@ //! +use core::fmt; + use super::{ tokenization::{GoalSymbol, SkipLineTerminators, Token, Tokenizer}, SyntaxError, @@ -141,3 +143,9 @@ pub(crate) fn parse_identifier_reference( Err(tokenizer.syntax_error("expected identifier")) } } + +impl fmt::Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} diff --git a/crates/js/src/value/mod.rs b/crates/js/src/value/mod.rs index 693b9019..28a31c35 100644 --- a/crates/js/src/value/mod.rs +++ b/crates/js/src/value/mod.rs @@ -8,7 +8,10 @@ pub use object::Object; pub use reference_record::{ReferenceRecord, ValueOrReference}; pub use symbol::Symbol; -use crate::bytecode::{Exception, ThrowCompletionOr}; +use crate::{ + bytecode::{Exception, ThrowCompletionOr}, + parser::identifiers::Identifier, +}; const SPEC_CANNOT_FAIL: &str = "This operation cannot fail according to the specification (indicated by '!')"; @@ -453,6 +456,23 @@ impl Value { }, } } + + /// + #[must_use] + pub fn evaluate_property_access_with_identifier_key( + base_value: Self, + identifier: Identifier, + ) -> ReferenceRecord { + // 1. Let propertyNameString be StringValue of identifierName. + let property_name_string = identifier.to_string(); + + // 2. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyNameString, + // [[Strict]]: strict, [[ThisValue]]: empty }. + ReferenceRecord { + base: base_value, + referenced_name: property_name_string, + } + } } /// diff --git a/crates/js/src/value/reference_record.rs b/crates/js/src/value/reference_record.rs index e78bb6ea..38527c8a 100644 --- a/crates/js/src/value/reference_record.rs +++ b/crates/js/src/value/reference_record.rs @@ -7,8 +7,8 @@ use super::{object::PropertyKey, Value}; /// #[derive(Clone, Debug)] pub struct ReferenceRecord { - base: Value, - referenced_name: String, + pub base: Value, + pub referenced_name: String, } #[derive(Clone, Debug)]