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

LibJS/Bytecode: Do not coerce the receiver to Object for internal_set

This makes the behavior of `Symbol` correct in strict mode, wherein if
the receiver is a symbol primitive, assigning new properties should
throw a TypeError.
This commit is contained in:
Daniel Bertalan 2023-07-02 20:33:58 +02:00 committed by Linus Groh
parent 0cd85ab0fc
commit d165590809
2 changed files with 16 additions and 7 deletions

View file

@ -48,8 +48,9 @@ DeprecatedString Instruction::to_deprecated_string(Bytecode::Executable const& e
namespace JS::Bytecode::Op { namespace JS::Bytecode::Op {
static ThrowCompletionOr<void> put_by_property_key(VM& vm, Object* object, Value value, PropertyKey name, PropertyKind kind) static ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value value, PropertyKey name, PropertyKind kind)
{ {
auto object = TRY(base.to_object(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());
@ -70,9 +71,9 @@ static ThrowCompletionOr<void> put_by_property_key(VM& vm, Object* object, Value
break; break;
} }
case PropertyKind::KeyValue: { case PropertyKind::KeyValue: {
bool succeeded = TRY(object->internal_set(name, value, object)); bool succeeded = TRY(object->internal_set(name, value, base));
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, value.to_string_without_side_effects())); return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, TRY_OR_THROW_OOM(vm, base.to_string_without_side_effects()));
break; break;
} }
case PropertyKind::Spread: case PropertyKind::Spread:
@ -548,9 +549,9 @@ ThrowCompletionOr<void> PutById::execute_impl(Bytecode::Interpreter& interpreter
auto& vm = interpreter.vm(); auto& vm = interpreter.vm();
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it. // NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter.accumulator(); auto value = interpreter.accumulator();
auto object = TRY(interpreter.reg(m_base).to_object(vm)); auto base = interpreter.reg(m_base);
PropertyKey name = interpreter.current_executable().get_identifier(m_property); PropertyKey name = interpreter.current_executable().get_identifier(m_property);
TRY(put_by_property_key(vm, object, value, name, m_kind)); TRY(put_by_property_key(vm, base, value, name, m_kind));
interpreter.accumulator() = value; interpreter.accumulator() = value;
return {}; return {};
} }
@ -1017,10 +1018,10 @@ ThrowCompletionOr<void> PutByValue::execute_impl(Bytecode::Interpreter& interpre
// NOTE: Get the value from the accumulator before side effects have a chance to overwrite it. // NOTE: Get the value from the accumulator before side effects have a chance to overwrite it.
auto value = interpreter.accumulator(); auto value = interpreter.accumulator();
auto object = TRY(interpreter.reg(m_base).to_object(vm)); auto base = interpreter.reg(m_base);
auto property_key = TRY(interpreter.reg(m_property).to_property_key(vm)); auto property_key = TRY(interpreter.reg(m_property).to_property_key(vm));
TRY(put_by_property_key(vm, object, value, property_key, m_kind)); TRY(put_by_property_key(vm, base, value, property_key, m_kind));
interpreter.accumulator() = value; interpreter.accumulator() = value;
return {}; return {};
} }

View file

@ -17,3 +17,11 @@ test("constructing symbol from symbol is an error", () => {
Symbol(Symbol("foo")); Symbol(Symbol("foo"));
}).toThrowWithMessage(TypeError, "Cannot convert symbol to string"); }).toThrowWithMessage(TypeError, "Cannot convert symbol to string");
}); });
test("setting new properties on a symbol is an error in strict mode", () => {
"use strict";
var symbol = Symbol("foo");
expect(() => {
symbol.bar = 42;
}).toThrowWithMessage(TypeError, "Cannot set property 'bar' of Symbol(foo)");
});