Skip to content

Commit

Permalink
Merge branch 'master' into property-accessors
Browse files Browse the repository at this point in the history
  • Loading branch information
tofpie committed Dec 31, 2020
2 parents 92e4fc1 + 23bc476 commit 8a7ac4e
Show file tree
Hide file tree
Showing 16 changed files with 304 additions and 132 deletions.
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion boa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ console = []
[dependencies]
gc = { version = "0.3.6", features = ["derive"] }
serde = { version = "1.0.118", features = ["derive"] }
serde_json = "1.0.60"
serde_json = "1.0.61"
rand = "0.8.0"
num-traits = "0.2.14"
regress = "0.2.0"
Expand Down
47 changes: 30 additions & 17 deletions boa/src/syntax/ast/node/operator/bin_op/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,27 @@ impl BinOp {
}

/// Runs the assignment operators.
fn run_assign(op: AssignOp, x: Value, y: Value, context: &mut Context) -> Result<Value> {
fn run_assign(op: AssignOp, x: Value, y: &Node, context: &mut Context) -> Result<Value> {
match op {
AssignOp::Add => x.add(&y, context),
AssignOp::Sub => x.sub(&y, context),
AssignOp::Mul => x.mul(&y, context),
AssignOp::Exp => x.pow(&y, context),
AssignOp::Div => x.div(&y, context),
AssignOp::Mod => x.rem(&y, context),
AssignOp::And => x.bitand(&y, context),
AssignOp::Or => x.bitor(&y, context),
AssignOp::Xor => x.bitxor(&y, context),
AssignOp::Shl => x.shl(&y, context),
AssignOp::Shr => x.shr(&y, context),
AssignOp::Ushr => x.ushr(&y, context),
AssignOp::Add => x.add(&y.run(context)?, context),
AssignOp::Sub => x.sub(&y.run(context)?, context),
AssignOp::Mul => x.mul(&y.run(context)?, context),
AssignOp::Exp => x.pow(&y.run(context)?, context),
AssignOp::Div => x.div(&y.run(context)?, context),
AssignOp::Mod => x.rem(&y.run(context)?, context),
AssignOp::And => x.bitand(&y.run(context)?, context),
AssignOp::Or => x.bitor(&y.run(context)?, context),
AssignOp::Xor => x.bitxor(&y.run(context)?, context),
AssignOp::Shl => x.shl(&y.run(context)?, context),
AssignOp::Shr => x.shr(&y.run(context)?, context),
AssignOp::Ushr => x.ushr(&y.run(context)?, context),
AssignOp::Coalesce => {
if x.is_null_or_undefined() {
Ok(y.run(context)?)
} else {
Ok(x)
}
}
}
}
}
Expand Down Expand Up @@ -167,6 +174,14 @@ impl Executable for BinOp {
self.rhs().run(context)?
}
}
LogOp::Coalesce => {
let left = self.lhs.run(context)?;
if left.is_null_or_undefined() {
self.rhs().run(context)?
} else {
left
}
}
}),
op::BinOp::Assign(op) => match self.lhs() {
Node::Identifier(ref name) => {
Expand All @@ -176,8 +191,7 @@ impl Executable for BinOp {
.get_binding_value(name.as_ref())
.map_err(|e| e.to_error(context))?;

let v_b = self.rhs().run(context)?;
let value = Self::run_assign(op, v_a, v_b, context)?;
let value = Self::run_assign(op, v_a, self.rhs(), context)?;
context
.realm_mut()
.environment
Expand All @@ -188,8 +202,7 @@ impl Executable for BinOp {
Node::GetConstField(ref get_const_field) => {
let v_r_a = get_const_field.obj().run(context)?;
let v_a = v_r_a.get_field(get_const_field.field(), context)?;
let v_b = self.rhs().run(context)?;
let value = Self::run_assign(op, v_a, v_b, context)?;
let value = Self::run_assign(op, v_a, self.rhs(), context)?;
v_r_a.set_field(get_const_field.field(), value.clone(), context)?;
Ok(value)
}
Expand Down
19 changes: 19 additions & 0 deletions boa/src/syntax/ast/node/operator/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,22 @@ fn instanceofoperator_rhs_not_callable() {
"\"TypeError: right-hand side of 'instanceof' is not callable\""
);
}

#[test]
fn logical_nullish_assignment() {
let scenario = r#"
let a = undefined;
a ??= 10;
a;
"#;

assert_eq!(&exec(scenario), "10");

let scenario = r#"
let a = 20;
a ??= 10;
a;
"#;

assert_eq!(&exec(scenario), "20");
}
27 changes: 27 additions & 0 deletions boa/src/syntax/ast/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,19 @@ pub enum LogOp {
/// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR
Or,

/// The nullish coalescing operator is a logical operator that returns the second operand
/// when its first operand is null or undefined, and otherwise returns its first operand.
///
/// Syntax: `x ?? y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-CoalesceExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
Coalesce,
}

impl Display for LogOp {
Expand All @@ -693,6 +706,7 @@ impl Display for LogOp {
match *self {
Self::And => "&&",
Self::Or => "||",
Self::Coalesce => "??",
}
)
}
Expand Down Expand Up @@ -950,6 +964,18 @@ pub enum AssignOp {
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift_assignment
Ushr,

/// The logical nullish assignment operator only assigns if the target variable is nullish (null or undefined).
///
/// Syntax: `x ??= y`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_nullish_assignment
Coalesce,
}

unsafe impl Trace for AssignOp {
Expand All @@ -974,6 +1000,7 @@ impl Display for AssignOp {
Self::Shl => "<<=",
Self::Shr => ">>=",
Self::Ushr => ">>>=",
Self::Coalesce => "??=",
}
)
}
Expand Down
8 changes: 8 additions & 0 deletions boa/src/syntax/ast/punctuator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub enum Punctuator {
AssignAdd,
/// `&=`
AssignAnd,
/// `??=`,
AssignCoalesce,
/// `/=`
AssignDiv,
/// `<<=`
Expand Down Expand Up @@ -65,6 +67,8 @@ pub enum Punctuator {
CloseBracket,
/// `)`
CloseParen,
/// `??`
Coalesce,
/// `:`
Colon,
/// `,`
Expand Down Expand Up @@ -137,6 +141,7 @@ impl Punctuator {
match self {
Self::AssignAdd => Some(BinOp::Assign(AssignOp::Add)),
Self::AssignAnd => Some(BinOp::Assign(AssignOp::And)),
Self::AssignCoalesce => Some(BinOp::Assign(AssignOp::Coalesce)),
Self::AssignDiv => Some(BinOp::Assign(AssignOp::Div)),
Self::AssignLeftSh => Some(BinOp::Assign(AssignOp::Shl)),
Self::AssignMod => Some(BinOp::Assign(AssignOp::Mod)),
Expand All @@ -157,6 +162,7 @@ impl Punctuator {
Self::Xor => Some(BinOp::Bit(BitOp::Xor)),
Self::BoolAnd => Some(BinOp::Log(LogOp::And)),
Self::BoolOr => Some(BinOp::Log(LogOp::Or)),
Self::Coalesce => Some(BinOp::Log(LogOp::Coalesce)),
Self::Eq => Some(BinOp::Comp(CompOp::Equal)),
Self::NotEq => Some(BinOp::Comp(CompOp::NotEqual)),
Self::StrictEq => Some(BinOp::Comp(CompOp::StrictEqual)),
Expand Down Expand Up @@ -194,6 +200,7 @@ impl Display for Punctuator {
Self::Assign => "=",
Self::AssignAdd => "+=",
Self::AssignAnd => "&=",
Self::AssignCoalesce => "??=",
Self::AssignDiv => "/=",
Self::AssignLeftSh => "<<=",
Self::AssignMod => "%=",
Expand All @@ -206,6 +213,7 @@ impl Display for Punctuator {
Self::AssignXor => "^=",
Self::BoolAnd => "&&",
Self::BoolOr => "||",
Self::Coalesce => "??",
Self::CloseBlock => "}",
Self::CloseBracket => "]",
Self::CloseParen => ")",
Expand Down
6 changes: 1 addition & 5 deletions boa/src/syntax/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,8 @@ impl<R> Lexer<R> {
Punctuator::CloseBracket.into(),
Span::new(start, self.cursor.pos()),
)),
'?' => Ok(Token::new(
Punctuator::Question.into(),
Span::new(start, self.cursor.pos()),
)),
'/' => self.lex_slash_token(start),
'=' | '*' | '+' | '-' | '%' | '|' | '&' | '^' | '<' | '>' | '!' | '~' => {
'=' | '*' | '+' | '-' | '%' | '|' | '&' | '^' | '<' | '>' | '!' | '~' | '?' => {
Operator::new(next_ch as u8).lex(&mut self.cursor, start)
}
_ => {
Expand Down
16 changes: 16 additions & 0 deletions boa/src/syntax/lexer/operator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This module implements lexing for operators (+, - etc.) used in the JavaScript programing language.

use super::{Cursor, Error, Tokenizer};
use crate::syntax::lexer::TokenKind;
use crate::{
profiler::BoaProfiler,
syntax::{
Expand Down Expand Up @@ -123,6 +124,21 @@ impl<R> Tokenizer<R> for Operator {
b'&' => op!(cursor, start_pos, Ok(Punctuator::AssignAnd), Ok(Punctuator::And), {
Some(b'&') => Ok(Punctuator::BoolAnd)
}),
b'?' => match cursor.peek()? {
Some(b'?') => {
let _ = cursor.next_byte()?.expect("? vanished");
op!(
cursor,
start_pos,
Ok(Punctuator::AssignCoalesce),
Ok(Punctuator::Coalesce)
)
}
_ => Ok(Token::new(
TokenKind::Punctuator(Punctuator::Question),
Span::new(start_pos, cursor.pos()),
)),
},
b'^' => op!(
cursor,
start_pos,
Expand Down
4 changes: 3 additions & 1 deletion boa/src/syntax/lexer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ fn check_punctuators() {
// https://tc39.es/ecma262/#sec-punctuators
let s = "{ ( ) [ ] . ... ; , < > <= >= == != === !== \
+ - * % -- << >> >>> & | ^ ! ~ && || ? : \
= += -= *= &= **= ++ ** <<= >>= >>>= &= |= ^= =>";
= += -= *= &= **= ++ ** <<= >>= >>>= &= |= ^= => ?? ??=";
let mut lexer = Lexer::new(s.as_bytes());

let expected = [
Expand Down Expand Up @@ -162,6 +162,8 @@ fn check_punctuators() {
TokenKind::Punctuator(Punctuator::AssignOr),
TokenKind::Punctuator(Punctuator::AssignXor),
TokenKind::Punctuator(Punctuator::Arrow),
TokenKind::Punctuator(Punctuator::Coalesce),
TokenKind::Punctuator(Punctuator::AssignCoalesce),
];

expect_tokens(&mut lexer, &expected);
Expand Down
5 changes: 2 additions & 3 deletions boa/src/syntax/parser/expression/assignment/conditional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
syntax::{
ast::{node::ConditionalOp, Node, Punctuator},
parser::{
expression::{AssignmentExpression, LogicalORExpression},
expression::{AssignmentExpression, ShortCircuitExpression},
AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser,
},
},
Expand Down Expand Up @@ -65,8 +65,7 @@ where
fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("ConditionalExpression", "Parsing");

// TODO: coalesce expression
let lhs = LogicalORExpression::new(self.allow_in, self.allow_yield, self.allow_await)
let lhs = ShortCircuitExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor)?;

if let Some(tok) = cursor.peek(0)? {
Expand Down
Loading

0 comments on commit 8a7ac4e

Please sign in to comment.