Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

157 lines
5.3 KiB

use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::{node::Node, op},
Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "vm")]
use crate::{
profiler::BoaProfiler,
vm::{compilation::CodeGen, Compiler, Instruction},
};
/// A unary operation is an operation with only one operand.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct UnaryOp {
op: op::UnaryOp,
target: Box<Node>,
}
impl UnaryOp {
/// Creates a new `UnaryOp` AST node.
pub(in crate::syntax) fn new<V>(op: op::UnaryOp, target: V) -> Self
where
V: Into<Node>,
{
Self {
op,
target: Box::new(target.into()),
}
}
/// Gets the unary operation of the node.
pub fn op(&self) -> op::UnaryOp {
self.op
}
/// Gets the target of this unary operator.
pub fn target(&self) -> &Node {
self.target.as_ref()
}
}
impl Executable for UnaryOp {
fn run(&self, context: &mut Context) -> Result<Value> {
let x = self.target().run(context)?;
Ok(match self.op() {
op::UnaryOp::Minus => x.neg(context)?,
op::UnaryOp::Plus => Value::from(x.to_number(context)?),
op::UnaryOp::IncrementPost => {
let ret = x.clone();
let result = x.to_number(context)? + 1.0;
context.set_value(self.target(), result.into())?;
ret
}
op::UnaryOp::IncrementPre => {
let result = x.to_number(context)? + 1.0;
context.set_value(self.target(), result.into())?
}
op::UnaryOp::DecrementPost => {
let ret = x.clone();
let result = x.to_number(context)? - 1.0;
context.set_value(self.target(), result.into())?;
ret
}
op::UnaryOp::DecrementPre => {
let result = x.to_number(context)? - 1.0;
context.set_value(self.target(), result.into())?
}
op::UnaryOp::Not => x.not(context)?.into(),
op::UnaryOp::Tilde => {
let num_v_a = x.to_number(context)?;
Value::from(if num_v_a.is_nan() {
-1
} else {
// TODO: this is not spec compliant.
!(num_v_a as i32)
})
}
op::UnaryOp::Void => Value::undefined(),
op::UnaryOp::Delete => match *self.target() {
Node::GetConstField(ref get_const_field) => Value::boolean(
get_const_field
.obj()
.run(context)?
.to_object(context)?
.delete(&get_const_field.field().into()),
),
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(context)?;
let field = &get_field.field().run(context)?;
let res = obj
.to_object(context)?
.delete(&field.to_property_key(context)?);
return Ok(Value::boolean(res));
}
Node::Identifier(_) => Value::boolean(false),
Node::ArrayDecl(_)
| Node::Block(_)
| Node::Const(_)
| Node::FunctionDecl(_)
| Node::FunctionExpr(_)
| Node::New(_)
| Node::Object(_)
| Node::UnaryOp(_) => Value::boolean(true),
_ => return context.throw_syntax_error(format!("wrong delete argument {}", self)),
},
op::UnaryOp::TypeOf => Value::from(x.get_type().as_str()),
})
}
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.op, self.target)
}
}
impl From<UnaryOp> for Node {
fn from(op: UnaryOp) -> Self {
Self::UnaryOp(op)
}
}
#[cfg(feature = "vm")]
impl CodeGen for UnaryOp {
fn compile(&self, compiler: &mut Compiler) {
let _timer = BoaProfiler::global().start_event("UnaryOp", "codeGen");
self.target().compile(compiler);
match self.op {
op::UnaryOp::Void => compiler.add_instruction(Instruction::Void),
op::UnaryOp::Plus => compiler.add_instruction(Instruction::Pos),
op::UnaryOp::Minus => compiler.add_instruction(Instruction::Neg),
op::UnaryOp::TypeOf => compiler.add_instruction(Instruction::TypeOf),
op::UnaryOp::Not => compiler.add_instruction(Instruction::Not),
op::UnaryOp::Tilde => compiler.add_instruction(Instruction::BitNot),
op::UnaryOp::IncrementPost => {}
op::UnaryOp::IncrementPre => {}
op::UnaryOp::DecrementPost => {}
op::UnaryOp::DecrementPre => {}
op::UnaryOp::Delete => {}
}
}
}