Browse Source

Implement object literals for vm (#1668)

pull/1682/head
raskad 3 years ago committed by GitHub
parent
commit
7e6bef92b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 125
      boa/src/bytecompiler.rs
  2. 4
      boa/src/vm/code_block.rs
  3. 105
      boa/src/vm/mod.rs
  4. 40
      boa/src/vm/opcode.rs

125
boa/src/bytecompiler.rs

@ -3,7 +3,10 @@ use gc::Gc;
use crate::{
builtins::function::ThisMode,
syntax::ast::{
node::{Declaration, GetConstField, GetField, StatementList},
node::{
Declaration, GetConstField, GetField, MethodDefinitionKind, PropertyDefinition,
PropertyName, StatementList,
},
op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp},
Const, Node,
},
@ -67,19 +70,19 @@ impl ByteCompiler {
}
#[inline]
fn get_or_insert_literal(&mut self, liternal: Literal) -> u32 {
if let Some(index) = self.literals_map.get(&liternal) {
fn get_or_insert_literal(&mut self, literal: Literal) -> u32 {
if let Some(index) = self.literals_map.get(&literal) {
return *index;
}
let value = match liternal.clone() {
let value = match literal.clone() {
Literal::String(value) => JsValue::new(value),
Literal::BigInt(value) => JsValue::new(value),
};
let index = self.code_block.literals.len() as u32;
self.code_block.literals.push(value);
self.literals_map.insert(liternal, index);
self.literals_map.insert(literal, index);
index
}
@ -153,8 +156,8 @@ impl ByteCompiler {
}
#[inline]
fn emit_push_literal(&mut self, liternal: Literal) {
let index = self.get_or_insert_literal(liternal);
fn emit_push_literal(&mut self, literal: Literal) {
let index = self.get_or_insert_literal(literal);
self.emit(Opcode::PushLiteral, &[index]);
}
@ -583,10 +586,97 @@ impl ByteCompiler {
}
}
Node::Object(object) => {
if object.properties().is_empty() {
self.emit(Opcode::PushEmptyObject, &[]);
} else {
todo!("object literal with properties");
self.emit_opcode(Opcode::PushEmptyObject);
for property in object.properties() {
self.emit_opcode(Opcode::Dup);
match property {
PropertyDefinition::IdentifierReference(identifier_reference) => {
let index = self.get_or_insert_name(identifier_reference);
self.emit(Opcode::SetPropertyByName, &[index]);
}
PropertyDefinition::Property(name, node) => {
self.compile_stmt(node, true);
self.emit_opcode(Opcode::Swap);
match name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(name);
self.emit(Opcode::SetPropertyByName, &[index]);
}
PropertyName::Computed(name_node) => {
self.compile_stmt(name_node, true);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::SetPropertyByValue);
}
}
}
PropertyDefinition::MethodDefinition(kind, name, func) => {
match kind {
MethodDefinitionKind::Get => {
self.compile_stmt(&func.clone().into(), true);
self.emit_opcode(Opcode::Swap);
match name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(name);
self.emit(Opcode::SetPropertyGetterByName, &[index]);
}
PropertyName::Computed(name_node) => {
self.compile_stmt(name_node, true);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::SetPropertyGetterByValue);
}
}
}
MethodDefinitionKind::Set => {
self.compile_stmt(&func.clone().into(), true);
self.emit_opcode(Opcode::Swap);
match name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(name);
self.emit(Opcode::SetPropertySetterByName, &[index]);
}
PropertyName::Computed(name_node) => {
self.compile_stmt(name_node, true);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::SetPropertySetterByValue);
}
}
}
MethodDefinitionKind::Ordinary => {
self.compile_stmt(&func.clone().into(), true);
self.emit_opcode(Opcode::Swap);
match name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(name);
self.emit(Opcode::SetPropertyByName, &[index]);
}
PropertyName::Computed(name_node) => {
self.compile_stmt(name_node, true);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::SetPropertyByValue);
}
}
}
MethodDefinitionKind::Generator => {
// TODO: Implement generators
self.emit_opcode(Opcode::PushUndefined);
self.emit_opcode(Opcode::Swap);
match name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(name);
self.emit(Opcode::SetPropertyByName, &[index]);
}
PropertyName::Computed(name_node) => {
self.compile_stmt(name_node, true);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::SetPropertyByValue);
}
}
}
}
}
// TODO: Spread Object
PropertyDefinition::SpreadObject(_) => todo!(),
}
}
if !use_expr {
@ -883,7 +973,7 @@ impl ByteCompiler {
Arrow,
}
let (kind, name, paramaters, body) = match function {
let (kind, name, parameters, body) = match function {
Node::FunctionDecl(function) => (
FunctionKind::Declaration,
Some(function.name()),
@ -905,7 +995,7 @@ impl ByteCompiler {
_ => unreachable!(),
};
let length = paramaters.len() as u32;
let length = parameters.len() as u32;
let mut code = CodeBlock::new(name.unwrap_or("").into(), length, false, true);
if let FunctionKind::Arrow = kind {
@ -926,7 +1016,7 @@ impl ByteCompiler {
compiler.compile_stmt(node, false);
}
compiler.code_block.params = paramaters.to_owned().into_boxed_slice();
compiler.code_block.params = parameters.to_owned().into_boxed_slice();
// TODO These are redundant if a function returns so may need to check if a function returns and adding these if it doesn't
compiler.emit(Opcode::PushUndefined, &[]);
@ -945,12 +1035,7 @@ impl ByteCompiler {
let access = Access::Variable { index };
self.access_set(access, None, false);
}
FunctionKind::Expression => {
if use_expr {
self.emit(Opcode::Dup, &[]);
}
}
FunctionKind::Arrow => {
FunctionKind::Expression | FunctionKind::Arrow => {
if !use_expr {
self.emit(Opcode::Pop, &[]);
}

4
boa/src/vm/code_block.rs

@ -160,6 +160,8 @@ impl CodeBlock {
| Opcode::SetName
| Opcode::GetPropertyByName
| Opcode::SetPropertyByName
| Opcode::SetPropertyGetterByName
| Opcode::SetPropertySetterByName
| Opcode::DeletePropertyByName => {
let operand = self.read::<u32>(*pc);
*pc += size_of::<u32>();
@ -210,6 +212,8 @@ impl CodeBlock {
| Opcode::Dec
| Opcode::GetPropertyByValue
| Opcode::SetPropertyByValue
| Opcode::SetPropertyGetterByValue
| Opcode::SetPropertySetterByValue
| Opcode::DeletePropertyByValue
| Opcode::ToBoolean
| Opcode::Throw

105
boa/src/vm/mod.rs

@ -3,23 +3,19 @@
//! plus an interpreter to execute those instructions
use crate::{
builtins::Array, environment::lexical_environment::VariableScope, BoaProfiler, Context,
JsResult, JsValue,
builtins::Array, environment::lexical_environment::VariableScope, property::PropertyDescriptor,
vm::code_block::Readable, BoaProfiler, Context, JsResult, JsValue,
};
use std::{convert::TryInto, mem::size_of, time::Instant};
mod call_frame;
mod code_block;
mod opcode;
pub use call_frame::CallFrame;
pub use code_block::CodeBlock;
pub use code_block::JsVmFunction;
pub use code_block::{CodeBlock, JsVmFunction};
pub use opcode::Opcode;
use std::{convert::TryInto, mem::size_of, time::Instant};
use self::code_block::Readable;
#[cfg(test)]
mod tests;
/// Virtual Machine.
@ -405,6 +401,99 @@ impl Context {
let key = key.to_property_key(self)?;
object.set(key, value, true, self)?;
}
Opcode::SetPropertyGetterByName => {
let index = self.vm.read::<u32>();
let object = self.vm.pop();
let value = self.vm.pop();
let object = object.to_object(self)?;
let name = self.vm.frame().code.variables[index as usize]
.clone()
.into();
let set = object
.__get_own_property__(&name, self)?
.as_ref()
.and_then(|a| a.set())
.cloned();
object.__define_own_property__(
name,
PropertyDescriptor::builder()
.maybe_get(Some(value))
.maybe_set(set)
.enumerable(true)
.configurable(true)
.build(),
self,
)?;
}
Opcode::SetPropertyGetterByValue => {
let object = self.vm.pop();
let key = self.vm.pop();
let value = self.vm.pop();
let object = object.to_object(self)?;
let name = key.to_property_key(self)?;
let set = object
.__get_own_property__(&name, self)?
.as_ref()
.and_then(|a| a.set())
.cloned();
object.__define_own_property__(
name,
PropertyDescriptor::builder()
.maybe_get(Some(value))
.maybe_set(set)
.enumerable(true)
.configurable(true)
.build(),
self,
)?;
}
Opcode::SetPropertySetterByName => {
let index = self.vm.read::<u32>();
let object = self.vm.pop();
let value = self.vm.pop();
let object = object.to_object(self)?;
let name = self.vm.frame().code.variables[index as usize]
.clone()
.into();
let get = object
.__get_own_property__(&name, self)?
.as_ref()
.and_then(|a| a.get())
.cloned();
object.__define_own_property__(
name,
PropertyDescriptor::builder()
.maybe_set(Some(value))
.maybe_get(get)
.enumerable(true)
.configurable(true)
.build(),
self,
)?;
}
Opcode::SetPropertySetterByValue => {
let object = self.vm.pop();
let key = self.vm.pop();
let value = self.vm.pop();
let object = object.to_object(self)?;
let name = key.to_property_key(self)?;
let get = object
.__get_own_property__(&name, self)?
.as_ref()
.and_then(|a| a.get())
.cloned();
object.__define_own_property__(
name,
PropertyDescriptor::builder()
.maybe_set(Some(value))
.maybe_get(get)
.enumerable(true)
.configurable(true)
.build(),
self,
)?;
}
Opcode::DeletePropertyByName => {
let index = self.vm.read::<u32>();
let key = self.vm.frame().code.variables[index as usize].clone();

40
boa/src/vm/opcode.rs

@ -456,6 +456,42 @@ pub enum Opcode {
/// Stack: value, key, object **=>**
SetPropertyByValue,
/// Sets a getter property by name of an object.
///
/// Like `get name() value`
///
/// Operands: name_index: `u32`
///
/// Stack: value, object **=>**
SetPropertyGetterByName,
/// Sets a getter property by value of an object.
///
/// Like `get [key]() value`
///
/// Operands:
///
/// Stack: value, key, object **=>**
SetPropertyGetterByValue,
/// Sets a setter property by name of an object.
///
/// Like `set name() value`
///
/// Operands: name_index: `u32`
///
/// Stack: value, object **=>**
SetPropertySetterByName,
/// Sets a setter property by value of an object.
///
/// Like `set [key]() value`
///
/// Operands:
///
/// Stack: value, key, object **=>**
SetPropertySetterByValue,
/// Deletes a property by name of an object.
///
/// Like `delete object.key.`
@ -639,6 +675,10 @@ impl Opcode {
Opcode::GetPropertyByValue => "GetPropertyByValue",
Opcode::SetPropertyByName => "SetPropertyByName",
Opcode::SetPropertyByValue => "SetPropertyByValue",
Opcode::SetPropertyGetterByName => "SetPropertyGetterByName",
Opcode::SetPropertyGetterByValue => "SetPropertyGetterByValue",
Opcode::SetPropertySetterByName => "SetPropertySetterByName",
Opcode::SetPropertySetterByValue => "SetPropertySetterByValue",
Opcode::DeletePropertyByName => "DeletePropertyByName",
Opcode::DeletePropertyByValue => "DeletePropertyByValue",
Opcode::Jump => "Jump",

Loading…
Cancel
Save