Browse Source

Fix base objects in `with` statements (#3870)

pull/3873/head
raskad 5 months ago committed by GitHub
parent
commit
4419e6df04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      core/engine/src/builtins/eval/mod.rs
  2. 2
      core/engine/src/builtins/function/mod.rs
  3. 2
      core/engine/src/builtins/json/mod.rs
  4. 5
      core/engine/src/bytecompiler/class.rs
  5. 2
      core/engine/src/bytecompiler/declarations.rs
  6. 9
      core/engine/src/bytecompiler/function.rs
  7. 19
      core/engine/src/bytecompiler/mod.rs
  8. 3
      core/engine/src/bytecompiler/statement/with.rs
  9. 51
      core/engine/src/environments/runtime/mod.rs
  10. 1
      core/engine/src/module/source.rs
  11. 1
      core/engine/src/module/synthetic.rs
  12. 1
      core/engine/src/script.rs
  13. 4
      core/engine/src/vm/code_block.rs
  14. 4
      core/engine/src/vm/flowgraph/mod.rs
  15. 39
      core/engine/src/vm/opcode/environment/mod.rs
  16. 9
      core/engine/src/vm/opcode/mod.rs

3
core/engine/src/builtins/eval/mod.rs

@ -246,6 +246,8 @@ impl Eval {
context, context,
)?; )?;
let in_with = context.vm.environments.has_object_environment();
let mut compiler = ByteCompiler::new( let mut compiler = ByteCompiler::new(
js_string!("<main>"), js_string!("<main>"),
body.strict(), body.strict(),
@ -253,6 +255,7 @@ impl Eval {
var_env.clone(), var_env.clone(),
lex_env.clone(), lex_env.clone(),
context.interner_mut(), context.interner_mut(),
in_with,
); );
compiler.current_open_environments_count += 1; compiler.current_open_environments_count += 1;

2
core/engine/src/builtins/function/mod.rs

@ -626,10 +626,12 @@ impl BuiltInFunctionObject {
body body
}; };
let in_with = context.vm.environments.has_object_environment();
let code = FunctionCompiler::new() let code = FunctionCompiler::new()
.name(js_string!("anonymous")) .name(js_string!("anonymous"))
.generator(generator) .generator(generator)
.r#async(r#async) .r#async(r#async)
.in_with(in_with)
.compile( .compile(
&parameters, &parameters,
&body, &body,

2
core/engine/src/builtins/json/mod.rs

@ -113,6 +113,7 @@ impl Json {
parser.set_json_parse(); parser.set_json_parse();
let script = parser.parse_script(context.interner_mut())?; let script = parser.parse_script(context.interner_mut())?;
let code_block = { let code_block = {
let in_with = context.vm.environments.has_object_environment();
let mut compiler = ByteCompiler::new( let mut compiler = ByteCompiler::new(
js_string!("<main>"), js_string!("<main>"),
script.strict(), script.strict(),
@ -120,6 +121,7 @@ impl Json {
context.realm().environment().compile_env(), context.realm().environment().compile_env(),
context.realm().environment().compile_env(), context.realm().environment().compile_env(),
context.interner_mut(), context.interner_mut(),
in_with,
); );
compiler.compile_statement_list(script.statements(), true, false); compiler.compile_statement_list(script.statements(), true, false);
Gc::new(compiler.finish()) Gc::new(compiler.finish())

5
core/engine/src/bytecompiler/class.rs

@ -55,6 +55,7 @@ impl ByteCompiler<'_> {
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.interner, self.interner,
self.in_with,
); );
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
@ -288,6 +289,7 @@ impl ByteCompiler<'_> {
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.interner, self.interner,
self.in_with,
); );
// Function environment // Function environment
@ -316,6 +318,7 @@ impl ByteCompiler<'_> {
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.interner, self.interner,
self.in_with,
); );
let _ = field_compiler.push_compile_environment(true); let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field { if let Some(node) = field {
@ -354,6 +357,7 @@ impl ByteCompiler<'_> {
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.interner, self.interner,
self.in_with,
); );
let _ = field_compiler.push_compile_environment(true); let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field { if let Some(node) = field {
@ -388,6 +392,7 @@ impl ByteCompiler<'_> {
self.variable_environment.clone(), self.variable_environment.clone(),
self.lexical_environment.clone(), self.lexical_environment.clone(),
self.interner, self.interner,
self.in_with,
); );
let _ = compiler.push_compile_environment(true); let _ = compiler.push_compile_environment(true);

2
core/engine/src/bytecompiler/declarations.rs

@ -586,6 +586,7 @@ impl ByteCompiler<'_> {
.generator(generator) .generator(generator)
.r#async(r#async) .r#async(r#async)
.strict(self.strict()) .strict(self.strict())
.in_with(self.in_with)
.binding_identifier(Some(name.sym().to_js_string(self.interner()))) .binding_identifier(Some(name.sym().to_js_string(self.interner())))
.compile( .compile(
parameters, parameters,
@ -954,6 +955,7 @@ impl ByteCompiler<'_> {
.generator(generator) .generator(generator)
.r#async(r#async) .r#async(r#async)
.strict(self.strict()) .strict(self.strict())
.in_with(self.in_with)
.binding_identifier(Some(name.sym().to_js_string(self.interner()))) .binding_identifier(Some(name.sym().to_js_string(self.interner())))
.compile( .compile(
parameters, parameters,

9
core/engine/src/bytecompiler/function.rs

@ -22,6 +22,7 @@ pub(crate) struct FunctionCompiler {
strict: bool, strict: bool,
arrow: bool, arrow: bool,
method: bool, method: bool,
in_with: bool,
binding_identifier: Option<JsString>, binding_identifier: Option<JsString>,
} }
@ -35,6 +36,7 @@ impl FunctionCompiler {
strict: false, strict: false,
arrow: false, arrow: false,
method: false, method: false,
in_with: false,
binding_identifier: None, binding_identifier: None,
} }
} }
@ -85,6 +87,12 @@ impl FunctionCompiler {
self self
} }
/// Indicate if the function is in a `with` statement.
pub(crate) const fn in_with(mut self, in_with: bool) -> Self {
self.in_with = in_with;
self
}
/// Compile a function statement list and it's parameters into bytecode. /// Compile a function statement list and it's parameters into bytecode.
pub(crate) fn compile( pub(crate) fn compile(
mut self, mut self,
@ -105,6 +113,7 @@ impl FunctionCompiler {
variable_environment, variable_environment,
lexical_environment, lexical_environment,
interner, interner,
self.in_with,
); );
compiler.length = length; compiler.length = length;
compiler compiler

19
core/engine/src/bytecompiler/mod.rs

@ -304,6 +304,9 @@ pub struct ByteCompiler<'ctx> {
pub(crate) async_handler: Option<u32>, pub(crate) async_handler: Option<u32>,
json_parse: bool, json_parse: bool,
/// Whether the function is in a `with` statement.
pub(crate) in_with: bool,
pub(crate) interner: &'ctx mut Interner, pub(crate) interner: &'ctx mut Interner,
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
@ -324,6 +327,7 @@ impl<'ctx> ByteCompiler<'ctx> {
variable_environment: Rc<CompileTimeEnvironment>, variable_environment: Rc<CompileTimeEnvironment>,
lexical_environment: Rc<CompileTimeEnvironment>, lexical_environment: Rc<CompileTimeEnvironment>,
interner: &'ctx mut Interner, interner: &'ctx mut Interner,
in_with: bool,
) -> ByteCompiler<'ctx> { ) -> ByteCompiler<'ctx> {
let mut code_block_flags = CodeBlockFlags::empty(); let mut code_block_flags = CodeBlockFlags::empty();
code_block_flags.set(CodeBlockFlags::STRICT, strict); code_block_flags.set(CodeBlockFlags::STRICT, strict);
@ -356,6 +360,7 @@ impl<'ctx> ByteCompiler<'ctx> {
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]
annex_b_function_names: Vec::new(), annex_b_function_names: Vec::new(),
in_with,
} }
} }
@ -1320,6 +1325,7 @@ impl<'ctx> ByteCompiler<'ctx> {
.r#async(r#async) .r#async(r#async)
.strict(self.strict()) .strict(self.strict())
.arrow(arrow) .arrow(arrow)
.in_with(self.in_with)
.binding_identifier(binding_identifier) .binding_identifier(binding_identifier)
.compile( .compile(
parameters, parameters,
@ -1395,6 +1401,7 @@ impl<'ctx> ByteCompiler<'ctx> {
.strict(self.strict()) .strict(self.strict())
.arrow(arrow) .arrow(arrow)
.method(true) .method(true)
.in_with(self.in_with)
.binding_identifier(binding_identifier) .binding_identifier(binding_identifier)
.compile( .compile(
parameters, parameters,
@ -1442,6 +1449,7 @@ impl<'ctx> ByteCompiler<'ctx> {
.strict(true) .strict(true)
.arrow(arrow) .arrow(arrow)
.method(true) .method(true)
.in_with(self.in_with)
.binding_identifier(binding_identifier) .binding_identifier(binding_identifier)
.compile( .compile(
parameters, parameters,
@ -1481,8 +1489,19 @@ impl<'ctx> ByteCompiler<'ctx> {
if *ident == Sym::EVAL { if *ident == Sym::EVAL {
kind = CallKind::CallEval; kind = CallKind::CallEval;
} }
if self.in_with {
let name = self.resolve_identifier_expect(*ident);
let binding = self.lexical_environment.get_identifier_reference(name);
let index = self.get_or_insert_binding(binding.locator());
self.emit_with_varying_operand(Opcode::ThisForObjectEnvironmentName, index);
} else {
self.emit_opcode(Opcode::PushUndefined);
} }
} else {
self.emit_opcode(Opcode::PushUndefined); self.emit_opcode(Opcode::PushUndefined);
}
self.compile_expr(expr, true); self.compile_expr(expr, true);
} }
expr => { expr => {

3
core/engine/src/bytecompiler/statement/with.rs

@ -10,7 +10,10 @@ impl ByteCompiler<'_> {
let _ = self.push_compile_environment(false); let _ = self.push_compile_environment(false);
self.emit_opcode(Opcode::PushObjectEnvironment); self.emit_opcode(Opcode::PushObjectEnvironment);
let in_with = self.in_with;
self.in_with = true;
self.compile_stmt(with.statement(), use_expr, true); self.compile_stmt(with.statement(), use_expr, true);
self.in_with = in_with;
self.pop_compile_environment(); self.pop_compile_environment();
self.lexical_environment = old_lex_env; self.lexical_environment = old_lex_env;

51
core/engine/src/environments/runtime/mod.rs

@ -411,6 +411,13 @@ impl EnvironmentStack {
} }
names names
} }
/// Indicate if the current environment stack has an object environment.
pub(crate) fn has_object_environment(&self) -> bool {
self.stack
.iter()
.any(|env| matches!(env, Environment::Object(_)))
}
} }
/// A binding locator contains all information about a binding that is needed to resolve it at runtime. /// A binding locator contains all information about a binding that is needed to resolve it at runtime.
@ -541,6 +548,50 @@ impl Context {
Ok(()) Ok(())
} }
/// Finds the object environment that contains the binding and returns the `this` value of the object environment.
pub(crate) fn this_from_object_environment_binding(
&mut self,
locator: &BindingLocator,
) -> JsResult<Option<JsObject>> {
let current = self.vm.environments.current();
if let Some(env) = current.as_declarative() {
if !env.with() {
return Ok(None);
}
}
for env_index in (locator.environment_index..self.vm.environments.stack.len() as u32).rev()
{
match self.environment_expect(env_index) {
Environment::Declarative(env) => {
if env.poisoned() {
let compile = env.compile_env();
if compile.is_function() && compile.get_binding(locator.name()).is_some() {
break;
}
} else if !env.with() {
break;
}
}
Environment::Object(o) => {
let o = o.clone();
let key = locator.name().clone();
if o.has_property(key.clone(), self)? {
if let Some(unscopables) = o.get(JsSymbol::unscopables(), self)?.as_object()
{
if unscopables.get(key.clone(), self)?.to_boolean() {
continue;
}
}
return Ok(Some(o));
}
}
}
}
Ok(None)
}
/// Checks if the binding pointed by `locator` is initialized. /// Checks if the binding pointed by `locator` is initialized.
/// ///
/// # Panics /// # Panics

1
core/engine/src/module/source.rs

@ -1433,6 +1433,7 @@ impl SourceTextModule {
env.clone(), env.clone(),
env.clone(), env.clone(),
context.interner_mut(), context.interner_mut(),
false,
); );
compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC; compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC;

1
core/engine/src/module/synthetic.rs

@ -287,6 +287,7 @@ impl SyntheticModule {
module_compile_env.clone(), module_compile_env.clone(),
module_compile_env.clone(), module_compile_env.clone(),
context.interner_mut(), context.interner_mut(),
false,
); );
// 4. For each String exportName in module.[[ExportNames]], do // 4. For each String exportName in module.[[ExportNames]], do

1
core/engine/src/script.rs

@ -135,6 +135,7 @@ impl Script {
self.inner.realm.environment().compile_env(), self.inner.realm.environment().compile_env(),
self.inner.realm.environment().compile_env(), self.inner.realm.environment().compile_env(),
context.interner_mut(), context.interner_mut(),
false,
); );
#[cfg(feature = "annex-b")] #[cfg(feature = "annex-b")]

4
core/engine/src/vm/code_block.rs

@ -611,6 +611,7 @@ impl CodeBlock {
| Instruction::Exception | Instruction::Exception
| Instruction::MaybeException | Instruction::MaybeException
| Instruction::This | Instruction::This
| Instruction::ThisForObjectEnvironmentName { .. }
| Instruction::Super | Instruction::Super
| Instruction::CheckReturn | Instruction::CheckReturn
| Instruction::Return | Instruction::Return
@ -719,8 +720,7 @@ impl CodeBlock {
| Instruction::Reserved50 | Instruction::Reserved50
| Instruction::Reserved51 | Instruction::Reserved51
| Instruction::Reserved52 | Instruction::Reserved52
| Instruction::Reserved53 | Instruction::Reserved53 => unreachable!("Reserved opcodes are unrechable"),
| Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"),
} }
} }
} }

4
core/engine/src/vm/flowgraph/mod.rs

@ -400,6 +400,7 @@ impl CodeBlock {
| Instruction::ToPropertyKey | Instruction::ToPropertyKey
| Instruction::ToBoolean | Instruction::ToBoolean
| Instruction::This | Instruction::This
| Instruction::ThisForObjectEnvironmentName { .. }
| Instruction::Super | Instruction::Super
| Instruction::IncrementLoopIteration | Instruction::IncrementLoopIteration
| Instruction::CreateForInIterator | Instruction::CreateForInIterator
@ -515,8 +516,7 @@ impl CodeBlock {
| Instruction::Reserved50 | Instruction::Reserved50
| Instruction::Reserved51 | Instruction::Reserved51
| Instruction::Reserved52 | Instruction::Reserved52
| Instruction::Reserved53 | Instruction::Reserved53 => unreachable!("Reserved opcodes are unrechable"),
| Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"),
} }
} }

39
core/engine/src/vm/opcode/environment/mod.rs

@ -35,6 +35,45 @@ impl Operation for This {
} }
} }
/// `ThisForObjectEnvironmentName` implements the Opcode Operation for `Opcode::ThisForObjectEnvironmentName`
///
/// Operation:
/// - Pushes `this` value that is related to the object environment of the given binding.
#[derive(Debug, Clone, Copy)]
pub(crate) struct ThisForObjectEnvironmentName;
impl ThisForObjectEnvironmentName {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let binding_locator = context.vm.frame().code_block.bindings[index].clone();
let this = context
.this_from_object_environment_binding(&binding_locator)?
.map_or(JsValue::undefined(), Into::into);
context.vm.push(this);
Ok(CompletionType::Normal)
}
}
impl Operation for ThisForObjectEnvironmentName {
const NAME: &'static str = "ThisForObjectEnvironmentName";
const INSTRUCTION: &'static str = "INST - ThisForObjectEnvironmentName";
const COST: u8 = 1;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>();
Self::operation(context, index as usize)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>();
Self::operation(context, index as usize)
}
}
/// `Super` implements the Opcode Operation for `Opcode::Super` /// `Super` implements the Opcode Operation for `Opcode::Super`
/// ///
/// Operation: /// Operation:

9
core/engine/src/vm/opcode/mod.rs

@ -1614,6 +1614,13 @@ generate_opcodes! {
/// Stack: **=>** this /// Stack: **=>** this
This, This,
/// Pushes `this` value that is related to the object environment of the given binding
///
/// Operands: index: `VaryingOperand`
///
/// Stack: **=>** value
ThisForObjectEnvironmentName { index: VaryingOperand },
/// Pushes the current `super` value to the stack. /// Pushes the current `super` value to the stack.
/// ///
/// Operands: /// Operands:
@ -2252,8 +2259,6 @@ generate_opcodes! {
Reserved52 => Reserved, Reserved52 => Reserved,
/// Reserved [`Opcode`]. /// Reserved [`Opcode`].
Reserved53 => Reserved, Reserved53 => Reserved,
/// Reserved [`Opcode`].
Reserved54 => Reserved,
} }
/// Specific opcodes for bindings. /// Specific opcodes for bindings.

Loading…
Cancel
Save