mirror of https://github.com/boa-dev/boa.git
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
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 => {} |
|
} |
|
} |
|
}
|
|
|