// Callback-free provenance sealing for the closed constructor recipe. The
// shell may use this registry only after every global/function/prototype check
// below has succeeded; recursive edges never perform name or property lookup.
///|
priv struct ConstructorRecursionPreflight {
plan : ConstructorRecursionPlan
expected_global_object : ObjectData
expected_object_prototype : Value
expected_realm_protos : FunctionRealmProtos
expected_source_identity : String?
throw_type_error_snapshot : CallbackFreeBindingSnapshot
}
///|
fn ConstructorRecursionPreflight::ConstructorRecursionPreflight(
plan~ : ConstructorRecursionPlan,
expected_global_object~ : ObjectData,
expected_object_prototype~ : Value,
expected_realm_protos~ : FunctionRealmProtos,
expected_source_identity~ : String?,
throw_type_error_snapshot~ : CallbackFreeBindingSnapshot,
) -> ConstructorRecursionPreflight {
{
plan,
expected_global_object,
expected_object_prototype,
expected_realm_protos,
expected_source_identity,
throw_type_error_snapshot,
}
}
///|
#warnings("-unused_field")
priv struct TrustedConstructorRecursionFunction {
plan : ConstructorRecursionFunctionPlan
callee : Value
closure : Environment
params : Array[String]
body : Array[@ast.Stmt]
strict : Bool
source_text : String?
prototype : Value
expected_realm_protos : FunctionRealmProtos
expected_source_identity : String?
}
///|
fn TrustedConstructorRecursionFunction::TrustedConstructorRecursionFunction(
plan~ : ConstructorRecursionFunctionPlan,
callee~ : Value,
closure~ : Environment,
params~ : Array[String],
body~ : Array[@ast.Stmt],
strict~ : Bool,
source_text~ : String?,
prototype~ : Value,
expected_realm_protos~ : FunctionRealmProtos,
expected_source_identity~ : String?,
) -> TrustedConstructorRecursionFunction {
{
plan,
callee,
closure,
params,
body,
strict,
source_text,
prototype,
expected_realm_protos,
expected_source_identity,
}
}
///|
#warnings("-unused_field")
priv struct TrustedConstructorRecursionRegistry {
plan : ConstructorRecursionPlan
functions : Array[TrustedConstructorRecursionFunction]
expected_global_object : ObjectData
expected_object_prototype : Value
throw_type_error : Value?
throw_type_error_snapshot : CallbackFreeBindingSnapshot
}
///|
fn TrustedConstructorRecursionRegistry::TrustedConstructorRecursionRegistry(
plan~ : ConstructorRecursionPlan,
functions~ : Array[TrustedConstructorRecursionFunction],
expected_global_object~ : ObjectData,
expected_object_prototype~ : Value,
throw_type_error~ : Value?,
throw_type_error_snapshot~ : CallbackFreeBindingSnapshot,
) -> TrustedConstructorRecursionRegistry {
{
plan,
functions: functions.copy(),
expected_global_object,
expected_object_prototype,
throw_type_error,
throw_type_error_snapshot,
}
}
///|
fn constructor_recursion_global_binding(
interp : Interpreter,
name : String,
) -> Value? {
match interp.global.bindings.get(name) {
Some(binding) if binding.initialized && binding.kind == VarBinding =>
Some(binding.value)
_ => None
}
}
///|
fn constructor_recursion_value_identity(
expected : Value,
actual : Value,
) -> Bool {
match (expected, actual) {
(Object(expected_data), Object(actual_data)) =>
physical_equal(expected_data, actual_data)
_ => false
}
}
///|
fn constructor_recursion_own_prototype(
callee : Value,
) -> (Value, PropDescriptor)? {
match callee {
Object({ callable: Some(UserFunc(_)), bag, .. }) =>
match
(bag.properties.get("prototype"), bag.descriptors.get("prototype")) {
(Some(value), Some(descriptor)) if !descriptor.is_accessor &&
descriptor.getter is None &&
descriptor.setter is None => Some((value, descriptor))
_ => None
}
_ => None
}
}
///|
fn constructor_recursion_instance_prototype(
callee : Value,
expected_object_prototype : Value,
) -> Value raise Error {
let (prototype, _) = match constructor_recursion_own_prototype(callee) {
Some(found) => found
None =>
raise @errors.TypeError(
message="constructor prototype provenance changed",
)
}
match prototype {
Object(_) | Array(_) | Map(_) | Set(_) | Promise(_) | Proxy(_) => prototype
_ =>
constructor_realm_intrinsic_prototype(
callee, "Object", expected_object_prototype,
)
}
}
///|
fn constructor_recursion_function_matches(
interp : Interpreter,
trusted : TrustedConstructorRecursionFunction,
candidate : Value,
) -> Bool {
guard candidate is Object(actual_object) &&
actual_object.class_name == "Function" &&
actual_object.callable is Some(UserFunc(actual_data)) &&
constructor_recursion_value_identity(trusted.callee, candidate) &&
physical_equal(actual_data.params, trusted.params) &&
physical_equal(actual_data.body, trusted.body) &&
physical_equal(actual_data.closure, trusted.closure) &&
actual_data.strict == trusted.strict &&
actual_data.is_method == false &&
actual_data.name == Some(trusted.plan.name) &&
actual_data.source_text == trusted.source_text &&
interp.global.parent is None else {
return false
}
guard constructor_recursion_own_prototype(candidate)
is Some((prototype, descriptor)) else {
return false
}
guard constructor_recursion_value_identity(trusted.prototype, prototype) &&
!descriptor.is_accessor &&
descriptor.getter is None &&
descriptor.setter is None else {
return false
}
function_source_identity(candidate) == trusted.expected_source_identity &&
numeric_recursion_realm_protos_match(
trusted.expected_realm_protos,
callee_realm_protos(candidate),
)
}
///|
fn constructor_recursion_revalidate(
interp : Interpreter,
registry : TrustedConstructorRecursionRegistry,
) -> Unit raise InvalidActivationDispatchShell {
guard interp.numeric_recursion_canonical_global_object()
is Some(actual_global_object) &&
physical_equal(actual_global_object, registry.expected_global_object) else {
invalid_activation_dispatch_shell(
"constructor recursion global changed after callback-free admission",
)
}
let actual_throw_type_error = numeric_recursion_callback_free_throw_type_error(
interp.global,
)
guard numeric_recursion_callback_free_binding_snapshots_match(
registry.throw_type_error_snapshot,
actual_throw_type_error,
) else {
invalid_activation_dispatch_shell(
"constructor recursion ThrowTypeError binding changed after admission",
)
}
for trusted in registry.functions {
let candidate = match
constructor_recursion_global_binding(interp, trusted.plan.name) {
Some(value) => value
None =>
invalid_activation_dispatch_shell(
"constructor recursion function binding was lost",
)
}
guard constructor_recursion_function_matches(interp, trusted, candidate) else {
invalid_activation_dispatch_shell(
"constructor recursion function provenance changed",
)
}
}
}
///|
fn Interpreter::preflight_constructor_recursion_program(
self : Interpreter,
plan : ConstructorRecursionPlan,
) -> ConstructorRecursionPreflight? {
guard self.numeric_recursion_canonical_global_object()
is Some(expected_global_object) else {
return None
}
for function in plan.functions {
guard !self.global.bindings.contains(function.name) &&
expected_global_object.bag.properties.get(function.name) is None else {
return None
}
}
let expected_realm_protos = function_realm_protos_from_realm_state(
self.realm_state,
)
let expected_object_prototype = match expected_realm_protos.object_proto {
Some(value) => value
None => get_obj_proto(realm_state=Some(self.realm_state))
}
let throw_type_error_snapshot = numeric_recursion_callback_free_throw_type_error(
self.global,
)
match throw_type_error_snapshot {
CallbackFreeBindingUnsafe => None
_ =>
Some(
ConstructorRecursionPreflight(
plan~,
expected_global_object~,
expected_object_prototype~,
expected_realm_protos~,
expected_source_identity=self.realm_state.active_source_identity.val,
throw_type_error_snapshot~,
),
)
}
}
///|
#warnings("-unused_value")
fn Interpreter::preflight_dispatchable_constructor_recursion_program(
self : Interpreter,
stmts : Array[@ast.Stmt],
) -> ConstructorRecursionPreflight? {
match classify_constructor_recursion_program(stmts) {
Some(plan) => self.preflight_constructor_recursion_program(plan)
None => None
}
}
///|
fn Interpreter::seal_constructor_recursion_registry(
self : Interpreter,
preflight : ConstructorRecursionPreflight,
stmts : Array[@ast.Stmt],
) -> TrustedConstructorRecursionRegistry raise InvalidActivationDispatchShell {
guard self.numeric_recursion_canonical_global_object()
is Some(actual_global_object) &&
physical_equal(actual_global_object, preflight.expected_global_object) else {
invalid_activation_dispatch_shell(
"constructor recursion global changed during root setup",
)
}
let actual_throw_type_error = numeric_recursion_callback_free_throw_type_error(
self.global,
)
guard numeric_recursion_callback_free_binding_snapshots_match(
preflight.throw_type_error_snapshot,
actual_throw_type_error,
) else {
invalid_activation_dispatch_shell(
"constructor recursion ThrowTypeError binding changed during root setup",
)
}
let actual_plan = match classify_constructor_recursion_program(stmts) {
Some(plan) => plan
None =>
invalid_activation_dispatch_shell(
"constructor recursion program no longer satisfies exact admission",
)
}
guard actual_plan.root_name == preflight.plan.root_name &&
actual_plan.initial_argument == preflight.plan.initial_argument &&
actual_plan.functions.length() == preflight.plan.functions.length() else {
invalid_activation_dispatch_shell(
"constructor recursion plan changed during root setup",
)
}
let functions : Array[TrustedConstructorRecursionFunction] = []
for index, function_plan in actual_plan.functions {
guard constructor_recursion_root_statement_matches(
actual_plan,
index,
stmts[index],
) else {
invalid_activation_dispatch_shell(
"constructor recursion declaration changed during root setup",
)
}
let callee = match
constructor_recursion_global_binding(self, function_plan.name) {
Some(value) => value
None =>
invalid_activation_dispatch_shell(
"constructor recursion declaration was not initialized",
)
}
let (data, _) = match callee {
Object({ callable: Some(UserFunc(data)), .. }) => (data, callee)
_ =>
invalid_activation_dispatch_shell(
"constructor recursion declaration is not an ordinary UserFunc",
)
}
let prototype_result : Result[Value, Error] = Ok(
constructor_recursion_instance_prototype(
callee,
preflight.expected_object_prototype,
),
) catch {
error => Err(error)
}
let prototype = match prototype_result {
Ok(value) => value
Err(_) =>
invalid_activation_dispatch_shell(
"constructor recursion prototype provenance could not be sealed",
)
}
let trusted = TrustedConstructorRecursionFunction(
plan=function_plan,
callee~,
closure=data.closure,
params=data.params,
body=data.body,
strict=data.strict,
source_text=data.source_text,
prototype~,
expected_realm_protos=preflight.expected_realm_protos,
expected_source_identity=preflight.expected_source_identity,
)
guard constructor_recursion_function_matches(self, trusted, callee) else {
invalid_activation_dispatch_shell(
"constructor recursion declaration failed provenance sealing",
)
}
functions.push(trusted)
}
let registry = TrustedConstructorRecursionRegistry(
plan=actual_plan,
functions~,
expected_global_object=preflight.expected_global_object,
expected_object_prototype=preflight.expected_object_prototype,
throw_type_error=numeric_recursion_throw_type_error_value(
actual_throw_type_error,
),
throw_type_error_snapshot=preflight.throw_type_error_snapshot,
)
constructor_recursion_revalidate(self, registry)
registry
}
///|
fn TrustedConstructorRecursionRegistry::require_function(
self : TrustedConstructorRecursionRegistry,
interp : Interpreter,
name : String,
) -> TrustedConstructorRecursionFunction raise InvalidActivationDispatchShell {
constructor_recursion_revalidate(interp, self)
for function in self.functions {
if function.plan.name == name {
return function
}
}
invalid_activation_dispatch_shell(
"constructor recursion requested an unsealed function",
)
}
///|
fn TrustedConstructorRecursionRegistry::require_root_function(
self : TrustedConstructorRecursionRegistry,
interp : Interpreter,
) -> TrustedConstructorRecursionFunction raise InvalidActivationDispatchShell {
self.require_function(interp, self.plan.root_name)
}