Browse Source

Fix unreachable panics in compile_access (#1861)

This PR changes the following:

- More elegantly handles illegal access statements in compile_access
- Adds a slew of previously unhandled illegal access test cases

### Caveats

It is very, very likely that you will want to simply restrict unary and assignment operations in the AST. However, this prevents crashes in the meantime with a error that is just slightly less detailed than if it were implemented in AST.
pull/1904/head
Addison Crump 3 years ago
parent
commit
7fa37b50bc
  1. 65
      boa_engine/src/bytecompiler.rs
  2. 45
      boa_engine/src/tests.rs

65
boa_engine/src/bytecompiler.rs

@ -456,13 +456,13 @@ impl<'b> ByteCompiler<'b> {
} }
#[inline] #[inline]
fn compile_access(node: &'_ Node) -> Access<'_> { fn compile_access(node: &Node) -> Option<Access<'_>> {
match node { match node {
Node::Identifier(name) => Access::Variable { name: name.sym() }, Node::Identifier(name) => Some(Access::Variable { name: name.sym() }),
Node::GetConstField(node) => Access::ByName { node }, Node::GetConstField(node) => Some(Access::ByName { node }),
Node::GetField(node) => Access::ByValue { node }, Node::GetField(node) => Some(Access::ByValue { node }),
Node::This => Access::This, Node::This => Some(Access::This),
_ => unreachable!(), _ => None,
} }
} }
@ -571,7 +571,10 @@ impl<'b> ByteCompiler<'b> {
self.compile_expr(unary.target(), true)?; self.compile_expr(unary.target(), true)?;
self.emit(Opcode::Inc, &[]); self.emit(Opcode::Inc, &[]);
let access = Self::compile_access(unary.target()); let access = Self::compile_access(unary.target()).ok_or_else(|| {
self.context
.construct_syntax_error("Invalid increment operand")
})?;
self.access_set(access, None, true)?; self.access_set(access, None, true)?;
None None
} }
@ -579,7 +582,10 @@ impl<'b> ByteCompiler<'b> {
self.compile_expr(unary.target(), true)?; self.compile_expr(unary.target(), true)?;
self.emit(Opcode::Dec, &[]); self.emit(Opcode::Dec, &[]);
let access = Self::compile_access(unary.target()); let access = Self::compile_access(unary.target()).ok_or_else(|| {
self.context
.construct_syntax_error("Invalid decrement operand")
})?;
self.access_set(access, None, true)?; self.access_set(access, None, true)?;
None None
} }
@ -587,7 +593,11 @@ impl<'b> ByteCompiler<'b> {
self.compile_expr(unary.target(), true)?; self.compile_expr(unary.target(), true)?;
self.emit(Opcode::Dup, &[]); self.emit(Opcode::Dup, &[]);
self.emit(Opcode::Inc, &[]); self.emit(Opcode::Inc, &[]);
let access = Self::compile_access(unary.target());
let access = Self::compile_access(unary.target()).ok_or_else(|| {
self.context
.construct_syntax_error("Invalid increment operand")
})?;
self.access_set(access, None, false)?; self.access_set(access, None, false)?;
None None
@ -596,7 +606,11 @@ impl<'b> ByteCompiler<'b> {
self.compile_expr(unary.target(), true)?; self.compile_expr(unary.target(), true)?;
self.emit(Opcode::Dup, &[]); self.emit(Opcode::Dup, &[]);
self.emit(Opcode::Dec, &[]); self.emit(Opcode::Dec, &[]);
let access = Self::compile_access(unary.target());
let access = Self::compile_access(unary.target()).ok_or_else(|| {
self.context
.construct_syntax_error("Invalid decrement operand")
})?;
self.access_set(access, None, false)?; self.access_set(access, None, false)?;
None None
@ -744,7 +758,12 @@ impl<'b> ByteCompiler<'b> {
AssignOp::BoolAnd => { AssignOp::BoolAnd => {
let exit = self.jump_with_custom_opcode(Opcode::LogicalAnd); let exit = self.jump_with_custom_opcode(Opcode::LogicalAnd);
self.compile_expr(binary.rhs(), true)?; self.compile_expr(binary.rhs(), true)?;
let access = Self::compile_access(binary.lhs()); let access =
Self::compile_access(binary.lhs()).ok_or_else(|| {
self.context.construct_syntax_error(
"Invalid left-hand side in assignment",
)
})?;
self.access_set(access, None, use_expr)?; self.access_set(access, None, use_expr)?;
self.patch_jump(exit); self.patch_jump(exit);
None None
@ -752,7 +771,12 @@ impl<'b> ByteCompiler<'b> {
AssignOp::BoolOr => { AssignOp::BoolOr => {
let exit = self.jump_with_custom_opcode(Opcode::LogicalOr); let exit = self.jump_with_custom_opcode(Opcode::LogicalOr);
self.compile_expr(binary.rhs(), true)?; self.compile_expr(binary.rhs(), true)?;
let access = Self::compile_access(binary.lhs()); let access =
Self::compile_access(binary.lhs()).ok_or_else(|| {
self.context.construct_syntax_error(
"Invalid left-hand side in assignment",
)
})?;
self.access_set(access, None, use_expr)?; self.access_set(access, None, use_expr)?;
self.patch_jump(exit); self.patch_jump(exit);
None None
@ -760,7 +784,12 @@ impl<'b> ByteCompiler<'b> {
AssignOp::Coalesce => { AssignOp::Coalesce => {
let exit = self.jump_with_custom_opcode(Opcode::Coalesce); let exit = self.jump_with_custom_opcode(Opcode::Coalesce);
self.compile_expr(binary.rhs(), true)?; self.compile_expr(binary.rhs(), true)?;
let access = Self::compile_access(binary.lhs()); let access =
Self::compile_access(binary.lhs()).ok_or_else(|| {
self.context.construct_syntax_error(
"Invalid left-hand side in assignment",
)
})?;
self.access_set(access, None, use_expr)?; self.access_set(access, None, use_expr)?;
self.patch_jump(exit); self.patch_jump(exit);
None None
@ -770,7 +799,10 @@ impl<'b> ByteCompiler<'b> {
if let Some(opcode) = opcode { if let Some(opcode) = opcode {
self.compile_expr(binary.rhs(), true)?; self.compile_expr(binary.rhs(), true)?;
self.emit(opcode, &[]); self.emit(opcode, &[]);
let access = Self::compile_access(binary.lhs()); let access = Self::compile_access(binary.lhs()).ok_or_else(|| {
self.context
.construct_syntax_error("Invalid left-hand side in assignment")
})?;
self.access_set(access, None, use_expr)?; self.access_set(access, None, use_expr)?;
} }
} }
@ -903,7 +935,10 @@ impl<'b> ByteCompiler<'b> {
if let Node::Object(_) = assign.lhs() { if let Node::Object(_) = assign.lhs() {
self.emit_opcode(Opcode::PushUndefined); self.emit_opcode(Opcode::PushUndefined);
} else { } else {
let access = Self::compile_access(assign.lhs()); let access = Self::compile_access(assign.lhs()).ok_or_else(|| {
self.context
.construct_syntax_error("Invalid left-hand side in assignment")
})?;
self.access_set(access, Some(assign.rhs()), use_expr)?; self.access_set(access, Some(assign.rhs()), use_expr)?;
} }
} }

45
boa_engine/src/tests.rs

@ -485,6 +485,16 @@ fn unary_pre() {
assert_eq!(&exec(execs_before_dec), "true"); assert_eq!(&exec(execs_before_dec), "true");
} }
#[test]
fn invalid_unary_access() {
check_output(&[
TestAction::TestStartsWith("++[];", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("[]++;", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("--[];", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("[]--;", "Uncaught \"SyntaxError\": "),
]);
}
#[test] #[test]
fn typeof_string() { fn typeof_string() {
let typeof_string = r#" let typeof_string = r#"
@ -690,6 +700,7 @@ fn unary_delete() {
mod in_operator { mod in_operator {
use super::*; use super::*;
use crate::forward_val; use crate::forward_val;
#[test] #[test]
fn propery_in_object() { fn propery_in_object() {
let p_in_o = r#" let p_in_o = r#"
@ -1335,6 +1346,21 @@ fn assignment_to_non_assignable() {
} }
} }
#[test]
fn assignment_to_non_assignable_ctd() {
check_output(&[
TestAction::TestStartsWith("(()=>{})() -= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() *= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() /= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() %= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() &= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() ^= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() |= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() += 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() = 5", "Uncaught \"SyntaxError\": "),
]);
}
#[test] #[test]
fn multicharacter_assignment_to_non_assignable() { fn multicharacter_assignment_to_non_assignable() {
// Relates to the behaviour described at // Relates to the behaviour described at
@ -1351,6 +1377,15 @@ fn multicharacter_assignment_to_non_assignable() {
} }
} }
#[test]
fn multicharacter_assignment_to_non_assignable_ctd() {
check_output(&[
TestAction::TestStartsWith("(()=>{})() **= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() <<= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() >>= 5", "Uncaught \"SyntaxError\": "),
]);
}
#[test] #[test]
fn multicharacter_bitwise_assignment_to_non_assignable() { fn multicharacter_bitwise_assignment_to_non_assignable() {
let mut context = Context::default(); let mut context = Context::default();
@ -1366,6 +1401,16 @@ fn multicharacter_bitwise_assignment_to_non_assignable() {
} }
} }
#[test]
fn multicharacter_bitwise_assignment_to_non_assignable_ctd() {
check_output(&[
TestAction::TestStartsWith("(()=>{})() >>>= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() &&= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() ||= 5", "Uncaught \"SyntaxError\": "),
TestAction::TestStartsWith("(()=>{})() ??= 5", "Uncaught \"SyntaxError\": "),
]);
}
#[test] #[test]
fn assign_to_array_decl() { fn assign_to_array_decl() {
check_output(&[ check_output(&[

Loading…
Cancel
Save