1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 02:47:34 +00:00

LibJS/Bytecode: Extract accumulator value before incurring side effects

Many operations in JavaScript may incur side effects, including calling
arbitrary user code. Since the user code will clobber the accumulator,
we have to take care to extract anything we need from the accumulator
before doing anything that may have side effects.

Fixes 3 test262 tests. :^)
This commit is contained in:
Andreas Kling 2023-06-17 09:37:12 +02:00
parent c30775522e
commit 82828ad936

View file

@ -48,10 +48,8 @@ DeprecatedString Instruction::to_deprecated_string(Bytecode::Executable const& e
namespace JS::Bytecode::Op { namespace JS::Bytecode::Op {
static ThrowCompletionOr<void> put_by_property_key(Object* object, Value value, PropertyKey name, Bytecode::Interpreter& interpreter, PropertyKind kind) static ThrowCompletionOr<void> put_by_property_key(VM& vm, Object* object, Value value, PropertyKey name, PropertyKind kind)
{ {
auto& vm = interpreter.vm();
if (kind == PropertyKind::Getter || kind == PropertyKind::Setter) { if (kind == PropertyKind::Getter || kind == PropertyKind::Setter) {
// The generator should only pass us functions for getters and setters. // The generator should only pass us functions for getters and setters.
VERIFY(value.is_function()); VERIFY(value.is_function());
@ -72,9 +70,9 @@ static ThrowCompletionOr<void> put_by_property_key(Object* object, Value value,
break; break;
} }
case PropertyKind::KeyValue: { case PropertyKind::KeyValue: {
bool succeeded = TRY(object->internal_set(name, interpreter.accumulator(), object)); bool succeeded = TRY(object->internal_set(name, value, object));
if (!succeeded && vm.in_strict_mode()) if (!succeeded && vm.in_strict_mode())
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, TRY_OR_THROW_OOM(vm, interpreter.accumulator().to_string_without_side_effects())); return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, TRY_OR_THROW_OOM(vm, value.to_string_without_side_effects()));
break; break;
} }
case PropertyKind::Spread: case PropertyKind::Spread:
@ -517,10 +515,11 @@ ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter
ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter.accumulator();
auto object = TRY(interpreter.reg(m_base).to_object(vm)); auto object = TRY(interpreter.reg(m_base).to_object(vm));
PropertyKey name = interpreter.current_executable().get_identifier(m_property); PropertyKey name = interpreter.current_executable().get_identifier(m_property);
auto value = interpreter.accumulator(); return put_by_property_key(vm, object, value, name, m_kind);
return put_by_property_key(object, value, name, interpreter, m_kind);
} }
ThrowCompletionOr<void> DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const
@ -903,9 +902,13 @@ void Yield::replace_references_impl(BasicBlock const& from, BasicBlock const& to
ThrowCompletionOr<void> GetByValue::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> GetByValue::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
// NOTE: Get the property key from the accumulator before side effects have a chance to overwrite it.
auto property_key_value = interpreter.accumulator();
auto object = TRY(interpreter.reg(m_base).to_object(vm)); auto object = TRY(interpreter.reg(m_base).to_object(vm));
auto property_key = TRY(interpreter.accumulator().to_property_key(vm)); auto property_key = TRY(property_key_value.to_property_key(vm));
interpreter.accumulator() = TRY(object->get(property_key)); interpreter.accumulator() = TRY(object->get(property_key));
return {}; return {};
@ -914,17 +917,25 @@ ThrowCompletionOr<void> GetByValue::execute_impl(Bytecode::Interpreter& interpre
ThrowCompletionOr<void> PutByValue::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> PutByValue::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter.accumulator();
auto object = TRY(interpreter.reg(m_base).to_object(vm)); auto object = TRY(interpreter.reg(m_base).to_object(vm));
auto property_key = TRY(interpreter.reg(m_property).to_property_key(vm)); auto property_key = TRY(interpreter.reg(m_property).to_property_key(vm));
return put_by_property_key(object, interpreter.accumulator(), property_key, interpreter, m_kind); return put_by_property_key(vm, object, value, property_key, m_kind);
} }
ThrowCompletionOr<void> DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> DeleteByValue::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
// NOTE: Get the property key from the accumulator before side effects have a chance to overwrite it.
auto property_key_value = interpreter.accumulator();
auto object = TRY(interpreter.reg(m_base).to_object(vm)); auto object = TRY(interpreter.reg(m_base).to_object(vm));
auto property_key = TRY(interpreter.accumulator().to_property_key(vm)); auto property_key = TRY(property_key_value.to_property_key(vm));
bool strict = vm.in_strict_mode(); bool strict = vm.in_strict_mode();
auto reference = Reference { object, property_key, {}, strict }; auto reference = Reference { object, property_key, {}, strict };
interpreter.accumulator() = Value(TRY(reference.delete_(vm))); interpreter.accumulator() = Value(TRY(reference.delete_(vm)));