Skip to content

Commit 50414fa

Browse files
authored
feat(ecmascript): Builtin class constructors and field initializers (#426)
1 parent e278a86 commit 50414fa

File tree

26 files changed

+1035
-542
lines changed

26 files changed

+1035
-542
lines changed

nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@ use crate::{
1717
builtins::{
1818
array_create,
1919
keyed_collections::map_objects::map_prototype::canonicalize_keyed_collection_key,
20-
ArgumentsList, Array,
20+
ArgumentsList, Array, BuiltinConstructorFunction,
21+
},
22+
execution::{
23+
agent::ExceptionType, new_class_field_initializer_environment, Agent,
24+
ECMAScriptCodeEvaluationState, EnvironmentIndex, ExecutionContext, JsResult,
25+
RealmIdentifier,
2126
},
22-
execution::{agent::ExceptionType, Agent, JsResult, RealmIdentifier},
2327
types::{
24-
Function, InternalMethods, IntoObject, IntoValue, Number, Object, OrdinaryObject,
25-
PropertyDescriptor, PropertyKey, String, Value, BUILTIN_STRING_MEMORY,
28+
Function, InternalMethods, IntoFunction, IntoObject, IntoValue, Number, Object,
29+
OrdinaryObject, PropertyDescriptor, PropertyKey, String, Value, BUILTIN_STRING_MEMORY,
2630
},
2731
},
28-
engine::instanceof_operator,
32+
engine::{instanceof_operator, Vm},
2933
heap::{Heap, ObjectEntry},
3034
SmallInteger,
3135
};
@@ -870,6 +874,63 @@ pub(crate) fn copy_data_properties_into_object(
870874
))
871875
}
872876

877+
/// [7.3.33 InitializeInstanceElements ( O, constructor )](https://tc39.es/ecma262/#sec-initializeinstanceelements)
878+
///
879+
/// The abstract operation InitializeInstanceElements takes arguments O (an
880+
/// Object) and constructor (an ECMAScript function object) and returns either
881+
/// a normal completion containing unused or a throw completion.
882+
pub(crate) fn initialize_instance_elements(
883+
agent: &mut Agent,
884+
o: Object,
885+
constructor: BuiltinConstructorFunction,
886+
) -> JsResult<()> {
887+
// 1. Let methods be the value of constructor.[[PrivateMethods]].
888+
// 2. For each PrivateElement method of methods, do
889+
// a. Perform ? PrivateMethodOrAccessorAdd(O, method).
890+
// TODO: Private properties and methods.
891+
// 3. Let fields be the value of constructor.[[Fields]].
892+
// 4. For each element fieldRecord of fields, do
893+
// a. Perform ? DefineField(O, fieldRecord).
894+
// 5. Return unused.
895+
let constructor_data = &agent[constructor];
896+
if let Some(bytecode) = constructor_data.compiled_initializer_bytecode {
897+
// Note: The code here looks quite a bit different from what the spec
898+
// says. For one, the spec is bugged and doesn't consider default
899+
// constructors at all. Second, we compile field initializers into
900+
// the ECMAScript class constructors directly, so our code only needs
901+
// to work for builtin constructors.
902+
// Third, the spec defines the initializers as individual functions
903+
// run one after the other. Instea we compile all of the initializers
904+
// into a single bytecode executable associated with the constructor.
905+
// The problem then becomes how to run this executable as an ECMAScript
906+
// function.
907+
// To do this, we need a new execution context that points to a new
908+
// Function environment. The function environment should be lexically a
909+
// child of the class constructor's creating environment.
910+
let bytecode = unsafe { bytecode.as_ref() };
911+
let f = constructor.into_function();
912+
let outer_env = constructor_data.environment;
913+
let outer_priv_env = constructor_data.private_environment;
914+
let source_code = constructor_data.source_code;
915+
let decl_env = new_class_field_initializer_environment(agent, f, o, outer_env);
916+
agent.execution_context_stack.push(ExecutionContext {
917+
ecmascript_code: Some(ECMAScriptCodeEvaluationState {
918+
lexical_environment: EnvironmentIndex::Function(decl_env),
919+
variable_environment: EnvironmentIndex::Function(decl_env),
920+
private_environment: outer_priv_env,
921+
is_strict_mode: true,
922+
source_code,
923+
}),
924+
function: Some(f),
925+
realm: agent[constructor].realm,
926+
script_or_module: None,
927+
});
928+
let _ = Vm::execute(agent, bytecode, None).into_js_result()?;
929+
agent.execution_context_stack.pop();
930+
}
931+
Ok(())
932+
}
933+
873934
/// ### [7.3.34 AddValueToKeyedGroup ( groups, key, value )](https://tc39.es/ecma262/#sec-add-value-to-keyed-group)
874935
/// The abstract operation AddValueToKeyedGroup takes arguments groups (a List of Records with fields
875936
/// [[Key]] (an ECMAScript language value) and [[Elements]] (a List of ECMAScript language values)),

nova_vm/src/ecmascript/builtins.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub(crate) mod arguments;
1212
mod array;
1313
mod array_buffer;
1414
pub mod bound_function;
15+
mod builtin_constructor;
1516
mod builtin_function;
1617
pub(crate) mod control_abstraction_objects;
1718
pub(crate) mod data_view;
@@ -49,6 +50,8 @@ pub use array::Array;
4950
pub(crate) use array::{ArrayHeapData, SealableElementsVector};
5051
pub use array_buffer::ArrayBuffer;
5152
pub(crate) use array_buffer::ArrayBufferHeapData;
53+
pub use builtin_constructor::BuiltinConstructorFunction;
54+
pub(crate) use builtin_constructor::{create_builtin_constructor, BuiltinConstructorArgs};
5255
pub use builtin_function::{
5356
create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunction,
5457
BuiltinFunctionArgs, BuiltinGetter, ConstructorFn, RegularFn as JsFunction, RegularFn,

0 commit comments

Comments
 (0)