LibJS: Cache dynamic environment coordinates
Dynamic environment binding opcodes lost the old coordinate warmup. They were split away from the static coordinate opcodes. Hot closures and eval-sensitive functions then resolved the same binding by name on every execution, which regressed JS benchmark throughput badly. Give each dynamic environment opcode a per-executable coordinate cache slot. The cache keeps the bytecode stream immutable while letting both interpreters take a direct declarative environment fast path after the first lookup. Keep the existing eval invalidation behavior and only warm caches for declarative-only chains so with environments continue to observe object shadowing. Reject cached bytecode that uses the no-cache sentinel for dynamic environment coordinate cache operands, since execution indexes those cache arrays unconditionally. Rebaseline bytecode expectations for the instruction size changes. Add coverage for with-object shadowing across repeated dynamic lookups and for rejecting corrupt dynamic environment cache indices.
This commit is contained in:
parent
73e7cacd78
commit
4ac744082b
21 changed files with 379 additions and 35 deletions
|
|
@ -275,11 +275,17 @@ i64 asm_try_inline_call(VM*, u32 pc);
|
|||
i64 asm_try_put_by_id_cache(VM*, u32 pc);
|
||||
i64 asm_try_get_by_id_cache(VM*, u32 pc);
|
||||
i64 asm_slow_path_initialize_lexical_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_dynamic_initialize_lexical_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_dynamic_initialize_variable_binding(VM*, u32 pc);
|
||||
|
||||
i64 asm_try_get_by_value_typed_array(VM*, u32 pc);
|
||||
i64 asm_slow_path_get_initialized_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_dynamic_get_initialized_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_get_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_dynamic_get_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_set_lexical_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_dynamic_set_lexical_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_dynamic_set_variable_binding(VM*, u32 pc);
|
||||
i64 asm_slow_path_bitwise_not(VM*, u32 pc);
|
||||
i64 asm_slow_path_unary_plus(VM*, u32 pc);
|
||||
i64 asm_slow_path_throw_if_tdz(VM*, u32 pc);
|
||||
|
|
@ -288,6 +294,7 @@ i64 asm_slow_path_throw_if_nullish(VM*, u32 pc);
|
|||
i64 asm_slow_path_loosely_equals(VM*, u32 pc);
|
||||
i64 asm_slow_path_loosely_inequals(VM*, u32 pc);
|
||||
i64 asm_slow_path_get_callee_and_this(VM*, u32 pc);
|
||||
i64 asm_slow_path_dynamic_get_callee_and_this(VM*, u32 pc);
|
||||
i64 asm_try_put_by_value_typed_array(VM*, u32 pc);
|
||||
i64 asm_slow_path_get_private_by_id(VM*, u32 pc);
|
||||
i64 asm_slow_path_put_private_by_id(VM*, u32 pc);
|
||||
|
|
@ -619,6 +626,11 @@ i64 asm_slow_path_get_initialized_binding(VM* vm, u32 pc)
|
|||
return slow_path_throwing<Op::GetInitializedBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_dynamic_get_initialized_binding(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::DynamicGetInitializedBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_loosely_equals(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::LooselyEquals>(*vm, pc);
|
||||
|
|
@ -634,6 +646,11 @@ i64 asm_slow_path_get_callee_and_this(VM* vm, u32 pc)
|
|||
return slow_path_throwing<Op::GetCalleeAndThisFromEnvironment>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_dynamic_get_callee_and_this(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::DynamicGetCalleeAndThisFromEnvironment>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_postfix_increment(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::PostfixIncrement>(*vm, pc);
|
||||
|
|
@ -1009,16 +1026,41 @@ i64 asm_slow_path_get_binding(VM* vm, u32 pc)
|
|||
return slow_path_throwing<Op::GetBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_dynamic_get_binding(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::DynamicGetBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_initialize_lexical_binding(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::InitializeLexicalBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_dynamic_initialize_lexical_binding(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::DynamicInitializeLexicalBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_dynamic_initialize_variable_binding(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::DynamicInitializeVariableBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_set_lexical_binding(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::SetLexicalBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_dynamic_set_lexical_binding(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::DynamicSetLexicalBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_dynamic_set_variable_binding(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::DynamicSetVariableBinding>(*vm, pc);
|
||||
}
|
||||
|
||||
i64 asm_slow_path_bitwise_not(VM* vm, u32 pc)
|
||||
{
|
||||
return slow_path_throwing<Op::BitwiseNot>(*vm, pc);
|
||||
|
|
|
|||
|
|
@ -460,6 +460,39 @@ macro walk_env_chain(m_cache_field, target_env, bind_index, fail_label)
|
|||
assert_nonzero target_env
|
||||
end
|
||||
|
||||
# Walk the environment chain using a runtime EnvironmentCoordinate cache.
|
||||
# The cache lives in Executable::environment_coordinate_caches so bytecode can
|
||||
# remain immutable while dynamic binding lookups still warm up.
|
||||
macro walk_cached_env_chain(m_environment_field, target_env, bind_index, fail_label)
|
||||
temp exe, caches, coord_addr, hops, sentinel, flag
|
||||
load32 coord_addr, [pb, pc, m_cache]
|
||||
mul coord_addr, coord_addr, ENVIRONMENT_COORDINATE_SIZE
|
||||
load64 exe, [exec_ctx, EXECUTION_CONTEXT_EXECUTABLE]
|
||||
load64 caches, [exe, EXECUTABLE_ENVIRONMENT_COORDINATE_CACHES_DATA]
|
||||
add coord_addr, caches
|
||||
load_pair32 hops, bind_index, [coord_addr, ENVIRONMENT_COORDINATE_HOPS], [coord_addr, ENVIRONMENT_COORDINATE_INDEX]
|
||||
mov sentinel, ENVIRONMENT_COORDINATE_INVALID
|
||||
branch_eq hops, sentinel, fail_label
|
||||
load64 target_env, [exec_ctx, m_environment_field]
|
||||
assert_nonzero target_env
|
||||
branch_zero hops, .walk_done
|
||||
.walk_loop:
|
||||
load8 flag, [target_env, ENVIRONMENT_DECLARATIVE]
|
||||
branch_zero flag, fail_label
|
||||
load8 flag, [target_env, ENVIRONMENT_SCREWED_BY_EVAL]
|
||||
branch_nonzero flag, fail_label
|
||||
load64 target_env, [target_env, ENVIRONMENT_OUTER]
|
||||
branch_zero target_env, fail_label
|
||||
sub hops, 1
|
||||
branch_nonzero hops, .walk_loop
|
||||
.walk_done:
|
||||
assert_nonzero target_env
|
||||
load8 flag, [target_env, ENVIRONMENT_DECLARATIVE]
|
||||
branch_zero flag, fail_label
|
||||
load8 flag, [target_env, ENVIRONMENT_SCREWED_BY_EVAL]
|
||||
branch_nonzero flag, fail_label
|
||||
end
|
||||
|
||||
# Pop an inline frame and resume the caller without bouncing through C++.
|
||||
# The asm-managed JS-to-JS call fast path currently only inlines Call, never
|
||||
# CallConstruct, so caller_is_construct is always false for asm-managed inline
|
||||
|
|
@ -1060,6 +1093,16 @@ handler GetBinding
|
|||
call_slow_path asm_slow_path_get_binding
|
||||
end
|
||||
|
||||
handler DynamicGetBinding
|
||||
temp env, idx, binding_values, value, empty
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, env, idx, .slow
|
||||
check_binding_initialized env, idx, binding_values, value, empty, .slow
|
||||
store_operand m_dst, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_dynamic_get_binding
|
||||
end
|
||||
|
||||
# Inline environment chain walk + direct binding value load.
|
||||
handler GetInitializedBinding
|
||||
temp env, idx, binding_values, value
|
||||
|
|
@ -1071,6 +1114,16 @@ handler GetInitializedBinding
|
|||
call_slow_path asm_slow_path_get_initialized_binding
|
||||
end
|
||||
|
||||
handler DynamicGetInitializedBinding
|
||||
temp env, idx, binding_values, value
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, env, idx, .slow
|
||||
load_binding_value env, idx, binding_values, value
|
||||
store_operand m_dst, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_dynamic_get_initialized_binding
|
||||
end
|
||||
|
||||
# Inline environment chain walk + initialize binding.
|
||||
handler InitializeLexicalBinding
|
||||
temp env, idx, value, binding_values
|
||||
|
|
@ -1082,6 +1135,26 @@ handler InitializeLexicalBinding
|
|||
call_slow_path asm_slow_path_initialize_lexical_binding
|
||||
end
|
||||
|
||||
handler DynamicInitializeLexicalBinding
|
||||
temp env, idx, value, binding_values
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, env, idx, .slow
|
||||
load_operand value, m_src
|
||||
store_binding_value env, idx, binding_values, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_dynamic_initialize_lexical_binding
|
||||
end
|
||||
|
||||
handler DynamicInitializeVariableBinding
|
||||
temp env, idx, value, binding_values
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_VARIABLE_ENVIRONMENT, env, idx, .slow
|
||||
load_operand value, m_src
|
||||
store_binding_value env, idx, binding_values, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_dynamic_initialize_variable_binding
|
||||
end
|
||||
|
||||
# Inline environment chain walk + set mutable binding.
|
||||
handler SetLexicalBinding
|
||||
temp env, idx, flag, value, binding_values, flags, empty
|
||||
|
|
@ -1098,6 +1171,36 @@ handler SetLexicalBinding
|
|||
call_slow_path asm_slow_path_set_lexical_binding
|
||||
end
|
||||
|
||||
handler DynamicSetLexicalBinding
|
||||
temp env, idx, flag, value, binding_values, flags, empty
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, env, idx, .slow
|
||||
check_binding_initialized env, idx, binding_values, value, empty, .slow
|
||||
# Check mutable
|
||||
load_binding_flags env, idx, flags, flag
|
||||
and flag, BINDING_FLAG_MUTABLE
|
||||
branch_zero flag, .slow
|
||||
load_operand value, m_src
|
||||
store_binding_value env, idx, binding_values, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_dynamic_set_lexical_binding
|
||||
end
|
||||
|
||||
handler DynamicSetVariableBinding
|
||||
temp env, idx, flag, value, binding_values, flags, empty
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_VARIABLE_ENVIRONMENT, env, idx, .slow
|
||||
check_binding_initialized env, idx, binding_values, value, empty, .slow
|
||||
# Check mutable
|
||||
load_binding_flags env, idx, flags, flag
|
||||
and flag, BINDING_FLAG_MUTABLE
|
||||
branch_zero flag, .slow
|
||||
load_operand value, m_src
|
||||
store_binding_value env, idx, binding_values, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_dynamic_set_variable_binding
|
||||
end
|
||||
|
||||
# x++: save original to dst first, then increment src in-place.
|
||||
handler PostfixIncrement
|
||||
temp value, tag, int_value, dst
|
||||
|
|
@ -1426,6 +1529,19 @@ handler GetCalleeAndThisFromEnvironment
|
|||
call_slow_path asm_slow_path_get_callee_and_this
|
||||
end
|
||||
|
||||
handler DynamicGetCalleeAndThisFromEnvironment
|
||||
temp env, idx, binding_values, value, empty
|
||||
walk_cached_env_chain EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, env, idx, .slow
|
||||
check_binding_initialized env, idx, binding_values, value, empty, .slow
|
||||
store_operand m_callee, value
|
||||
# this = undefined for cached declarative environment references.
|
||||
mov value, UNDEFINED_SHIFTED
|
||||
store_operand m_this_value, value
|
||||
dispatch_next
|
||||
.slow:
|
||||
call_slow_path asm_slow_path_dynamic_get_callee_and_this
|
||||
end
|
||||
|
||||
handler LooselyEquals
|
||||
temp lhs, rhs
|
||||
load_operand lhs, m_lhs
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ int main()
|
|||
EMIT_OFFSET(EXECUTABLE_CONSTANTS, Executable, constants);
|
||||
EMIT_OFFSET(EXECUTABLE_PROPERTY_LOOKUP_CACHES, Executable, property_lookup_caches);
|
||||
EMIT_OFFSET(EXECUTABLE_GLOBAL_VARIABLE_CACHES, Executable, global_variable_caches);
|
||||
EMIT_OFFSET(EXECUTABLE_ENVIRONMENT_COORDINATE_CACHES, Executable, environment_coordinate_caches);
|
||||
EMIT_OFFSET(EXECUTABLE_REGISTERS_AND_LOCALS_COUNT, Executable, registers_and_locals_count);
|
||||
EMIT_OFFSET(EXECUTABLE_REGISTERS_AND_LOCALS_AND_CONSTANTS_COUNT, Executable, registers_and_locals_and_constants_count);
|
||||
EMIT_OFFSET(EXECUTABLE_ASM_CONSTANTS_SIZE, Executable, asm_constants_size);
|
||||
|
|
@ -211,6 +212,7 @@ int main()
|
|||
outln("const EXECUTABLE_BYTECODE_DATA = {}", offsetof(Executable, bytecode) + InstructionStream::data_member_offset());
|
||||
outln("const EXECUTABLE_PROPERTY_LOOKUP_CACHES_DATA = {}", offsetof(Executable, property_lookup_caches) + vec_data);
|
||||
outln("const EXECUTABLE_GLOBAL_VARIABLE_CACHES_DATA = {}", offsetof(Executable, global_variable_caches) + vec_data);
|
||||
outln("const EXECUTABLE_ENVIRONMENT_COORDINATE_CACHES_DATA = {}", offsetof(Executable, environment_coordinate_caches) + vec_data);
|
||||
outln("const EXECUTABLE_CONSTANTS_DATA = {}", offsetof(Executable, constants) + vec_data);
|
||||
outln("const EXECUTABLE_CONSTANTS_SIZE = {}", offsetof(Executable, constants) + vec_size);
|
||||
outln("const OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTY_VALUES_DATA = {}", offsetof(ObjectPropertyIteratorCacheData, m_property_values) + vec_data);
|
||||
|
|
@ -322,6 +324,7 @@ int main()
|
|||
// Environment layout
|
||||
outln("\n# Environment layout");
|
||||
EMIT_OFFSET(ENVIRONMENT_SCREWED_BY_EVAL, Environment, m_permanently_screwed_by_eval);
|
||||
EMIT_OFFSET(ENVIRONMENT_DECLARATIVE, Environment, m_declarative);
|
||||
EMIT_OFFSET(ENVIRONMENT_OUTER, Environment, m_outer_environment);
|
||||
|
||||
// DeclarativeEnvironment binding storage layout
|
||||
|
|
@ -342,6 +345,7 @@ int main()
|
|||
outln("const ENVIRONMENT_COORDINATE_HOPS = {}", offsetof(EnvironmentCoordinate, hops));
|
||||
outln("const ENVIRONMENT_COORDINATE_INDEX = {}", offsetof(EnvironmentCoordinate, index));
|
||||
outln("const ENVIRONMENT_COORDINATE_INVALID = 0x{:X}", EnvironmentCoordinate::invalid_marker);
|
||||
EMIT_SIZEOF(ENVIRONMENT_COORDINATE_SIZE, EnvironmentCoordinate);
|
||||
|
||||
// TypedArrayBase layout
|
||||
outln("\n# TypedArrayBase layout");
|
||||
|
|
|
|||
|
|
@ -476,6 +476,7 @@ op DynamicGetCalleeAndThisFromEnvironment < Instruction
|
|||
m_callee: Operand
|
||||
m_this_value: Operand
|
||||
m_identifier: IdentifierTableIndex
|
||||
m_cache: EnvironmentCoordinateCacheIndex
|
||||
endop
|
||||
|
||||
op GetCompletionFields < Instruction
|
||||
|
|
@ -564,6 +565,7 @@ endop
|
|||
op DynamicGetBinding < Instruction
|
||||
m_dst: Operand
|
||||
m_identifier: IdentifierTableIndex
|
||||
m_cache: EnvironmentCoordinateCacheIndex
|
||||
endop
|
||||
|
||||
op GetInitializedBinding < Instruction
|
||||
|
|
@ -575,6 +577,7 @@ endop
|
|||
op DynamicGetInitializedBinding < Instruction
|
||||
m_dst: Operand
|
||||
m_identifier: IdentifierTableIndex
|
||||
m_cache: EnvironmentCoordinateCacheIndex
|
||||
endop
|
||||
|
||||
op GreaterThan < Instruction
|
||||
|
|
@ -620,6 +623,7 @@ endop
|
|||
op DynamicInitializeLexicalBinding < Instruction
|
||||
m_identifier: IdentifierTableIndex
|
||||
m_src: Operand
|
||||
m_cache: EnvironmentCoordinateCacheIndex
|
||||
endop
|
||||
|
||||
op InitializeVariableBinding < Instruction
|
||||
|
|
@ -631,6 +635,7 @@ endop
|
|||
op DynamicInitializeVariableBinding < Instruction
|
||||
m_identifier: IdentifierTableIndex
|
||||
m_src: Operand
|
||||
m_cache: EnvironmentCoordinateCacheIndex
|
||||
endop
|
||||
|
||||
op InstanceOf < Instruction
|
||||
|
|
@ -1050,6 +1055,7 @@ endop
|
|||
op DynamicSetLexicalBinding < Instruction
|
||||
m_identifier: IdentifierTableIndex
|
||||
m_src: Operand
|
||||
m_cache: EnvironmentCoordinateCacheIndex
|
||||
endop
|
||||
|
||||
op SetVariableBinding < Instruction
|
||||
|
|
@ -1061,6 +1067,7 @@ endop
|
|||
op DynamicSetVariableBinding < Instruction
|
||||
m_identifier: IdentifierTableIndex
|
||||
m_src: Operand
|
||||
m_cache: EnvironmentCoordinateCacheIndex
|
||||
endop
|
||||
|
||||
op StrictlyEquals < Instruction
|
||||
|
|
@ -1138,6 +1145,7 @@ endop
|
|||
op DynamicTypeofBinding < Instruction
|
||||
m_dst: Operand
|
||||
m_identifier: IdentifierTableIndex
|
||||
m_cache: EnvironmentCoordinateCacheIndex
|
||||
endop
|
||||
|
||||
op UnaryMinus < Instruction
|
||||
|
|
|
|||
|
|
@ -262,6 +262,7 @@ Executable::Executable(
|
|||
NonnullRefPtr<SourceCode const> source_code,
|
||||
size_t number_of_property_lookup_caches,
|
||||
size_t number_of_global_variable_caches,
|
||||
size_t number_of_environment_coordinate_caches,
|
||||
size_t number_of_template_object_caches,
|
||||
size_t number_of_object_shape_caches,
|
||||
size_t number_of_object_property_iterator_caches,
|
||||
|
|
@ -280,6 +281,7 @@ Executable::Executable(
|
|||
{
|
||||
property_lookup_caches.resize(number_of_property_lookup_caches);
|
||||
global_variable_caches.resize(number_of_global_variable_caches);
|
||||
environment_coordinate_caches.resize(number_of_environment_coordinate_caches);
|
||||
template_object_caches.resize(number_of_template_object_caches);
|
||||
object_shape_caches.resize(number_of_object_shape_caches);
|
||||
object_property_iterator_caches.resize(number_of_object_property_iterator_caches);
|
||||
|
|
@ -563,6 +565,7 @@ size_t Executable::external_memory_size() const
|
|||
for (auto const& cache : property_lookup_caches)
|
||||
size = saturating_add_external_memory_size(size, cache.external_memory_size());
|
||||
size = saturating_add_external_memory_size(size, vector_external_memory_size(global_variable_caches));
|
||||
size = saturating_add_external_memory_size(size, vector_external_memory_size(environment_coordinate_caches));
|
||||
size = saturating_add_external_memory_size(size, vector_external_memory_size(template_object_caches));
|
||||
size = saturating_add_external_memory_size(size, vector_external_memory_size(object_shape_caches));
|
||||
for (auto const& cache : object_shape_caches)
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ public:
|
|||
NonnullRefPtr<SourceCode const>,
|
||||
size_t number_of_property_lookup_caches,
|
||||
size_t number_of_global_variable_caches,
|
||||
size_t number_of_environment_coordinate_caches,
|
||||
size_t number_of_template_object_caches,
|
||||
size_t number_of_object_shape_caches,
|
||||
size_t number_of_object_property_iterator_caches,
|
||||
|
|
@ -286,6 +287,7 @@ public:
|
|||
InstructionStream bytecode;
|
||||
Vector<PropertyLookupCache> property_lookup_caches;
|
||||
Vector<GlobalVariableCache> global_variable_caches;
|
||||
Vector<EnvironmentCoordinate> environment_coordinate_caches;
|
||||
Vector<TemplateObjectCache> template_object_caches;
|
||||
Vector<ObjectShapeCache> object_shape_caches;
|
||||
Vector<ObjectPropertyIteratorCache> object_property_iterator_caches;
|
||||
|
|
|
|||
|
|
@ -1304,9 +1304,59 @@ inline ThrowCompletionOr<CalleeAndThis> get_callee_and_this_from_environment(VM&
|
|||
};
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<CalleeAndThis> dynamically_get_callee_and_this_from_environment(VM& vm, Utf16FlyString const& name, Strict strict)
|
||||
template<typename EnvironmentPointer>
|
||||
static EnvironmentPointer get_cacheable_environment(EnvironmentPointer environment, EnvironmentCoordinate const& cache)
|
||||
{
|
||||
VERIFY(cache.is_valid());
|
||||
|
||||
for (size_t i = 0; i < cache.hops; ++i) {
|
||||
if (!environment->is_declarative_environment() || environment->is_permanently_screwed_by_eval()) [[unlikely]]
|
||||
return nullptr;
|
||||
environment = environment->outer_environment();
|
||||
if (!environment) [[unlikely]]
|
||||
return nullptr;
|
||||
}
|
||||
if (environment->is_declarative_environment() && !environment->is_permanently_screwed_by_eval()) [[likely]]
|
||||
return environment;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename EnvironmentPointer>
|
||||
static EnvironmentPointer get_cached_environment(EnvironmentPointer environment, EnvironmentCoordinate& cache)
|
||||
{
|
||||
if (!cache.is_valid()) [[unlikely]]
|
||||
return nullptr;
|
||||
|
||||
if (auto* cached_environment = get_cacheable_environment(environment, cache)) [[likely]]
|
||||
return cached_environment;
|
||||
|
||||
cache = {};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename EnvironmentPointer>
|
||||
static void update_environment_coordinate_cache(EnvironmentPointer environment, Reference const& reference, EnvironmentCoordinate& cache)
|
||||
{
|
||||
if (!reference.environment_coordinate().has_value())
|
||||
return;
|
||||
auto candidate = reference.environment_coordinate().value();
|
||||
if (get_cacheable_environment(environment, candidate))
|
||||
cache = candidate;
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<CalleeAndThis> dynamically_get_callee_and_this_from_environment(VM& vm, Utf16FlyString const& name, Strict strict, EnvironmentCoordinate& cache)
|
||||
{
|
||||
auto const* current_environment = vm.running_execution_context().lexical_environment.ptr();
|
||||
if (auto const* environment = get_cached_environment(current_environment, cache)) [[likely]] {
|
||||
auto callee = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, cache.index));
|
||||
return CalleeAndThis {
|
||||
.callee = callee,
|
||||
.this_value = js_undefined(),
|
||||
};
|
||||
}
|
||||
|
||||
auto reference = TRY(vm.resolve_binding(name, strict));
|
||||
update_environment_coordinate_cache(current_environment, reference, cache);
|
||||
|
||||
auto callee = TRY(reference.get_value(vm));
|
||||
|
||||
|
|
@ -2331,10 +2381,24 @@ static ThrowCompletionOr<void> get_binding(VM& vm, Operand dst, EnvironmentCoord
|
|||
return {};
|
||||
}
|
||||
|
||||
static ThrowCompletionOr<void> dynamically_get_binding(VM& vm, Operand dst, IdentifierTableIndex identifier, Strict strict)
|
||||
template<BindingIsKnownToBeInitialized binding_is_known_to_be_initialized>
|
||||
static ThrowCompletionOr<void> dynamically_get_binding(VM& vm, Operand dst, IdentifierTableIndex identifier, Strict strict, EnvironmentCoordinate& cache)
|
||||
{
|
||||
auto const* current_environment = vm.running_execution_context().lexical_environment.ptr();
|
||||
if (auto const* environment = get_cached_environment(current_environment, cache)) [[likely]] {
|
||||
Value value;
|
||||
if constexpr (binding_is_known_to_be_initialized == BindingIsKnownToBeInitialized::No) {
|
||||
value = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, cache.index));
|
||||
} else {
|
||||
value = static_cast<DeclarativeEnvironment const&>(*environment).get_initialized_binding_value_direct(cache.index);
|
||||
}
|
||||
vm.set(dst, value);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& executable = vm.current_executable();
|
||||
auto reference = TRY(vm.resolve_binding(executable.get_identifier(identifier), strict));
|
||||
update_environment_coordinate_cache(current_environment, reference, cache);
|
||||
|
||||
vm.set(dst, TRY(reference.get_value(vm)));
|
||||
return {};
|
||||
|
|
@ -2352,12 +2416,12 @@ ThrowCompletionOr<void> GetInitializedBinding::execute_impl(VM& vm) const
|
|||
|
||||
ThrowCompletionOr<void> DynamicGetBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_get_binding(vm, m_dst, m_identifier, strict());
|
||||
return dynamically_get_binding<BindingIsKnownToBeInitialized::No>(vm, m_dst, m_identifier, strict(), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicGetInitializedBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_get_binding(vm, m_dst, m_identifier, strict());
|
||||
return dynamically_get_binding<BindingIsKnownToBeInitialized::Yes>(vm, m_dst, m_identifier, strict(), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetCalleeAndThisFromEnvironment::execute_impl(VM& vm) const
|
||||
|
|
@ -2375,7 +2439,8 @@ ThrowCompletionOr<void> DynamicGetCalleeAndThisFromEnvironment::execute_impl(VM&
|
|||
auto callee_and_this = TRY(dynamically_get_callee_and_this_from_environment(
|
||||
vm,
|
||||
vm.get_identifier(m_identifier),
|
||||
strict()));
|
||||
strict(),
|
||||
vm.current_executable().environment_coordinate_caches[m_cache]));
|
||||
vm.set(m_callee, callee_and_this.callee);
|
||||
vm.set(m_this_value, callee_and_this.this_value);
|
||||
return {};
|
||||
|
|
@ -2601,13 +2666,23 @@ static ThrowCompletionOr<void> initialize_or_set_binding(VM& vm, Strict strict,
|
|||
}
|
||||
|
||||
template<EnvironmentMode environment_mode, BindingInitializationMode initialization_mode>
|
||||
static ThrowCompletionOr<void> dynamically_initialize_or_set_binding(VM& vm, IdentifierTableIndex identifier_index, Strict strict, Value value)
|
||||
static ThrowCompletionOr<void> dynamically_initialize_or_set_binding(VM& vm, IdentifierTableIndex identifier_index, Strict strict, Value value, EnvironmentCoordinate& cache)
|
||||
{
|
||||
auto* environment = environment_mode == EnvironmentMode::Lexical
|
||||
? vm.running_execution_context().lexical_environment.ptr()
|
||||
: vm.running_execution_context().variable_environment.ptr();
|
||||
|
||||
if (auto* cached_environment = get_cached_environment(environment, cache)) [[likely]] {
|
||||
if constexpr (initialization_mode == BindingInitializationMode::Initialize) {
|
||||
TRY(static_cast<DeclarativeEnvironment&>(*cached_environment).initialize_binding_direct(vm, cache.index, value, Environment::InitializeBindingHint::Normal));
|
||||
} else if (initialization_mode == BindingInitializationMode::Set) {
|
||||
TRY(static_cast<DeclarativeEnvironment&>(*cached_environment).set_mutable_binding_direct(vm, cache.index, value, strict == Strict::Yes));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto reference = TRY(vm.resolve_binding(vm.get_identifier(identifier_index), strict, environment));
|
||||
update_environment_coordinate_cache(environment, reference, cache);
|
||||
if constexpr (initialization_mode == BindingInitializationMode::Initialize) {
|
||||
TRY(reference.initialize_referenced_binding(vm, value));
|
||||
} else if (initialization_mode == BindingInitializationMode::Set) {
|
||||
|
|
@ -2628,12 +2703,12 @@ ThrowCompletionOr<void> InitializeVariableBinding::execute_impl(VM& vm) const
|
|||
|
||||
ThrowCompletionOr<void> DynamicInitializeLexicalBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Initialize>(vm, m_identifier, strict(), vm.get(m_src));
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Initialize>(vm, m_identifier, strict(), vm.get(m_src), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicInitializeVariableBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Initialize>(vm, m_identifier, strict(), vm.get(m_src));
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Initialize>(vm, m_identifier, strict(), vm.get(m_src), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> SetLexicalBinding::execute_impl(VM& vm) const
|
||||
|
|
@ -2648,12 +2723,12 @@ ThrowCompletionOr<void> SetVariableBinding::execute_impl(VM& vm) const
|
|||
|
||||
ThrowCompletionOr<void> DynamicSetLexicalBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Set>(vm, m_identifier, strict(), vm.get(m_src));
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Lexical, BindingInitializationMode::Set>(vm, m_identifier, strict(), vm.get(m_src), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> DynamicSetVariableBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Set>(vm, m_identifier, strict(), vm.get(m_src));
|
||||
return dynamically_initialize_or_set_binding<EnvironmentMode::Var, BindingInitializationMode::Set>(vm, m_identifier, strict(), vm.get(m_src), vm.current_executable().environment_coordinate_caches[m_cache]);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<void> GetById::execute_impl(VM& vm) const
|
||||
|
|
@ -3449,6 +3524,14 @@ ThrowCompletionOr<void> TypeofBinding::execute_impl(VM& vm) const
|
|||
|
||||
ThrowCompletionOr<void> DynamicTypeofBinding::execute_impl(VM& vm) const
|
||||
{
|
||||
auto& cache = vm.current_executable().environment_coordinate_caches[m_cache];
|
||||
auto const* current_environment = vm.running_execution_context().lexical_environment.ptr();
|
||||
if (auto const* environment = get_cached_environment(current_environment, cache)) [[likely]] {
|
||||
auto value = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, cache.index));
|
||||
vm.set(dst(), value.typeof_(vm));
|
||||
return {};
|
||||
}
|
||||
|
||||
// 1. Let val be the result of evaluating UnaryExpression.
|
||||
auto reference = TRY(vm.resolve_binding(vm.get_identifier(m_identifier), strict()));
|
||||
|
||||
|
|
@ -3460,6 +3543,8 @@ ThrowCompletionOr<void> DynamicTypeofBinding::execute_impl(VM& vm) const
|
|||
}
|
||||
|
||||
// 3. Set val to ? GetValue(val).
|
||||
update_environment_coordinate_cache(current_environment, reference, cache);
|
||||
|
||||
auto value = TRY(reference.get_value(vm));
|
||||
|
||||
// 4. NOTE: This step is replaced in section B.3.6.3.
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ static StringView validation_error_kind_to_string(JS::FFI::ValidationErrorKind k
|
|||
return "PropertyLookupCacheIndexOutOfRange"sv;
|
||||
case JS::FFI::ValidationErrorKind::GlobalVariableCacheIndexOutOfRange:
|
||||
return "GlobalVariableCacheIndexOutOfRange"sv;
|
||||
case JS::FFI::ValidationErrorKind::EnvironmentCoordinateCacheIndexOutOfRange:
|
||||
return "EnvironmentCoordinateCacheIndexOutOfRange"sv;
|
||||
case JS::FFI::ValidationErrorKind::TemplateObjectCacheIndexOutOfRange:
|
||||
return "TemplateObjectCacheIndexOutOfRange"sv;
|
||||
case JS::FFI::ValidationErrorKind::ObjectShapeCacheIndexOutOfRange:
|
||||
|
|
@ -109,6 +111,7 @@ ErrorOr<void> validate_bytecode(Executable const& executable, ReadonlySpan<u32>
|
|||
.regex_table_size = 0,
|
||||
.property_lookup_cache_count = static_cast<u32>(executable.property_lookup_caches.size()),
|
||||
.global_variable_cache_count = static_cast<u32>(executable.global_variable_caches.size()),
|
||||
.environment_coordinate_cache_count = static_cast<u32>(executable.environment_coordinate_caches.size()),
|
||||
.template_object_cache_count = static_cast<u32>(executable.template_object_caches.size()),
|
||||
.object_shape_cache_count = static_cast<u32>(executable.object_shape_caches.size()),
|
||||
.object_property_iterator_cache_count = static_cast<u32>(executable.object_property_iterator_caches.size()),
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ pub fn field_type_info(ty: &str) -> FieldType {
|
|||
"Value" => ("u64", 8, 8, "u64"),
|
||||
"PropertyLookupCacheIndex"
|
||||
| "GlobalVariableCacheIndex"
|
||||
| "EnvironmentCoordinateCacheIndex"
|
||||
| "TemplateObjectCacheIndex"
|
||||
| "ObjectShapeCacheIndex"
|
||||
| "ObjectPropertyIteratorCacheIndex" => ("u32", 4, 4, "u32"),
|
||||
|
|
|
|||
|
|
@ -201,6 +201,10 @@ fn emit_scalar_field_check(
|
|||
w,
|
||||
" validate_global_variable_cache_index(read_u32(bytes, at + {offset}), ctx)?;"
|
||||
)?,
|
||||
"EnvironmentCoordinateCacheIndex" => writeln!(
|
||||
w,
|
||||
" validate_environment_coordinate_cache_index(read_u32(bytes, at + {offset}), ctx)?;"
|
||||
)?,
|
||||
"TemplateObjectCacheIndex" => writeln!(
|
||||
w,
|
||||
" validate_template_object_cache_index(read_u32(bytes, at + {offset}), ctx)?;"
|
||||
|
|
|
|||
|
|
@ -88,8 +88,14 @@ fn emit_get_binding(
|
|||
) {
|
||||
(Some(cache), true) => generator.emit(Instruction::GetInitializedBinding { dst, identifier, cache }),
|
||||
(Some(cache), false) => generator.emit(Instruction::GetBinding { dst, identifier, cache }),
|
||||
(None, true) => generator.emit(Instruction::DynamicGetInitializedBinding { dst, identifier }),
|
||||
(None, false) => generator.emit(Instruction::DynamicGetBinding { dst, identifier }),
|
||||
(None, true) => {
|
||||
let cache = generator.next_environment_coordinate_cache();
|
||||
generator.emit(Instruction::DynamicGetInitializedBinding { dst, identifier, cache });
|
||||
}
|
||||
(None, false) => {
|
||||
let cache = generator.next_environment_coordinate_cache();
|
||||
generator.emit(Instruction::DynamicGetBinding { dst, identifier, cache });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,10 +113,12 @@ fn emit_get_callee_and_this_from_environment(
|
|||
cache,
|
||||
});
|
||||
} else {
|
||||
let cache = generator.next_environment_coordinate_cache();
|
||||
generator.emit(Instruction::DynamicGetCalleeAndThisFromEnvironment {
|
||||
callee,
|
||||
this_value,
|
||||
identifier,
|
||||
cache,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -119,7 +127,8 @@ fn emit_initialize_lexical_binding(generator: &mut Generator, identifier: Identi
|
|||
if let Some(cache) = generator.environment_coordinate_for_identifier(identifier) {
|
||||
generator.emit(Instruction::InitializeLexicalBinding { identifier, src, cache });
|
||||
} else {
|
||||
generator.emit(Instruction::DynamicInitializeLexicalBinding { identifier, src });
|
||||
let cache = generator.next_environment_coordinate_cache();
|
||||
generator.emit(Instruction::DynamicInitializeLexicalBinding { identifier, src, cache });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +139,8 @@ fn emit_initialize_variable_binding(generator: &mut Generator, identifier: Ident
|
|||
if let Some(cache) = generator.variable_environment_coordinate_for_identifier(identifier) {
|
||||
generator.emit(Instruction::InitializeVariableBinding { identifier, src, cache });
|
||||
} else {
|
||||
generator.emit(Instruction::DynamicInitializeVariableBinding { identifier, src });
|
||||
let cache = generator.next_environment_coordinate_cache();
|
||||
generator.emit(Instruction::DynamicInitializeVariableBinding { identifier, src, cache });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +148,8 @@ fn emit_set_lexical_binding(generator: &mut Generator, identifier: IdentifierTab
|
|||
if let Some(cache) = generator.environment_coordinate_for_identifier(identifier) {
|
||||
generator.emit(Instruction::SetLexicalBinding { identifier, src, cache });
|
||||
} else {
|
||||
generator.emit(Instruction::DynamicSetLexicalBinding { identifier, src });
|
||||
let cache = generator.next_environment_coordinate_cache();
|
||||
generator.emit(Instruction::DynamicSetLexicalBinding { identifier, src, cache });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +159,8 @@ fn emit_set_variable_binding(generator: &mut Generator, identifier: IdentifierTa
|
|||
if let Some(cache) = generator.variable_environment_coordinate_for_identifier(identifier) {
|
||||
generator.emit(Instruction::SetVariableBinding { identifier, src, cache });
|
||||
} else {
|
||||
generator.emit(Instruction::DynamicSetVariableBinding { identifier, src });
|
||||
let cache = generator.next_environment_coordinate_cache();
|
||||
generator.emit(Instruction::DynamicSetVariableBinding { identifier, src, cache });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +168,8 @@ fn emit_typeof_binding(generator: &mut Generator, dst: Operand, identifier: Iden
|
|||
if let Some(cache) = generator.environment_coordinate_for_identifier(identifier) {
|
||||
generator.emit(Instruction::TypeofBinding { dst, identifier, cache });
|
||||
} else {
|
||||
generator.emit(Instruction::DynamicTypeofBinding { dst, identifier });
|
||||
let cache = generator.next_environment_coordinate_cache();
|
||||
generator.emit(Instruction::DynamicTypeofBinding { dst, identifier, cache });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ pub struct FFIExecutableData {
|
|||
pub local_variable_count: usize,
|
||||
pub property_lookup_cache_count: u32,
|
||||
pub global_variable_cache_count: u32,
|
||||
pub environment_coordinate_cache_count: u32,
|
||||
pub template_object_cache_count: u32,
|
||||
pub object_shape_cache_count: u32,
|
||||
pub object_property_iterator_cache_count: u32,
|
||||
|
|
@ -700,6 +701,7 @@ pub struct ExecutableParts<'a> {
|
|||
pub struct ExecutableMetadata {
|
||||
pub property_lookup_cache_count: u32,
|
||||
pub global_variable_cache_count: u32,
|
||||
pub environment_coordinate_cache_count: u32,
|
||||
pub template_object_cache_count: u32,
|
||||
pub object_shape_cache_count: u32,
|
||||
pub object_property_iterator_cache_count: u32,
|
||||
|
|
@ -781,6 +783,7 @@ pub unsafe fn create_executable_from_slices(
|
|||
local_variable_count: slices.local_variable_names.len(),
|
||||
property_lookup_cache_count: metadata.property_lookup_cache_count,
|
||||
global_variable_cache_count: metadata.global_variable_cache_count,
|
||||
environment_coordinate_cache_count: metadata.environment_coordinate_cache_count,
|
||||
template_object_cache_count: metadata.template_object_cache_count,
|
||||
object_shape_cache_count: metadata.object_shape_cache_count,
|
||||
object_property_iterator_cache_count: metadata.object_property_iterator_cache_count,
|
||||
|
|
@ -850,6 +853,7 @@ pub unsafe fn create_executable_with_dependencies_from_parts(
|
|||
let metadata = ExecutableMetadata {
|
||||
property_lookup_cache_count: generator.next_property_lookup_cache,
|
||||
global_variable_cache_count: generator.next_global_variable_cache,
|
||||
environment_coordinate_cache_count: generator.next_environment_coordinate_cache,
|
||||
template_object_cache_count: generator.next_template_object_cache,
|
||||
object_shape_cache_count: generator.next_object_shape_cache,
|
||||
object_property_iterator_cache_count: generator.next_object_property_iterator_cache,
|
||||
|
|
|
|||
|
|
@ -259,6 +259,7 @@ pub struct Generator {
|
|||
// --- Various counters ---
|
||||
pub next_property_lookup_cache: u32,
|
||||
pub next_global_variable_cache: u32,
|
||||
pub next_environment_coordinate_cache: u32,
|
||||
pub next_template_object_cache: u32,
|
||||
pub next_object_shape_cache: u32,
|
||||
pub next_object_property_iterator_cache: u32,
|
||||
|
|
@ -424,6 +425,7 @@ impl Generator {
|
|||
current_finally_context: None,
|
||||
next_property_lookup_cache: 0,
|
||||
next_global_variable_cache: 0,
|
||||
next_environment_coordinate_cache: 0,
|
||||
next_template_object_cache: 0,
|
||||
next_object_shape_cache: 0,
|
||||
next_object_property_iterator_cache: 0,
|
||||
|
|
@ -968,6 +970,7 @@ impl Generator {
|
|||
|
||||
next_cache_method!(next_property_lookup_cache, next_property_lookup_cache);
|
||||
next_cache_method!(next_global_variable_cache, next_global_variable_cache);
|
||||
next_cache_method!(next_environment_coordinate_cache, next_environment_coordinate_cache);
|
||||
next_cache_method!(next_template_object_cache, next_template_object_cache);
|
||||
next_cache_method!(next_object_shape_cache, next_object_shape_cache);
|
||||
next_cache_method!(next_object_property_iterator_cache, next_object_property_iterator_cache);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ pub struct FFIValidatorBounds {
|
|||
pub regex_table_size: u32,
|
||||
pub property_lookup_cache_count: u32,
|
||||
pub global_variable_cache_count: u32,
|
||||
pub environment_coordinate_cache_count: u32,
|
||||
pub template_object_cache_count: u32,
|
||||
pub object_shape_cache_count: u32,
|
||||
pub object_property_iterator_cache_count: u32,
|
||||
|
|
@ -88,6 +89,7 @@ pub enum ValidationErrorKind {
|
|||
ExceptionHandlerHandlerInvalid = 24,
|
||||
ExceptionHandlerRangeInvalid = 25,
|
||||
SourceMapOffsetInvalid = 26,
|
||||
EnvironmentCoordinateCacheIndexOutOfRange = 27,
|
||||
}
|
||||
|
||||
/// Detail returned to the C++ caller on validation failure.
|
||||
|
|
@ -257,6 +259,17 @@ pub fn validate_global_variable_cache_index(raw: u32, ctx: &ValidationContext) -
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn validate_environment_coordinate_cache_index(
|
||||
raw: u32,
|
||||
ctx: &ValidationContext,
|
||||
) -> Result<(), ValidationErrorKind> {
|
||||
if raw >= ctx.bounds.environment_coordinate_cache_count {
|
||||
return Err(ValidationErrorKind::EnvironmentCoordinateCacheIndexOutOfRange);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn validate_template_object_cache_index(raw: u32, ctx: &ValidationContext) -> Result<(), ValidationErrorKind> {
|
||||
if raw == NO_CACHE_INDEX {
|
||||
|
|
@ -495,6 +508,7 @@ mod tests {
|
|||
regex_table_size: 0,
|
||||
property_lookup_cache_count: 4,
|
||||
global_variable_cache_count: 4,
|
||||
environment_coordinate_cache_count: 4,
|
||||
template_object_cache_count: 4,
|
||||
object_shape_cache_count: 4,
|
||||
object_property_iterator_cache_count: 4,
|
||||
|
|
@ -693,6 +707,21 @@ mod tests {
|
|||
assert_eq!(err.kind, ValidationErrorKind::ObjectShapeCacheIndexOutOfRange);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_environment_coordinate_cache_sentinel() {
|
||||
// DynamicGetBinding layout: header(2) + pad(2) + m_dst(4)
|
||||
// + m_identifier(4) + m_cache(4) = 16 bytes.
|
||||
let mut bytes = [0u8; 16];
|
||||
bytes[0] = OpCode::DynamicGetBinding as u8;
|
||||
// m_dst and m_identifier stay at 0.
|
||||
// m_cache at offset 12: dynamic environment opcodes always index this
|
||||
// cache, so the no-cache sentinel must not validate.
|
||||
put_u32(&mut bytes, 12, NO_CACHE_INDEX);
|
||||
|
||||
let err = validate(&bytes, &permissive_bounds()).unwrap_err();
|
||||
assert_eq!(err.kind, ValidationErrorKind::EnvironmentCoordinateCacheIndexOutOfRange);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_enum_out_of_range() {
|
||||
// SetCompletionType layout: header(2) + pad(2) + m_completion(4) + m_completion_type(4) = 12, padded to 16.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use crate::bytecode::validator::{
|
|||
use crate::{CompiledProgram, CompiledProgramBytecode, ModuleCallbacks, ast, u32_from_usize};
|
||||
|
||||
const MAGIC: &[u8; 8] = b"LBJSBC\0\0";
|
||||
const FORMAT_VERSION: u32 = 10;
|
||||
const FORMAT_VERSION: u32 = 11;
|
||||
const SOURCE_HASH_SIZE: usize = 32;
|
||||
const BYTECODE_ALIGNMENT: usize = 8;
|
||||
const COMPLETION_TYPE_VARIANT_COUNT: u32 = 6;
|
||||
|
|
@ -1229,6 +1229,7 @@ unsafe fn materialize_executable(
|
|||
crate::bytecode::ffi::ExecutableMetadata {
|
||||
property_lookup_cache_count: cache_counters.property_lookup_cache_count,
|
||||
global_variable_cache_count: cache_counters.global_variable_cache_count,
|
||||
environment_coordinate_cache_count: cache_counters.environment_coordinate_cache_count,
|
||||
template_object_cache_count: cache_counters.template_object_cache_count,
|
||||
object_shape_cache_count: cache_counters.object_shape_cache_count,
|
||||
object_property_iterator_cache_count: cache_counters.object_property_iterator_cache_count,
|
||||
|
|
@ -2391,6 +2392,7 @@ impl DecodedExecutableRecord {
|
|||
regex_table_size: 0,
|
||||
property_lookup_cache_count: self.cache_counters.property_lookup_cache_count,
|
||||
global_variable_cache_count: self.cache_counters.global_variable_cache_count,
|
||||
environment_coordinate_cache_count: self.cache_counters.environment_coordinate_cache_count,
|
||||
template_object_cache_count: self.cache_counters.template_object_cache_count,
|
||||
object_shape_cache_count: self.cache_counters.object_shape_cache_count,
|
||||
object_property_iterator_cache_count: self.cache_counters.object_property_iterator_cache_count,
|
||||
|
|
@ -2477,6 +2479,7 @@ impl Encode for CacheCounters<'_> {
|
|||
fn encode(&self, encoder: &mut Encoder) {
|
||||
self.0.next_property_lookup_cache.encode(encoder);
|
||||
self.0.next_global_variable_cache.encode(encoder);
|
||||
self.0.next_environment_coordinate_cache.encode(encoder);
|
||||
self.0.next_template_object_cache.encode(encoder);
|
||||
self.0.next_object_shape_cache.encode(encoder);
|
||||
self.0.next_object_property_iterator_cache.encode(encoder);
|
||||
|
|
@ -2488,6 +2491,7 @@ impl CacheCounters<'_> {
|
|||
Some(DecodedCacheCounters {
|
||||
property_lookup_cache_count: u32::decode(decoder)?,
|
||||
global_variable_cache_count: u32::decode(decoder)?,
|
||||
environment_coordinate_cache_count: u32::decode(decoder)?,
|
||||
template_object_cache_count: u32::decode(decoder)?,
|
||||
object_shape_cache_count: u32::decode(decoder)?,
|
||||
object_property_iterator_cache_count: u32::decode(decoder)?,
|
||||
|
|
@ -2498,6 +2502,7 @@ impl CacheCounters<'_> {
|
|||
struct DecodedCacheCounters {
|
||||
property_lookup_cache_count: u32,
|
||||
global_variable_cache_count: u32,
|
||||
environment_coordinate_cache_count: u32,
|
||||
template_object_cache_count: u32,
|
||||
object_shape_cache_count: u32,
|
||||
object_property_iterator_cache_count: u32,
|
||||
|
|
@ -2507,6 +2512,7 @@ impl DecodedCacheCounters {
|
|||
fn validate(&self) {
|
||||
let _ = self.property_lookup_cache_count
|
||||
+ self.global_variable_cache_count
|
||||
+ self.environment_coordinate_cache_count
|
||||
+ self.template_object_cache_count
|
||||
+ self.object_shape_cache_count
|
||||
+ self.object_property_iterator_cache_count;
|
||||
|
|
@ -3568,6 +3574,9 @@ mod tests {
|
|||
}
|
||||
|
||||
unsafe extern "C" fn ignore_foreign_owner(_: *mut c_void) {}
|
||||
unsafe extern "C" fn ignore_clone_foreign_owner(_: *const c_void) -> *mut c_void {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn utf16_decode_borrows_from_foreign_blob() {
|
||||
|
|
@ -3581,6 +3590,7 @@ mod tests {
|
|||
&bytes,
|
||||
Some(ForeignBytecodeCacheBlobOwner {
|
||||
owner: std::ptr::null_mut(),
|
||||
clone_owner: ignore_clone_foreign_owner,
|
||||
free_owner: ignore_foreign_owner,
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1076,6 +1076,7 @@ extern "C" void* rust_create_executable(
|
|||
source_code,
|
||||
data->property_lookup_cache_count,
|
||||
data->global_variable_cache_count,
|
||||
data->environment_coordinate_cache_count,
|
||||
data->template_object_cache_count,
|
||||
data->object_shape_cache_count,
|
||||
data->object_property_iterator_cache_count,
|
||||
|
|
|
|||
|
|
@ -349,6 +349,7 @@ NUMERIC_TYPES = {
|
|||
CACHE_INDEX_TYPES = {
|
||||
"PropertyLookupCacheIndex",
|
||||
"GlobalVariableCacheIndex",
|
||||
"EnvironmentCoordinateCacheIndex",
|
||||
"TemplateObjectCacheIndex",
|
||||
"ObjectShapeCacheIndex",
|
||||
"ObjectPropertyIteratorCacheIndex",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ block0:
|
|||
[ 60] End value:reg6
|
||||
|
||||
|
||||
with_eval$361b5eed eval-prevents-locals.js:6:5
|
||||
with_eval$b4b96665 eval-prevents-locals.js:6:5
|
||||
Registers: 9
|
||||
Blocks: 1
|
||||
Constants:
|
||||
|
|
@ -26,9 +26,9 @@ block0:
|
|||
[ 30] CreateVariable `x`, is_immutable:false, is_global:false, is_strict:false
|
||||
[ 40] InitializeLexicalBinding `x`, src:Int32(1)
|
||||
[ 58] DynamicGetCalleeAndThisFromEnvironment callee:reg7, this_value:reg8, `eval`
|
||||
[ 68] CallDirectEval dst:reg6, callee:reg7, this_value:reg8, eval, arguments:[String("")]
|
||||
[ 90] GetBinding dst:reg6, `x`
|
||||
[ a8] Return value:reg6
|
||||
[ 70] CallDirectEval dst:reg6, callee:reg7, this_value:reg8, eval, arguments:[String("")]
|
||||
[ 98] GetBinding dst:reg6, `x`
|
||||
[ b0] Return value:reg6
|
||||
|
||||
|
||||
eval$b72141f3
|
||||
|
|
@ -41,7 +41,7 @@ block0:
|
|||
[ 0] End value:Undefined
|
||||
|
||||
|
||||
outer_eval$e6231df2 eval-prevents-locals.js:14:9
|
||||
outer_eval$6785167a eval-prevents-locals.js:14:9
|
||||
Registers: 8
|
||||
Blocks: 1
|
||||
Constants:
|
||||
|
|
@ -55,10 +55,10 @@ block0:
|
|||
[ 38] NewFunction dst:reg5, shared_function_data_index:0
|
||||
[ 50] SetVariableBinding `inner`, src:reg5
|
||||
[ 68] DynamicGetCalleeAndThisFromEnvironment callee:reg6, this_value:reg7, `eval`
|
||||
[ 78] CallDirectEval dst:reg5, callee:reg6, this_value:reg7, eval, arguments:[String("")]
|
||||
[ a0] GetCalleeAndThisFromEnvironment callee:reg6, this_value:reg7, `inner`
|
||||
[ b8] Call dst:reg5, callee:reg6, this_value:reg7, inner
|
||||
[ d8] Return value:reg5
|
||||
[ 80] CallDirectEval dst:reg5, callee:reg6, this_value:reg7, eval, arguments:[String("")]
|
||||
[ a8] GetCalleeAndThisFromEnvironment callee:reg6, this_value:reg7, `inner`
|
||||
[ c0] Call dst:reg5, callee:reg6, this_value:reg7, inner
|
||||
[ e0] Return value:reg5
|
||||
|
||||
|
||||
eval$b72141f3
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ block0:
|
|||
[ 30] End value:reg5
|
||||
|
||||
|
||||
foo$bf97ec0c eval-same-function.js:6:9
|
||||
foo$3e35f384 eval-same-function.js:6:9
|
||||
Registers: 8
|
||||
Blocks: 1
|
||||
Constants:
|
||||
|
|
@ -21,10 +21,10 @@ foo$bf97ec0c eval-same-function.js:6:9
|
|||
block0:
|
||||
[ 0] CreateArguments is_immutable:false
|
||||
[ 10] DynamicGetCalleeAndThisFromEnvironment callee:reg6, this_value:reg7, `eval`
|
||||
[ 20] CallDirectEval dst:reg5, callee:reg6, this_value:reg7, eval, arguments:[String("var x = 1")]
|
||||
[ 48] DynamicGetBinding dst:reg6, `Number`
|
||||
[ 58] CallConstruct dst:reg5, callee:reg6, Number, arguments:[Int32(42)]
|
||||
[ 78] Return value:reg5
|
||||
[ 28] CallDirectEval dst:reg5, callee:reg6, this_value:reg7, eval, arguments:[String("var x = 1")]
|
||||
[ 50] DynamicGetBinding dst:reg6, `Number`
|
||||
[ 60] CallConstruct dst:reg5, callee:reg6, Number, arguments:[Int32(42)]
|
||||
[ 80] Return value:reg5
|
||||
|
||||
|
||||
eval$d8e83433 line 1, column 1
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ block0:
|
|||
[ 30] End value:reg5
|
||||
|
||||
|
||||
f$20dcebd6 var-env-capacity-with-param-expressions.js:3:9
|
||||
f$a23ee45e var-env-capacity-with-param-expressions.js:3:9
|
||||
Registers: 10
|
||||
Blocks: 3
|
||||
Constants:
|
||||
|
|
@ -42,8 +42,8 @@ block2:
|
|||
[ 108] CreateVariable `c`, is_immutable:false, is_global:false, is_strict:false
|
||||
[ 118] InitializeVariableBinding `c`, src:reg7
|
||||
[ 130] DynamicGetCalleeAndThisFromEnvironment callee:reg8, this_value:reg9, `eval`
|
||||
[ 140] CallDirectEval dst:reg7, callee:reg8, this_value:reg9, eval, arguments:[String("")]
|
||||
[ 168] End value:Undefined
|
||||
[ 148] CallDirectEval dst:reg7, callee:reg8, this_value:reg9, eval, arguments:[String("")]
|
||||
[ 170] End value:Undefined
|
||||
|
||||
|
||||
eval$b72141f3
|
||||
|
|
|
|||
|
|
@ -43,3 +43,18 @@ test("restores lexical environment even when exception is thrown", () => {
|
|||
}
|
||||
expect(() => foo).toThrowWithMessage(ReferenceError, "'foo' is not defined");
|
||||
});
|
||||
|
||||
test("with object changes can shadow an outer binding", () => {
|
||||
let outer = "outer";
|
||||
let object = {};
|
||||
let seen = [];
|
||||
|
||||
with (object) {
|
||||
for (let i = 0; i < 2; ++i) {
|
||||
seen.push(outer);
|
||||
object.outer = "object";
|
||||
}
|
||||
}
|
||||
|
||||
expect(seen).toEqual(["outer", "object"]);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue