From a53feb98f4afb8caefbf2e72bc54aa633cdf4b4b Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 7 Sep 2024 14:43:01 +0300 Subject: [PATCH 1/4] feat(ecmascript): ClassStaticBlocks --- .../scope_analysis.rs | 28 +++- .../executable/class_definition_evaluation.rs | 122 +++++++++++++++++- 2 files changed, 146 insertions(+), 4 deletions(-) diff --git a/nova_vm/src/ecmascript/syntax_directed_operations/scope_analysis.rs b/nova_vm/src/ecmascript/syntax_directed_operations/scope_analysis.rs index 285a949d..cd7619e0 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/scope_analysis.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/scope_analysis.rs @@ -265,12 +265,22 @@ pub(crate) fn function_body_lexically_scoped_declarations<'body>( lexically_scoped_declarations } -pub(crate) fn class_static_block_lexically_scoped_declarations() { +pub(crate) fn class_static_block_lexically_scoped_declarations<'body>( + static_block: &'body StaticBlock<'body>, +) -> Vec> { + let mut lexically_scoped_declarations = vec![]; // ClassStaticBlockStatementList : [empty] // 1. Return a new empty List. // ClassStaticBlockStatementList : StatementList // 1. Return the TopLevelLexicallyScopedDeclarations of StatementList. + static_block + .body + .top_level_lexically_scoped_declarations(&mut |decl| { + lexically_scoped_declarations.push(decl); + }); + + lexically_scoped_declarations } pub(crate) fn script_lexically_scoped_declarations<'body>( @@ -811,6 +821,22 @@ pub(crate) fn function_body_var_scoped_declarations<'a>( // AsyncConciseBody : ExpressionBody // 1. Return a new empty List. +pub(crate) fn class_static_block_var_scoped_declarations<'a>( + static_block: &'a StaticBlock<'a>, +) -> Vec> { + let mut var_scoped_declarations = vec![]; + // ClassStaticBlockStatementList : [empty] + // 1. Return a new empty List. + // ClassStaticBlockStatementList : StatementList + // 1. Return the TopLevelVarScopedDeclarations of StatementList. + static_block + .body + .top_level_var_scoped_declarations(&mut |declarator| { + var_scoped_declarations.push(declarator); + }); + var_scoped_declarations +} + #[derive(Debug, Clone, Copy)] pub(crate) enum VarScopedDeclaration<'a> { Variable(&'a VariableDeclarator<'a>), diff --git a/nova_vm/src/engine/bytecode/executable/class_definition_evaluation.rs b/nova_vm/src/engine/bytecode/executable/class_definition_evaluation.rs index 3ed9d282..78f77678 100644 --- a/nova_vm/src/engine/bytecode/executable/class_definition_evaluation.rs +++ b/nova_vm/src/engine/bytecode/executable/class_definition_evaluation.rs @@ -5,6 +5,11 @@ use crate::{ ecmascript::{ execution::agent::ExceptionType, + syntax_directed_operations::scope_analysis::{ + class_static_block_lexically_scoped_declarations, + class_static_block_var_declared_names, class_static_block_var_scoped_declarations, + LexicallyScopedDeclaration, VarScopedDeclaration, + }, types::{String, Value, BUILTIN_STRING_MEMORY}, }, engine::{ @@ -12,9 +17,10 @@ use crate::{ NamedEvaluationParameter, SendableRef, }, }; +use ahash::{AHashMap, AHashSet}; use oxc_ast::{ ast::{self, MethodDefinitionKind}, - syntax_directed_operations::{PrivateBoundIdentifiers, PropName}, + syntax_directed_operations::{BoundNames, PrivateBoundIdentifiers, PropName}, }; impl CompileEvaluation for ast::Class<'_> { @@ -413,16 +419,16 @@ impl CompileEvaluation for ast::Class<'_> { // 30. For each PrivateElement method of staticPrivateMethods, do // a. Perform ! PrivateMethodOrAccessorAdd(F, method). // 31. For each element elementRecord of staticElements, do - for _element_record in static_elements.iter() { + for element_record in static_elements.iter() { // a. If elementRecord is a ClassFieldDefinition Record, then // i. Let result be Completion(DefineField(F, elementRecord)). // b. Else, // i. Assert: elementRecord is a ClassStaticBlockDefinition Record. // ii. Let result be Completion(Call(elementRecord.[[BodyFunction]], F)). + element_record.compile(ctx); // c. If result is an abrupt completion, then // i. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment. // ii. Return ? result. - todo!(); } // 32. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment. // 33. Return F. @@ -556,3 +562,113 @@ fn define_method(class_element: &ast::MethodDefinition, ctx: &mut CompileContext // 9. Return the Record { [[Key]]: propKey, [[Closure]]: closure }. } + +impl CompileEvaluation for ast::StaticBlock<'_> { + fn compile(&self, ctx: &mut CompileContext) { + // 12. Let functionNames be a new empty List. + // 13. Let functionsToInitialize be a new empty List. + // NOTE: the keys of `functions` will be `functionNames`, its values will be + // `functionsToInitialize`. + let mut functions = AHashMap::new(); + for d in class_static_block_var_scoped_declarations(self) { + // a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then + if let VarScopedDeclaration::Function(d) = d { + // i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration. + // ii. Let fn be the sole element of the BoundNames of d. + let f_name = d.id.as_ref().unwrap().name.clone(); + // iii. If functionNames does not contain fn, then + // 1. Insert fn as the first element of functionNames. + // 2. NOTE: If there are multiple function declarations for the same name, the last declaration is used. + // 3. Insert d as the first element of functionsToInitialize. + functions.insert(f_name, d); + } + } + + // 27. If hasParameterExpressions is false, then + // a. NOTE: Only a single Environment Record is needed for the parameters and top-level vars. + // b. Let instantiatedVarNames be a copy of the List parameterBindings. + let mut instantiated_var_names = AHashSet::new(); + // c. For each element n of varNames, do + for n in class_static_block_var_declared_names(self) { + // i. If instantiatedVarNames does not contain n, then + if instantiated_var_names.contains(&n) { + continue; + } + // 1. Append n to instantiatedVarNames. + let n_string = String::from_str(ctx.agent, &n); + instantiated_var_names.insert(n); + // 2. Perform ! env.CreateMutableBinding(n, false). + ctx.exe + .add_instruction_with_identifier(Instruction::CreateMutableBinding, n_string); + // 3. Perform ! env.InitializeBinding(n, undefined). + ctx.exe + .add_instruction_with_identifier(Instruction::ResolveBinding, n_string); + ctx.exe + .add_instruction_with_constant(Instruction::StoreConstant, Value::Undefined); + ctx.exe + .add_instruction(Instruction::InitializeReferencedBinding); + } + + // 34. For each element d of lexDeclarations, do + for d in class_static_block_lexically_scoped_declarations(self) { + // a. NOTE: A lexically declared name cannot be the same as a function/generator declaration, formal parameter, or a var name. Lexically declared names are only instantiated here but not initialized. + // b. For each element dn of the BoundNames of d, do + match d { + // i. If IsConstantDeclaration of d is true, then + LexicallyScopedDeclaration::Variable(decl) if decl.kind.is_const() => { + { + decl.id.bound_names(&mut |identifier| { + let dn = String::from_str(ctx.agent, &identifier.name); + // 1. Perform ! lexEnv.CreateImmutableBinding(dn, true). + ctx.exe.add_instruction_with_identifier( + Instruction::CreateImmutableBinding, + dn, + ); + }) + } + } + // ii. Else, + // 1. Perform ! lexEnv.CreateMutableBinding(dn, false). + LexicallyScopedDeclaration::Variable(decl) => { + decl.id.bound_names(&mut |identifier| { + let dn = String::from_str(ctx.agent, &identifier.name); + ctx.exe + .add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); + }) + } + LexicallyScopedDeclaration::Function(decl) => { + let dn = String::from_str(ctx.agent, &decl.id.as_ref().unwrap().name); + ctx.exe + .add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); + } + LexicallyScopedDeclaration::Class(decl) => { + let dn = String::from_str(ctx.agent, &decl.id.as_ref().unwrap().name); + ctx.exe + .add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); + } + LexicallyScopedDeclaration::DefaultExport => { + let dn = BUILTIN_STRING_MEMORY._default_; + ctx.exe + .add_instruction_with_identifier(Instruction::CreateMutableBinding, dn); + } + } + } + + // 36. For each Parse Node f of functionsToInitialize, do + for f in functions.values() { + // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. + f.compile(ctx); + // a. Let fn be the sole element of the BoundNames of f. + let f_name = String::from_str(ctx.agent, &f.id.as_ref().unwrap().name); + // c. Perform ! varEnv.SetMutableBinding(fn, fo, false). + // TODO: This compilation is incorrect if !strict, when varEnv != lexEnv. + ctx.exe + .add_instruction_with_identifier(Instruction::ResolveBinding, f_name); + ctx.exe.add_instruction(Instruction::PutValue); + } + + for statement in self.body.iter() { + statement.compile(ctx); + } + } +} From b28abbce10e028fc75c5d1cc661fc1e6d22d4769 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 7 Sep 2024 14:43:05 +0300 Subject: [PATCH 2/4] chore(test262): Update expectations --- tests/expectations.json | 23 ----------------------- tests/metrics.json | 6 +++--- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/tests/expectations.json b/tests/expectations.json index 5ce6bb5e..aa9997b8 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -16909,7 +16909,6 @@ "language/expressions/class/scope-name-lex-close.js": "CRASH", "language/expressions/class/scope-name-lex-open-heritage.js": "CRASH", "language/expressions/class/scope-name-lex-open-no-heritage.js": "CRASH", - "language/expressions/class/static-init-await-reference.js": "CRASH", "language/expressions/class/static-method-length-dflt.js": "CRASH", "language/expressions/class/subclass-builtins/subclass-AggregateError.js": "CRASH", "language/expressions/class/subclass-builtins/subclass-ArrayBuffer.js": "CRASH", @@ -17726,8 +17725,6 @@ "language/expressions/function/length-dflt.js": "FAIL", "language/expressions/function/scope-param-rest-elem-var-close.js": "CRASH", "language/expressions/function/scope-param-rest-elem-var-open.js": "CRASH", - "language/expressions/function/static-init-await-binding.js": "CRASH", - "language/expressions/function/static-init-await-reference.js": "CRASH", "language/expressions/function/unscopables-with-in-nested-fn.js": "CRASH", "language/expressions/function/unscopables-with.js": "CRASH", "language/expressions/generators/dflt-params-abrupt.js": "CRASH", @@ -17871,8 +17868,6 @@ "language/expressions/generators/length-dflt.js": "FAIL", "language/expressions/generators/scope-param-rest-elem-var-close.js": "CRASH", "language/expressions/generators/scope-param-rest-elem-var-open.js": "CRASH", - "language/expressions/generators/static-init-await-binding.js": "CRASH", - "language/expressions/generators/static-init-await-reference.js": "CRASH", "language/expressions/generators/unscopables-with-in-nested-fn.js": "CRASH", "language/expressions/generators/unscopables-with.js": "CRASH", "language/expressions/generators/yield-star-before-newline.js": "CRASH", @@ -18378,8 +18373,6 @@ "language/expressions/object/fn-name-class.js": "CRASH", "language/expressions/object/fn-name-cover.js": "FAIL", "language/expressions/object/getter-super-prop.js": "CRASH", - "language/expressions/object/ident-name-prop-name-literal-await-static-init.js": "CRASH", - "language/expressions/object/identifier-shorthand-static-init-await-valid.js": "CRASH", "language/expressions/object/literal-property-name-bigint.js": "CRASH", "language/expressions/object/method-definition/async-gen-meth-dflt-params-abrupt.js": "CRASH", "language/expressions/object/method-definition/async-gen-meth-dflt-params-arg-val-not-undefined.js": "CRASH", @@ -18481,12 +18474,6 @@ "language/expressions/object/method-definition/name-prototype-prop.js": "FAIL", "language/expressions/object/method-definition/name-super-prop-body.js": "CRASH", "language/expressions/object/method-definition/name-super-prop-param.js": "CRASH", - "language/expressions/object/method-definition/static-init-await-binding-accessor.js": "CRASH", - "language/expressions/object/method-definition/static-init-await-binding-generator.js": "CRASH", - "language/expressions/object/method-definition/static-init-await-binding-normal.js": "CRASH", - "language/expressions/object/method-definition/static-init-await-reference-accessor.js": "CRASH", - "language/expressions/object/method-definition/static-init-await-reference-generator.js": "CRASH", - "language/expressions/object/method-definition/static-init-await-reference-normal.js": "CRASH", "language/expressions/object/method-definition/yield-star-before-newline.js": "CRASH", "language/expressions/object/method.js": "CRASH", "language/expressions/object/object-spread-proxy-get-not-called-on-dontenum-keys.js": "CRASH", @@ -22321,7 +22308,6 @@ "language/statements/class/static-init-abrupt.js": "CRASH", "language/statements/class/static-init-arguments-functions.js": "CRASH", "language/statements/class/static-init-arguments-methods.js": "CRASH", - "language/statements/class/static-init-await-binding-valid.js": "CRASH", "language/statements/class/static-init-expr-new-target.js": "CRASH", "language/statements/class/static-init-expr-this.js": "CRASH", "language/statements/class/static-init-scope-lex-close.js": "CRASH", @@ -22329,10 +22315,8 @@ "language/statements/class/static-init-scope-lex-open.js": "CRASH", "language/statements/class/static-init-scope-private.js": "CRASH", "language/statements/class/static-init-scope-var-close.js": "CRASH", - "language/statements/class/static-init-scope-var-derived.js": "CRASH", "language/statements/class/static-init-scope-var-open.js": "CRASH", "language/statements/class/static-init-sequence.js": "CRASH", - "language/statements/class/static-init-statement-list-optional.js": "CRASH", "language/statements/class/static-init-super-property.js": "CRASH", "language/statements/class/static-method-length-dflt.js": "CRASH", "language/statements/class/subclass-builtins/subclass-AggregateError.js": "CRASH", @@ -22456,7 +22440,6 @@ "language/statements/const/dstr/obj-ptrn-id-init-fn-name-cover.js": "CRASH", "language/statements/const/fn-name-class.js": "CRASH", "language/statements/const/fn-name-cover.js": "FAIL", - "language/statements/const/static-init-await-binding-valid.js": "CRASH", "language/statements/const/syntax/with-initializer-case-expression-statement-list.js": "CRASH", "language/statements/const/syntax/with-initializer-default-statement-list.js": "CRASH", "language/statements/continue/S12.7_A7.js": "CRASH", @@ -24359,7 +24342,6 @@ "language/statements/function/length-dflt.js": "FAIL", "language/statements/function/scope-param-rest-elem-var-close.js": "CRASH", "language/statements/function/scope-param-rest-elem-var-open.js": "CRASH", - "language/statements/function/static-init-await-binding-valid.js": "CRASH", "language/statements/function/unscopables-with-in-nested-fn.js": "FAIL", "language/statements/function/unscopables-with.js": "FAIL", "language/statements/generators/cptn-decl.js": "CRASH", @@ -24532,7 +24514,6 @@ "language/statements/let/dstr/obj-ptrn-id-init-fn-name-cover.js": "CRASH", "language/statements/let/fn-name-class.js": "CRASH", "language/statements/let/fn-name-cover.js": "FAIL", - "language/statements/let/static-init-await-binding-valid.js": "CRASH", "language/statements/let/syntax/with-initialisers-in-statement-positions-case-expression-statement-list.js": "CRASH", "language/statements/let/syntax/with-initialisers-in-statement-positions-default-statement-list.js": "CRASH", "language/statements/let/syntax/without-initialisers-in-statement-positions-case-expression-statement-list.js": "CRASH", @@ -24692,7 +24673,6 @@ "language/statements/try/scope-catch-block-lex-open.js": "CRASH", "language/statements/try/scope-catch-param-lex-open.js": "CRASH", "language/statements/try/scope-catch-param-var-none.js": "CRASH", - "language/statements/try/static-init-await-binding-valid.js": "CRASH", "language/statements/try/tco-catch-finally.js": "CRASH", "language/statements/try/tco-catch.js": "CRASH", "language/statements/try/tco-finally.js": "CRASH", @@ -24700,13 +24680,10 @@ "language/statements/variable/cptn-value.js": "FAIL", "language/statements/variable/dstr/ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", "language/statements/variable/dstr/ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/statements/variable/dstr/ary-ptrn-elem-id-static-init-await-valid.js": "CRASH", - "language/statements/variable/dstr/obj-ptrn-elem-id-static-init-await-valid.js": "CRASH", "language/statements/variable/dstr/obj-ptrn-id-init-fn-name-class.js": "CRASH", "language/statements/variable/dstr/obj-ptrn-id-init-fn-name-cover.js": "CRASH", "language/statements/variable/fn-name-class.js": "CRASH", "language/statements/variable/fn-name-cover.js": "FAIL", - "language/statements/variable/static-init-await-binding-valid.js": "CRASH", "language/statements/while/S12.6.2_A4_T2.js": "CRASH", "language/statements/while/S12.6.2_A4_T3.js": "CRASH", "language/statements/while/S12.6.2_A4_T4.js": "CRASH", diff --git a/tests/metrics.json b/tests/metrics.json index ce2fb858..f4cbd532 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { - "crash": 17353, - "fail": 7705, - "pass": 19961, + "crash": 17321, + "fail": 7714, + "pass": 19984, "skip": 31, "timeout": 1, "unresolved": 0 From 58c6ddf0757cbae893a8d91fc9b95539ed0eb0f6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 8 Sep 2024 13:19:20 +0300 Subject: [PATCH 3/4] Properly handle this and class name visibility in static blocks --- nova_vm/src/ecmascript/execution.rs | 8 ++-- .../src/ecmascript/execution/environments.rs | 3 +- .../environments/function_environment.rs | 44 ++++++++++++++++++- .../executable/class_definition_evaluation.rs | 22 ++++++++-- nova_vm/src/engine/bytecode/instructions.rs | 9 ++-- nova_vm/src/engine/bytecode/vm.rs | 35 ++++++++++----- 6 files changed, 97 insertions(+), 24 deletions(-) diff --git a/nova_vm/src/ecmascript/execution.rs b/nova_vm/src/ecmascript/execution.rs index a6154a3f..c8370ca0 100644 --- a/nova_vm/src/ecmascript/execution.rs +++ b/nova_vm/src/ecmascript/execution.rs @@ -11,10 +11,10 @@ mod realm; pub use agent::{Agent, JsResult}; pub use default_host_hooks::DefaultHostHooks; pub(crate) use environments::{ - get_this_environment, new_declarative_environment, new_function_environment, - DeclarativeEnvironmentIndex, EnvironmentIndex, Environments, FunctionEnvironmentIndex, - GlobalEnvironment, GlobalEnvironmentIndex, ModuleEnvironmentIndex, ObjectEnvironmentIndex, - PrivateEnvironmentIndex, ThisBindingStatus, + get_this_environment, new_class_static_element_environment, new_declarative_environment, + new_function_environment, DeclarativeEnvironmentIndex, EnvironmentIndex, Environments, + FunctionEnvironmentIndex, GlobalEnvironment, GlobalEnvironmentIndex, ModuleEnvironmentIndex, + ObjectEnvironmentIndex, PrivateEnvironmentIndex, ThisBindingStatus, }; pub(crate) use execution_context::*; #[cfg(test)] diff --git a/nova_vm/src/ecmascript/execution/environments.rs b/nova_vm/src/ecmascript/execution/environments.rs index ca39c487..97088154 100644 --- a/nova_vm/src/ecmascript/execution/environments.rs +++ b/nova_vm/src/ecmascript/execution/environments.rs @@ -35,7 +35,8 @@ mod private_environment; pub(crate) use declarative_environment::{new_declarative_environment, DeclarativeEnvironment}; pub(crate) use function_environment::{ - new_function_environment, FunctionEnvironment, ThisBindingStatus, + new_class_static_element_environment, new_function_environment, FunctionEnvironment, + ThisBindingStatus, }; pub(crate) use global_environment::GlobalEnvironment; pub(crate) use object_environment::ObjectEnvironment; diff --git a/nova_vm/src/ecmascript/execution/environments/function_environment.rs b/nova_vm/src/ecmascript/execution/environments/function_environment.rs index cacd185d..8ef22350 100644 --- a/nova_vm/src/ecmascript/execution/environments/function_environment.rs +++ b/nova_vm/src/ecmascript/execution/environments/function_environment.rs @@ -7,7 +7,7 @@ use crate::{ ecmascript::{ builtins::{ECMAScriptFunction, ThisMode}, execution::{agent::ExceptionType, Agent, JsResult}, - types::{Function, InternalMethods, IntoFunction, Object, String, Value}, + types::{Function, InternalMethods, IntoFunction, IntoValue, Object, String, Value}, }, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, }; @@ -125,6 +125,48 @@ pub(crate) fn new_function_environment( agent.heap.environments.push_function_environment(env) } +/// ### NewClassStaticElementEnvironment ( classConstructor ) +/// +/// This is a non-standard abstract operation that performs the same steps as +/// NewFunctionEnvironment, but for a class static element's evaluation +/// function. These functions are never visible to ECMAScript code and thus we +/// avoid creating them entirely. The only parameter is the class constructor, +/// which is used as both the this value and the \[\[FunctionObject]] of the +/// new function environment. +pub(crate) fn new_class_static_element_environment( + agent: &mut Agent, + class_constructor: Function, +) -> FunctionEnvironmentIndex { + // 1. Let env be a new Function Environment Record containing no bindings. + let dcl_env = DeclarativeEnvironment::new(Some( + agent + .running_execution_context() + .ecmascript_code + .as_ref() + .unwrap() + .lexical_environment, + )); + agent.heap.environments.declarative.push(Some(dcl_env)); + let declarative_environment = + DeclarativeEnvironmentIndex::last(&agent.heap.environments.declarative); + + let env = FunctionEnvironment { + this_value: Some(class_constructor.into_value()), + + function_object: class_constructor, + + this_binding_status: ThisBindingStatus::Initialized, + + // 5. Set env.[[NewTarget]] to newTarget. + new_target: None, + + // 6. Set env.[[OuterEnv]] to F.[[Environment]]. + declarative_environment, + }; + // 7. Return env. + agent.heap.environments.push_function_environment(env) +} + impl FunctionEnvironmentIndex { pub(crate) fn get_this_binding_status(self, agent: &Agent) -> ThisBindingStatus { agent[self].this_binding_status diff --git a/nova_vm/src/engine/bytecode/executable/class_definition_evaluation.rs b/nova_vm/src/engine/bytecode/executable/class_definition_evaluation.rs index 78f77678..16ccb38e 100644 --- a/nova_vm/src/engine/bytecode/executable/class_definition_evaluation.rs +++ b/nova_vm/src/engine/bytecode/executable/class_definition_evaluation.rs @@ -399,8 +399,13 @@ impl CompileEvaluation for ast::Class<'_> { // stack: [constructor] // 26. Set the running execution context's LexicalEnvironment to env. - // 27. If classBinding is not undefined, then + // Note: We do not exit classEnv here. First, classBinding is + // initialized in classEnv. Second, the static elements are "functions" + // that were "created" in the classEnv, and they are "evaluated" below. + // The evaluation is done inline so we need the classEnv to be active, + // and the "function environments" to be created in it. + // 27. If classBinding is not undefined, then // Note: The classBinding needs to be initialized in classEnv, as any // class method calls access the classBinding through the classEnv. if let Some(class_binding) = class_identifier { @@ -412,8 +417,6 @@ impl CompileEvaluation for ast::Class<'_> { .add_instruction(Instruction::InitializeReferencedBinding); } - ctx.exe - .add_instruction(Instruction::ExitDeclarativeEnvironment); // 28. Set F.[[PrivateMethods]] to instancePrivateMethods. // 29. Set F.[[Fields]] to instanceFields. // 30. For each PrivateElement method of staticPrivateMethods, do @@ -430,12 +433,17 @@ impl CompileEvaluation for ast::Class<'_> { // i. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment. // ii. Return ? result. } + // Note: We finally leave classEnv here. See step 26. + ctx.exe + .add_instruction(Instruction::ExitDeclarativeEnvironment); + // 32. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment. // 33. Return F. // 15.7.15 Runtime Semantics: BindingClassDeclarationEvaluation // ClassDeclaration: class BindingIdentifier ClassTail - if let Some(class_identifier) = class_identifier { + if self.is_declaration() { + let class_identifier = class_identifier.unwrap(); // 4. Let env be the running execution context's LexicalEnvironment. // 5. Perform ? InitializeBoundName(className, value, env). // => a. Perform ! environment.InitializeBinding(name, value). @@ -589,6 +597,8 @@ impl CompileEvaluation for ast::StaticBlock<'_> { // b. Let instantiatedVarNames be a copy of the List parameterBindings. let mut instantiated_var_names = AHashSet::new(); // c. For each element n of varNames, do + ctx.exe + .add_instruction(Instruction::EnterClassStaticElementEnvironment); for n in class_static_block_var_declared_names(self) { // i. If instantiatedVarNames does not contain n, then if instantiated_var_names.contains(&n) { @@ -670,5 +680,9 @@ impl CompileEvaluation for ast::StaticBlock<'_> { for statement in self.body.iter() { statement.compile(ctx); } + ctx.exe + .add_instruction(Instruction::ExitDeclarativeEnvironment); + ctx.exe + .add_instruction(Instruction::ExitVariableEnvironment); } } diff --git a/nova_vm/src/engine/bytecode/instructions.rs b/nova_vm/src/engine/bytecode/instructions.rs index 62bdeb07..e7b5dcb3 100644 --- a/nova_vm/src/engine/bytecode/instructions.rs +++ b/nova_vm/src/engine/bytecode/instructions.rs @@ -250,13 +250,14 @@ pub enum Instruction { /// spec requires that creation of bindings in the environment is done /// first. This is immaterial because creating the bindings cannot fail. EnterDeclarativeEnvironment, - /// Perform NewDeclarativeEnvironment with the running execution context's - /// LexicalEnvironment as the only parameter, and set it as the running - /// execution context's VariableEnvironment. - EnterDeclarativeVariableEnvironment, + /// Enter a new FunctionEnvironment with the top of the stack as the this + /// binding and \[\[FunctionObject]]. This is used for class static + /// initializers. + EnterClassStaticElementEnvironment, /// Reset the running execution context's LexicalEnvironment to its current /// value's \[\[OuterEnv]]. ExitDeclarativeEnvironment, + ExitVariableEnvironment, /// Begin binding values using destructuring BeginSimpleObjectBindingPattern, /// Begin binding values using a sync iterator for known repetitions diff --git a/nova_vm/src/engine/bytecode/vm.rs b/nova_vm/src/engine/bytecode/vm.rs index 052d8fa6..cbefcd1b 100644 --- a/nova_vm/src/engine/bytecode/vm.rs +++ b/nova_vm/src/engine/bytecode/vm.rs @@ -35,8 +35,9 @@ use crate::{ }, execution::{ agent::{resolve_binding, ExceptionType, JsError}, - get_this_environment, new_declarative_environment, Agent, - ECMAScriptCodeEvaluationState, EnvironmentIndex, JsResult, ProtoIntrinsics, + get_this_environment, new_class_static_element_environment, + new_declarative_environment, Agent, ECMAScriptCodeEvaluationState, EnvironmentIndex, + JsResult, ProtoIntrinsics, }, syntax_directed_operations::class_definitions::{ base_class_default_constructor, derived_class_default_constructor, @@ -1347,28 +1348,42 @@ impl Vm { .unwrap() .lexical_environment = EnvironmentIndex::Declarative(new_env); } - Instruction::EnterDeclarativeVariableEnvironment => { - let outer_env = agent + Instruction::EnterClassStaticElementEnvironment => { + let class_constructor = Function::try_from(*vm.stack.last().unwrap()).unwrap(); + let local_env = new_class_static_element_environment(agent, class_constructor); + let local_env = EnvironmentIndex::Function(local_env); + + let current_context = agent + .running_execution_context_mut() + .ecmascript_code + .as_mut() + .unwrap(); + current_context.lexical_environment = local_env; + current_context.variable_environment = local_env; + } + Instruction::ExitDeclarativeEnvironment => { + let old_env = agent .running_execution_context() .ecmascript_code .as_ref() .unwrap() - .lexical_environment; - let new_env = new_declarative_environment(agent, Some(outer_env)); + .lexical_environment + .get_outer_env(agent) + .unwrap(); agent .running_execution_context_mut() .ecmascript_code .as_mut() .unwrap() - .variable_environment = EnvironmentIndex::Declarative(new_env); + .lexical_environment = old_env; } - Instruction::ExitDeclarativeEnvironment => { + Instruction::ExitVariableEnvironment => { let old_env = agent .running_execution_context() .ecmascript_code .as_ref() .unwrap() - .lexical_environment + .variable_environment .get_outer_env(agent) .unwrap(); agent @@ -1376,7 +1391,7 @@ impl Vm { .ecmascript_code .as_mut() .unwrap() - .lexical_environment = old_env; + .variable_environment = old_env; } Instruction::CreateMutableBinding => { let lex_env = agent From f00c4354fddadce3b14e0356a4ff86023e55ae83 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 8 Sep 2024 13:19:25 +0300 Subject: [PATCH 4/4] chore(test262): Update expectations --- tests/expectations.json | 14 -------------- tests/metrics.json | 6 +++--- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/tests/expectations.json b/tests/expectations.json index aa9997b8..4f53fe54 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -14208,8 +14208,6 @@ "language/expressions/class/async-method/returns-async-function-returns-arguments-from-own-function.js": "CRASH", "language/expressions/class/async-method/returns-async-function-returns-newtarget.js": "CRASH", "language/expressions/class/async-method/returns-async-function.js": "CRASH", - "language/expressions/class/class-name-ident-await-escaped.js": "CRASH", - "language/expressions/class/class-name-ident-await.js": "CRASH", "language/expressions/class/class-name-ident-let-escaped.js": "CRASH", "language/expressions/class/class-name-ident-let.js": "CRASH", "language/expressions/class/class-name-ident-static-escaped.js": "CRASH", @@ -16867,7 +16865,6 @@ "language/expressions/class/gen-method/dflt-params-ref-later.js": "CRASH", "language/expressions/class/gen-method/dflt-params-ref-self.js": "CRASH", "language/expressions/class/method-length-dflt.js": "CRASH", - "language/expressions/class/name.js": "CRASH", "language/expressions/class/private-getter-brand-check-multiple-evaluations-of-class-eval-indirect.js": "CRASH", "language/expressions/class/private-getter-brand-check-multiple-evaluations-of-class-eval.js": "CRASH", "language/expressions/class/private-getter-brand-check-multiple-evaluations-of-class-factory.js": "CRASH", @@ -16906,9 +16903,6 @@ "language/expressions/class/private-static-setter-multiple-evaluations-of-class-factory.js": "CRASH", "language/expressions/class/private-static-setter-multiple-evaluations-of-class-function-ctor.js": "CRASH", "language/expressions/class/private-static-setter-multiple-evaluations-of-class-realm.js": "FAIL", - "language/expressions/class/scope-name-lex-close.js": "CRASH", - "language/expressions/class/scope-name-lex-open-heritage.js": "CRASH", - "language/expressions/class/scope-name-lex-open-no-heritage.js": "CRASH", "language/expressions/class/static-method-length-dflt.js": "CRASH", "language/expressions/class/subclass-builtins/subclass-AggregateError.js": "CRASH", "language/expressions/class/subclass-builtins/subclass-ArrayBuffer.js": "CRASH", @@ -22302,20 +22296,13 @@ "language/statements/class/gen-method/dflt-params-ref-later.js": "CRASH", "language/statements/class/gen-method/dflt-params-ref-self.js": "CRASH", "language/statements/class/method-length-dflt.js": "CRASH", - "language/statements/class/name-binding/const.js": "CRASH", - "language/statements/class/name-binding/expression.js": "CRASH", "language/statements/class/static-classelementname-abrupt-completion.js": "CRASH", "language/statements/class/static-init-abrupt.js": "CRASH", "language/statements/class/static-init-arguments-functions.js": "CRASH", "language/statements/class/static-init-arguments-methods.js": "CRASH", "language/statements/class/static-init-expr-new-target.js": "CRASH", - "language/statements/class/static-init-expr-this.js": "CRASH", - "language/statements/class/static-init-scope-lex-close.js": "CRASH", - "language/statements/class/static-init-scope-lex-derived.js": "CRASH", "language/statements/class/static-init-scope-lex-open.js": "CRASH", "language/statements/class/static-init-scope-private.js": "CRASH", - "language/statements/class/static-init-scope-var-close.js": "CRASH", - "language/statements/class/static-init-scope-var-open.js": "CRASH", "language/statements/class/static-init-sequence.js": "CRASH", "language/statements/class/static-init-super-property.js": "CRASH", "language/statements/class/static-method-length-dflt.js": "CRASH", @@ -22432,7 +22419,6 @@ "language/statements/class/super/in-static-setter.js": "CRASH", "language/statements/class/syntax/class-body-has-direct-super-class-heritage.js": "CRASH", "language/statements/class/syntax/class-body-method-definition-super-property.js": "CRASH", - "language/statements/class/syntax/class-expression-binding-identifier-opt-class-element-list.js": "CRASH", "language/statements/const/cptn-value.js": "FAIL", "language/statements/const/dstr/ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", "language/statements/const/dstr/ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", diff --git a/tests/metrics.json b/tests/metrics.json index f4cbd532..c1a6ef10 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { - "crash": 17321, - "fail": 7714, - "pass": 19984, + "crash": 17296, + "fail": 7725, + "pass": 19998, "skip": 31, "timeout": 1, "unresolved": 0