1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 22:07:35 +00:00

LibJS: Move most of Interpreter into VM

This patch moves the exception state, call stack and scope stack from
Interpreter to VM. I'm doing this to help myself discover what the
split between Interpreter and VM should be, by shuffling things around
and seeing what falls where.

With these changes, we no longer have a persistent lexical environment
for the current global object on the Interpreter's call stack. Instead,
we push/pop that environment on Interpreter::run() enter/exit.
Since it should only be used to find the global "this", and not for
variable storage (that goes directly into the global object instead!),
I had to insert some short-circuiting when walking the environment
parent chain during variable lookup.

Note that this is a "stepping stone" commit, not a final design.
This commit is contained in:
Andreas Kling 2020-09-27 15:18:55 +02:00
parent 838d9fa251
commit 6861c619c6
48 changed files with 765 additions and 726 deletions

View file

@ -55,7 +55,7 @@ Array* Array::typed_this(Interpreter& interpreter, GlobalObject& global_object)
if (!this_object)
return {};
if (!this_object->is_array()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAn, "Array");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAn, "Array");
return nullptr;
}
return static_cast<Array*>(this_object);
@ -78,7 +78,7 @@ JS_DEFINE_NATIVE_SETTER(Array::length_setter)
if (interpreter.exception())
return;
if (length.is_nan() || length.is_infinity() || length.as_double() < 0) {
interpreter.throw_exception<RangeError>(ErrorType::ArrayInvalidLength);
interpreter.vm().throw_exception<RangeError>(global_object, ErrorType::ArrayInvalidLength);
return;
}
array->indexed_properties().set_array_like_size(length.as_double());

View file

@ -67,7 +67,7 @@ Value ArrayConstructor::call(Interpreter& interpreter)
if (interpreter.argument_count() == 1 && interpreter.argument(0).is_number()) {
auto array_length_value = interpreter.argument(0);
if (!array_length_value.is_integer() || array_length_value.as_i32() < 0) {
interpreter.throw_exception<TypeError>(ErrorType::ArrayInvalidLength);
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ArrayInvalidLength);
return {};
}
auto* array = Array::create(global_object());

View file

@ -54,7 +54,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayIteratorPrototype::next)
{
auto this_value = interpreter.this_value(global_object);
if (!this_value.is_object() || !this_value.as_object().is_array_iterator_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAn, "Array Iterator");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAn, "Array Iterator");
return {};
}
auto& this_object = this_value.as_object();

View file

@ -90,12 +90,12 @@ ArrayPrototype::~ArrayPrototype()
static Function* callback_from_args(Interpreter& interpreter, const String& name)
{
if (interpreter.argument_count() < 1) {
interpreter.throw_exception<TypeError>(ErrorType::ArrayPrototypeOneArg, name.characters());
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::ArrayPrototypeOneArg, name.characters());
return nullptr;
}
auto callback = interpreter.argument(0);
if (!callback.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, callback.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::NotAFunction, callback.to_string_without_side_effects().characters());
return nullptr;
}
return &callback.as_function();
@ -199,7 +199,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::push)
auto argument_count = interpreter.argument_count();
auto new_length = length + argument_count;
if (new_length > MAX_ARRAY_LIKE_INDEX) {
interpreter.throw_exception<TypeError>(ErrorType::ArrayMaxSize);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
return {};
}
for (size_t i = 0; i < argument_count; ++i) {
@ -474,7 +474,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reduce)
start += 1;
}
if (!start_found) {
interpreter.throw_exception<TypeError>(ErrorType::ReduceNoInitial);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ReduceNoInitial);
return {};
}
}
@ -527,7 +527,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reduce_right)
start -= 1;
}
if (!start_found) {
interpreter.throw_exception<TypeError>(ErrorType::ReduceNoInitial);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ReduceNoInitial);
return {};
}
}
@ -729,7 +729,7 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::splice)
size_t new_length = initial_length + insert_count - actual_delete_count;
if (new_length > MAX_ARRAY_LIKE_INDEX) {
interpreter.throw_exception<TypeError>(ErrorType::ArrayMaxSize);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
return {};
}

View file

@ -61,7 +61,7 @@ Value BigIntConstructor::call(Interpreter& interpreter)
return {};
if (primitive.is_number()) {
if (!primitive.is_integer()) {
interpreter.throw_exception<RangeError>(ErrorType::BigIntIntArgument);
interpreter.vm().throw_exception<RangeError>(global_object(), ErrorType::BigIntIntArgument);
return {};
}
return js_bigint(interpreter, Crypto::SignedBigInteger { primitive.as_i32() });
@ -74,7 +74,7 @@ Value BigIntConstructor::call(Interpreter& interpreter)
Value BigIntConstructor::construct(Interpreter& interpreter, Function&)
{
interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, "BigInt");
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, "BigInt");
return {};
}

View file

@ -58,7 +58,7 @@ static BigIntObject* bigint_object_from(Interpreter& interpreter, GlobalObject&
if (!this_object)
return nullptr;
if (!this_object->is_bigint_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "BigInt");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "BigInt");
return nullptr;
}
return static_cast<BigIntObject*>(this_object);

View file

@ -55,7 +55,7 @@ JS_DEFINE_NATIVE_FUNCTION(BooleanPrototype::to_string)
return js_string(interpreter.heap(), this_object.as_bool() ? "true" : "false");
}
if (!this_object.is_object() || !this_object.as_object().is_boolean_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Boolean");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Boolean");
return {};
}
@ -70,7 +70,7 @@ JS_DEFINE_NATIVE_FUNCTION(BooleanPrototype::value_of)
return this_object;
}
if (!this_object.is_object() || !this_object.as_object().is_boolean_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Boolean");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Boolean");
return {};
}

View file

@ -42,7 +42,7 @@ static Date* typed_this(Interpreter& interpreter, GlobalObject& global_object)
if (!this_object)
return nullptr;
if (!this_object->is_date()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Date");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Date");
return nullptr;
}
return static_cast<Date*>(this_object);

View file

@ -59,7 +59,7 @@ JS_DEFINE_NATIVE_GETTER(ErrorPrototype::name_getter)
if (!this_object)
return {};
if (!this_object->is_error()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAn, "Error");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
return {};
}
return js_string(interpreter, static_cast<const Error*>(this_object)->name());
@ -71,7 +71,7 @@ JS_DEFINE_NATIVE_SETTER(ErrorPrototype::name_setter)
if (!this_object)
return;
if (!this_object->is_error()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAn, "Error");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
return;
}
auto name = value.to_string(interpreter);
@ -86,7 +86,7 @@ JS_DEFINE_NATIVE_GETTER(ErrorPrototype::message_getter)
if (!this_object)
return {};
if (!this_object->is_error()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAn, "Error");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
return {};
}
return js_string(interpreter, static_cast<const Error*>(this_object)->message());
@ -95,7 +95,7 @@ JS_DEFINE_NATIVE_GETTER(ErrorPrototype::message_getter)
JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::to_string)
{
if (!interpreter.this_value(global_object).is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAnObject, interpreter.this_value(global_object).to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAnObject, interpreter.this_value(global_object).to_string_without_side_effects().characters());
return {};
}
auto& this_object = interpreter.this_value(global_object).as_object();

View file

@ -32,7 +32,7 @@ namespace JS {
Exception::Exception(Value value)
: m_value(value)
{
auto& call_stack = interpreter().call_stack();
auto& call_stack = vm().call_stack();
for (ssize_t i = call_stack.size() - 1; i >= 0; --i) {
auto function_name = call_stack[i].function_name;
if (function_name.is_empty())

View file

@ -26,6 +26,7 @@
#pragma once
#include <AK/Vector.h>
#include <LibJS/Runtime/Cell.h>
#include <LibJS/Runtime/Value.h>

View file

@ -84,7 +84,7 @@ Value FunctionConstructor::construct(Interpreter& interpreter, Function&)
auto function_expression = parser.parse_function_node<FunctionExpression>();
if (parser.has_errors()) {
auto error = parser.errors()[0];
interpreter.throw_exception<SyntaxError>(error.to_string());
interpreter.vm().throw_exception<SyntaxError>(global_object(), error.to_string());
return {};
}
return function_expression->execute(interpreter, global_object());

View file

@ -66,7 +66,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::apply)
if (!this_object)
return {};
if (!this_object->is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Function");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
auto& function = static_cast<Function&>(*this_object);
@ -75,7 +75,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::apply)
if (arg_array.is_null() || arg_array.is_undefined())
return interpreter.call(function, this_arg);
if (!arg_array.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::FunctionArgsNotObject);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::FunctionArgsNotObject);
return {};
}
auto length_property = arg_array.as_object().get("length");
@ -100,7 +100,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::bind)
if (!this_object)
return {};
if (!this_object->is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Function");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
auto& this_function = static_cast<Function&>(*this_object);
@ -121,7 +121,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::call)
if (!this_object)
return {};
if (!this_object->is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Function");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
auto& function = static_cast<Function&>(*this_object);
@ -140,7 +140,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::to_string)
if (!this_object)
return {};
if (!this_object->is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Function");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
String function_name = static_cast<Function*>(this_object)->name();
@ -182,7 +182,7 @@ JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::symbol_has_instance)
if (!this_object)
return {};
if (!this_object->is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Function");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
return {};
}
return ordinary_has_instance(interpreter, interpreter.argument(0), this_object);

View file

@ -46,14 +46,14 @@ Object* get_iterator(GlobalObject& global_object, Value value, String hint, Valu
return {};
}
if (!method.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotIterable, value.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotIterable, value.to_string_without_side_effects().characters());
return nullptr;
}
auto iterator = interpreter.call(method.as_function(), value);
if (interpreter.exception())
return {};
if (!iterator.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotIterable, value.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotIterable, value.to_string_without_side_effects().characters());
return nullptr;
}
return &iterator.as_object();
@ -62,12 +62,13 @@ Object* get_iterator(GlobalObject& global_object, Value value, String hint, Valu
Object* iterator_next(Object& iterator, Value value)
{
auto& interpreter = iterator.interpreter();
auto& global_object = iterator.global_object();
auto next_method = iterator.get("next");
if (interpreter.exception())
return {};
if (!next_method.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::IterableNextNotAFunction);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::IterableNextNotAFunction);
return nullptr;
}
@ -80,7 +81,7 @@ Object* iterator_next(Object& iterator, Value value)
if (interpreter.exception())
return {};
if (!result.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::IterableNextBadReturn);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::IterableNextBadReturn);
return nullptr;
}

View file

@ -192,14 +192,14 @@ String JSONObject::serialize_json_property(Interpreter& interpreter, StringifySt
return serialize_json_object(interpreter, state, value.as_object());
}
if (value.is_bigint())
interpreter.throw_exception<TypeError>(ErrorType::JsonBigInt);
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::JsonBigInt);
return {};
}
String JSONObject::serialize_json_object(Interpreter& interpreter, StringifyState& state, Object& object)
{
if (state.seen_objects.contains(&object)) {
interpreter.throw_exception<TypeError>(ErrorType::JsonCircular);
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::JsonCircular);
return {};
}
@ -282,7 +282,7 @@ String JSONObject::serialize_json_object(Interpreter& interpreter, StringifyStat
String JSONObject::serialize_json_array(Interpreter& interpreter, StringifyState& state, Object& object)
{
if (state.seen_objects.contains(&object)) {
interpreter.throw_exception<TypeError>(ErrorType::JsonCircular);
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::JsonCircular);
return {};
}
@ -394,7 +394,7 @@ JS_DEFINE_NATIVE_FUNCTION(JSONObject::parse)
auto json = JsonValue::from_string(string);
if (!json.has_value()) {
interpreter.throw_exception<SyntaxError>(ErrorType::JsonMalformed);
interpreter.vm().throw_exception<SyntaxError>(global_object, ErrorType::JsonMalformed);
return {};
}
Value result = parse_json_value(interpreter, global_object, json.value());

View file

@ -27,6 +27,7 @@
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/LexicalEnvironment.h>
#include <LibJS/Runtime/Value.h>
@ -72,12 +73,16 @@ void LexicalEnvironment::visit_children(Visitor& visitor)
Optional<Variable> LexicalEnvironment::get(const FlyString& name) const
{
ASSERT(type() != EnvironmentRecordType::Global);
return m_variables.get(name);
}
void LexicalEnvironment::set(const FlyString& name, Variable variable)
{
m_variables.set(name, variable);
if (type() == EnvironmentRecordType::Global)
interpreter().global_object().put(name, variable.value);
else
m_variables.set(name, variable);
}
bool LexicalEnvironment::has_super_binding() const
@ -113,7 +118,7 @@ Value LexicalEnvironment::get_this_binding() const
{
ASSERT(has_this_binding());
if (this_binding_status() == ThisBindingStatus::Uninitialized) {
interpreter().throw_exception<ReferenceError>(ErrorType::ThisHasNotBeenInitialized);
interpreter().vm().throw_exception<ReferenceError>(interpreter().global_object(), ErrorType::ThisHasNotBeenInitialized);
return {};
}
return m_this_value;
@ -123,7 +128,7 @@ void LexicalEnvironment::bind_this_value(Value this_value)
{
ASSERT(has_this_binding());
if (m_this_binding_status == ThisBindingStatus::Initialized) {
interpreter().throw_exception<ReferenceError>(ErrorType::ThisIsAlreadyInitialized);
interpreter().vm().throw_exception<ReferenceError>(interpreter().global_object(), ErrorType::ThisIsAlreadyInitialized);
return;
}
m_this_value = this_value;

View file

@ -87,6 +87,8 @@ public:
Function* current_function() const { return m_current_function; }
void set_current_function(Function& function) { m_current_function = &function; }
EnvironmentRecordType type() const { return m_environment_record_type; }
private:
virtual const char* class_name() const override { return "LexicalEnvironment"; }
virtual void visit_children(Visitor&) override;

View file

@ -69,7 +69,7 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_string)
} else if (this_value.is_object() && this_value.as_object().is_number_object()) {
number_value = static_cast<NumberObject&>(this_value.as_object()).value_of();
} else {
interpreter.throw_exception<TypeError>(ErrorType::NumberIncompatibleThis, "toString");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NumberIncompatibleThis, "toString");
return {};
}
@ -82,7 +82,7 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_string)
}
if (interpreter.exception() || radix < 2 || radix > 36) {
interpreter.throw_exception<RangeError>(ErrorType::InvalidRadix);
interpreter.vm().throw_exception<RangeError>(global_object, ErrorType::InvalidRadix);
return {};
}

View file

@ -358,7 +358,7 @@ bool Object::define_property(const StringOrSymbol& property_name, const Object&
if (is_accessor_property) {
if (descriptor.has_property("value") || descriptor.has_property("writable")) {
if (throw_exceptions)
interpreter().throw_exception<TypeError>(ErrorType::AccessorValueOrWritable);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::AccessorValueOrWritable);
return false;
}
@ -375,14 +375,14 @@ bool Object::define_property(const StringOrSymbol& property_name, const Object&
if (getter.is_function()) {
getter_function = &getter.as_function();
} else if (!getter.is_undefined()) {
interpreter().throw_exception<TypeError>(ErrorType::AccessorBadField, "get");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::AccessorBadField, "get");
return false;
}
if (setter.is_function()) {
setter_function = &setter.as_function();
} else if (!setter.is_undefined()) {
interpreter().throw_exception<TypeError>(ErrorType::AccessorBadField, "set");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::AccessorBadField, "set");
return false;
}
@ -465,7 +465,7 @@ bool Object::put_own_property(Object& this_object, const StringOrSymbol& propert
dbg() << "Disallow define_property of non-extensible object";
#endif
if (throw_exceptions && interpreter().in_strict_mode())
interpreter().throw_exception<TypeError>(ErrorType::NonExtensibleDefine, property_name.to_display_string().characters());
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::NonExtensibleDefine, property_name.to_display_string().characters());
return false;
}
@ -499,7 +499,7 @@ bool Object::put_own_property(Object& this_object, const StringOrSymbol& propert
dbg() << "Disallow reconfig of non-configurable property";
#endif
if (throw_exceptions)
interpreter().throw_exception<TypeError>(ErrorType::DescChangeNonConfigurable, property_name.to_display_string().characters());
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::DescChangeNonConfigurable, property_name.to_display_string().characters());
return false;
}
@ -547,7 +547,7 @@ bool Object::put_own_property_by_index(Object& this_object, u32 property_index,
dbg() << "Disallow define_property of non-extensible object";
#endif
if (throw_exceptions && interpreter().in_strict_mode())
interpreter().throw_exception<TypeError>(ErrorType::NonExtensibleDefine, property_index);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::NonExtensibleDefine, property_index);
return false;
}
@ -566,7 +566,7 @@ bool Object::put_own_property_by_index(Object& this_object, u32 property_index,
dbg() << "Disallow reconfig of non-configurable property";
#endif
if (throw_exceptions)
interpreter().throw_exception<TypeError>(ErrorType::DescChangeNonConfigurable, property_index);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::DescChangeNonConfigurable, property_index);
return false;
}
@ -843,7 +843,7 @@ Value Object::to_string() const
auto& interpreter = const_cast<Object*>(this)->interpreter();
auto to_string_result = interpreter.call(to_string_function, const_cast<Object*>(this));
if (to_string_result.is_object())
interpreter.throw_exception<TypeError>(ErrorType::Convert, "object", "string");
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::Convert, "object", "string");
if (interpreter.exception())
return {};
auto* string = to_string_result.to_primitive_string(interpreter);
@ -861,7 +861,7 @@ Value Object::invoke(const StringOrSymbol& property_name, Optional<MarkedValueLi
if (interpreter.exception())
return {};
if (!property.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, property.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::NotAFunction, property.to_string_without_side_effects().characters());
return {};
}
return interpreter.call(property.as_function(), this, move(arguments));
@ -870,20 +870,20 @@ Value Object::invoke(const StringOrSymbol& property_name, Optional<MarkedValueLi
Value Object::call_native_property_getter(Object* this_object, Value property) const
{
ASSERT(property.is_native_property());
auto& call_frame = interpreter().push_call_frame();
auto& call_frame = interpreter().vm().push_call_frame();
call_frame.this_value = this_object;
auto result = property.as_native_property().get(interpreter(), global_object());
interpreter().pop_call_frame();
interpreter().vm().pop_call_frame();
return result;
}
void Object::call_native_property_setter(Object* this_object, Value property, Value value) const
{
ASSERT(property.is_native_property());
auto& call_frame = interpreter().push_call_frame();
auto& call_frame = interpreter().vm().push_call_frame();
call_frame.this_value = this_object;
property.as_native_property().set(interpreter(), global_object(), value);
interpreter().pop_call_frame();
interpreter().vm().pop_call_frame();
}
}

View file

@ -106,7 +106,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::get_prototype_of)
JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::set_prototype_of)
{
if (interpreter.argument_count() < 2) {
interpreter.throw_exception<TypeError>(ErrorType::ObjectSetPrototypeOfTwoArgs);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ObjectSetPrototypeOfTwoArgs);
return {};
}
auto* object = interpreter.argument(0).to_object(interpreter, global_object);
@ -119,12 +119,12 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::set_prototype_of)
} else if (prototype_value.is_object()) {
prototype = &prototype_value.as_object();
} else {
interpreter.throw_exception<TypeError>(ErrorType::ObjectPrototypeWrongType);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ObjectPrototypeWrongType);
return {};
}
if (!object->set_prototype(prototype)) {
if (!interpreter.exception())
interpreter.throw_exception<TypeError>(ErrorType::ObjectSetPrototypeOfReturnedFalse);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ObjectSetPrototypeOfReturnedFalse);
return {};
}
return object;
@ -145,7 +145,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::prevent_extensions)
return argument;
if (!argument.as_object().prevent_extensions()) {
if (!interpreter.exception())
interpreter.throw_exception<TypeError>(ErrorType::ObjectPreventExtensionsReturnedFalse);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ObjectPreventExtensionsReturnedFalse);
return {};
}
return argument;
@ -165,11 +165,11 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::get_own_property_descriptor)
JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::define_property_)
{
if (!interpreter.argument(0).is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAnObject, "Object argument");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAnObject, "Object argument");
return {};
}
if (!interpreter.argument(2).is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAnObject, "Descriptor argument");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAnObject, "Descriptor argument");
return {};
}
auto& object = interpreter.argument(0).as_object();
@ -180,9 +180,9 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::define_property_)
if (!object.define_property(property_key, descriptor)) {
if (!interpreter.exception()) {
if (object.is_proxy_object()) {
interpreter.throw_exception<TypeError>(ErrorType::ObjectDefinePropertyReturnedFalse);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ObjectDefinePropertyReturnedFalse);
} else {
interpreter.throw_exception<TypeError>(ErrorType::NonExtensibleDefine, property_key.to_display_string().characters());
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NonExtensibleDefine, property_key.to_display_string().characters());
}
}
return {};
@ -198,7 +198,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::is)
JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::keys)
{
if (!interpreter.argument_count()) {
interpreter.throw_exception<TypeError>(ErrorType::ConvertUndefinedToObject);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ConvertUndefinedToObject);
return {};
}
@ -212,7 +212,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::keys)
JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::values)
{
if (!interpreter.argument_count()) {
interpreter.throw_exception<TypeError>(ErrorType::ConvertUndefinedToObject);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ConvertUndefinedToObject);
return {};
}
auto* obj_arg = interpreter.argument(0).to_object(interpreter, global_object);
@ -225,7 +225,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::values)
JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::entries)
{
if (!interpreter.argument_count()) {
interpreter.throw_exception<TypeError>(ErrorType::ConvertUndefinedToObject);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ConvertUndefinedToObject);
return {};
}
auto* obj_arg = interpreter.argument(0).to_object(interpreter, global_object);

View file

@ -51,14 +51,14 @@ ProxyConstructor::~ProxyConstructor()
Value ProxyConstructor::call(Interpreter& interpreter)
{
interpreter.throw_exception<TypeError>(ErrorType::ProxyCallWithNew);
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyCallWithNew);
return {};
}
Value ProxyConstructor::construct(Interpreter& interpreter, Function&)
{
if (interpreter.argument_count() < 2) {
interpreter.throw_exception<TypeError>(ErrorType::ProxyTwoArguments);
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyTwoArguments);
return {};
}
@ -66,11 +66,11 @@ Value ProxyConstructor::construct(Interpreter& interpreter, Function&)
auto handler = interpreter.argument(1);
if (!target.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::ProxyConstructorBadType, "target", target.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyConstructorBadType, "target", target.to_string_without_side_effects().characters());
return {};
}
if (!handler.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::ProxyConstructorBadType, "handler", handler.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyConstructorBadType, "handler", handler.to_string_without_side_effects().characters());
return {};
}
return ProxyObject::create(global_object(), target.as_object(), handler.as_object());

View file

@ -77,7 +77,7 @@ ProxyObject::~ProxyObject()
Object* ProxyObject::prototype()
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return nullptr;
}
auto trap = m_handler.get("getPrototypeOf");
@ -86,7 +86,7 @@ Object* ProxyObject::prototype()
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.prototype();
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "getPrototypeOf");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "getPrototypeOf");
return nullptr;
}
@ -94,7 +94,7 @@ Object* ProxyObject::prototype()
if (vm().exception())
return nullptr;
if (!trap_result.is_object() && !trap_result.is_null()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetPrototypeOfReturn);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetPrototypeOfReturn);
return nullptr;
}
if (m_target.is_extensible()) {
@ -108,7 +108,7 @@ Object* ProxyObject::prototype()
if (vm().exception())
return nullptr;
if (!same_value(interpreter(), trap_result, Value(target_proto))) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetPrototypeOfNonExtensible);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetPrototypeOfNonExtensible);
return nullptr;
}
return &trap_result.as_object();
@ -117,7 +117,7 @@ Object* ProxyObject::prototype()
const Object* ProxyObject::prototype() const
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return nullptr;
}
return const_cast<const Object*>(const_cast<ProxyObject*>(this)->prototype());
@ -126,7 +126,7 @@ const Object* ProxyObject::prototype() const
bool ProxyObject::set_prototype(Object* object)
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return false;
}
auto trap = m_handler.get("setPrototypeOf");
@ -135,7 +135,7 @@ bool ProxyObject::set_prototype(Object* object)
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.set_prototype(object);
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "setPrototypeOf");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "setPrototypeOf");
return false;
}
@ -148,7 +148,7 @@ bool ProxyObject::set_prototype(Object* object)
if (vm().exception())
return false;
if (!same_value(interpreter(), Value(object), Value(target_proto))) {
interpreter().throw_exception<TypeError>(ErrorType::ProxySetPrototypeOfNonExtensible);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxySetPrototypeOfNonExtensible);
return false;
}
return true;
@ -157,7 +157,7 @@ bool ProxyObject::set_prototype(Object* object)
bool ProxyObject::is_extensible() const
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return false;
}
auto trap = m_handler.get("isExtensible");
@ -166,7 +166,7 @@ bool ProxyObject::is_extensible() const
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.is_extensible();
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "isExtensible");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "isExtensible");
return {};
}
@ -175,7 +175,7 @@ bool ProxyObject::is_extensible() const
return false;
if (trap_result != m_target.is_extensible()) {
if (!vm().exception())
interpreter().throw_exception<TypeError>(ErrorType::ProxyIsExtensibleReturn);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyIsExtensibleReturn);
return false;
}
return trap_result;
@ -184,7 +184,7 @@ bool ProxyObject::is_extensible() const
bool ProxyObject::prevent_extensions()
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return false;
}
auto trap = m_handler.get("preventExtensions");
@ -193,7 +193,7 @@ bool ProxyObject::prevent_extensions()
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.prevent_extensions();
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "preventExtensions");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "preventExtensions");
return {};
}
@ -202,7 +202,7 @@ bool ProxyObject::prevent_extensions()
return false;
if (trap_result && m_target.is_extensible()) {
if (!vm().exception())
interpreter().throw_exception<TypeError>(ErrorType::ProxyPreventExtensionsReturn);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyPreventExtensionsReturn);
return false;
}
return trap_result;
@ -211,7 +211,7 @@ bool ProxyObject::prevent_extensions()
Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const PropertyName& name) const
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return {};
}
auto trap = m_handler.get("getOwnPropertyDescriptor");
@ -220,7 +220,7 @@ Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const Prop
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.get_own_property_descriptor(name);
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "getOwnPropertyDescriptor");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "getOwnPropertyDescriptor");
return {};
}
@ -228,7 +228,7 @@ Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const Prop
if (vm().exception())
return {};
if (!trap_result.is_object() && !trap_result.is_undefined()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetOwnDescriptorReturn);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorReturn);
return {};
}
auto target_desc = m_target.get_own_property_descriptor(name);
@ -238,12 +238,12 @@ Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const Prop
if (!target_desc.has_value())
return {};
if (!target_desc.value().attributes.is_configurable()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetOwnDescriptorNonConfigurable);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorNonConfigurable);
return {};
}
if (!m_target.is_extensible()) {
if (!vm().exception())
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetOwnDescriptorUndefReturn);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorUndefReturn);
return {};
}
return {};
@ -253,11 +253,11 @@ Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const Prop
return {};
if (!is_compatible_property_descriptor(interpreter(), m_target.is_extensible(), result_desc, target_desc)) {
if (!vm().exception())
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetOwnDescriptorInvalidDescriptor);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorInvalidDescriptor);
return {};
}
if (!result_desc.attributes.is_configurable() && (!target_desc.has_value() || target_desc.value().attributes.is_configurable())) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetOwnDescriptorInvalidNonConfig);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorInvalidNonConfig);
return {};
}
return result_desc;
@ -266,7 +266,7 @@ Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const Prop
bool ProxyObject::define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions)
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return false;
}
auto trap = m_handler.get("defineProperty");
@ -275,7 +275,7 @@ bool ProxyObject::define_property(const StringOrSymbol& property_name, const Obj
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.define_property(property_name, descriptor, throw_exceptions);
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "defineProperty");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "defineProperty");
return false;
}
@ -293,21 +293,21 @@ bool ProxyObject::define_property(const StringOrSymbol& property_name, const Obj
if (!target_desc.has_value()) {
if (!m_target.is_extensible()) {
if (!vm().exception())
interpreter().throw_exception<TypeError>(ErrorType::ProxyDefinePropNonExtensible);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropNonExtensible);
return false;
}
if (setting_config_false) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyDefinePropNonConfigurableNonExisting);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropNonConfigurableNonExisting);
return false;
}
} else {
if (!is_compatible_property_descriptor(interpreter(), m_target.is_extensible(), PropertyDescriptor::from_dictionary(vm(), descriptor), target_desc)) {
if (!vm().exception())
interpreter().throw_exception<TypeError>(ErrorType::ProxyDefinePropIncompatibleDescriptor);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropIncompatibleDescriptor);
return false;
}
if (setting_config_false && target_desc.value().attributes.is_configurable()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyDefinePropExistingConfigurable);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropExistingConfigurable);
return false;
}
}
@ -317,7 +317,7 @@ bool ProxyObject::define_property(const StringOrSymbol& property_name, const Obj
bool ProxyObject::has_property(const PropertyName& name) const
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return false;
}
auto trap = m_handler.get("has");
@ -326,7 +326,7 @@ bool ProxyObject::has_property(const PropertyName& name) const
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.has_property(name);
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "has");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "has");
return false;
}
@ -339,12 +339,12 @@ bool ProxyObject::has_property(const PropertyName& name) const
return false;
if (target_desc.has_value()) {
if (!target_desc.value().attributes.is_configurable()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyHasExistingNonConfigurable);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyHasExistingNonConfigurable);
return false;
}
if (!m_target.is_extensible()) {
if (!vm().exception())
interpreter().throw_exception<TypeError>(ErrorType::ProxyHasExistingNonExtensible);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyHasExistingNonExtensible);
return false;
}
}
@ -355,7 +355,7 @@ bool ProxyObject::has_property(const PropertyName& name) const
Value ProxyObject::get(const PropertyName& name, Value) const
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return {};
}
auto trap = m_handler.get("get");
@ -364,7 +364,7 @@ Value ProxyObject::get(const PropertyName& name, Value) const
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.get(name);
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "get");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "get");
return {};
}
@ -376,11 +376,11 @@ Value ProxyObject::get(const PropertyName& name, Value) const
if (vm().exception())
return {};
if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(interpreter(), trap_result, target_desc.value().value)) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetImmutableDataProperty);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetImmutableDataProperty);
return {};
}
if (target_desc.value().is_accessor_descriptor() && target_desc.value().getter == nullptr && !trap_result.is_undefined()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyGetNonConfigurableAccessor);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyGetNonConfigurableAccessor);
return {};
}
}
@ -390,7 +390,7 @@ Value ProxyObject::get(const PropertyName& name, Value) const
bool ProxyObject::put(const PropertyName& name, Value value, Value)
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return false;
}
auto trap = m_handler.get("set");
@ -399,7 +399,7 @@ bool ProxyObject::put(const PropertyName& name, Value value, Value)
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.put(name, value);
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "set");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "set");
return false;
}
auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), Value(&m_target), js_string(interpreter(), name.to_string()), value, Value(const_cast<ProxyObject*>(this))).to_boolean();
@ -410,11 +410,11 @@ bool ProxyObject::put(const PropertyName& name, Value value, Value)
return false;
if (target_desc.has_value() && !target_desc.value().attributes.is_configurable()) {
if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(interpreter(), value, target_desc.value().value)) {
interpreter().throw_exception<TypeError>(ErrorType::ProxySetImmutableDataProperty);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxySetImmutableDataProperty);
return false;
}
if (target_desc.value().is_accessor_descriptor() && !target_desc.value().setter) {
interpreter().throw_exception<TypeError>(ErrorType::ProxySetNonConfigurableAccessor);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxySetNonConfigurableAccessor);
}
}
return true;
@ -423,7 +423,7 @@ bool ProxyObject::put(const PropertyName& name, Value value, Value)
Value ProxyObject::delete_property(const PropertyName& name)
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return {};
}
auto trap = m_handler.get("deleteProperty");
@ -432,7 +432,7 @@ Value ProxyObject::delete_property(const PropertyName& name)
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return m_target.delete_property(name);
if (!trap.is_function()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "deleteProperty");
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "deleteProperty");
return {};
}
@ -447,7 +447,7 @@ Value ProxyObject::delete_property(const PropertyName& name)
if (!target_desc.has_value())
return Value(true);
if (!target_desc.value().attributes.is_configurable()) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyDeleteNonConfigurable);
interpreter().vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyDeleteNonConfigurable);
return {};
}
return Value(true);
@ -463,11 +463,11 @@ void ProxyObject::visit_children(Cell::Visitor& visitor)
Value ProxyObject::call(Interpreter& interpreter)
{
if (!is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, Value(this).to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::NotAFunction, Value(this).to_string_without_side_effects().characters());
return {};
}
if (m_is_revoked) {
interpreter.throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return {};
}
auto trap = m_handler.get("apply");
@ -476,7 +476,7 @@ Value ProxyObject::call(Interpreter& interpreter)
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return static_cast<Function&>(m_target).call(interpreter);
if (!trap.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "apply");
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "apply");
return {};
}
MarkedValueList arguments(interpreter.heap());
@ -484,7 +484,7 @@ Value ProxyObject::call(Interpreter& interpreter)
arguments.append(Value(&m_handler));
// FIXME: Pass global object
auto arguments_array = Array::create(interpreter.global_object());
interpreter.for_each_argument([&](auto& argument) {
interpreter.vm().for_each_argument([&](auto& argument) {
arguments_array->indexed_properties().append(argument);
});
arguments.append(arguments_array);
@ -495,11 +495,11 @@ Value ProxyObject::call(Interpreter& interpreter)
Value ProxyObject::construct(Interpreter& interpreter, Function& new_target)
{
if (!is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, Value(this).to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, Value(this).to_string_without_side_effects().characters());
return {};
}
if (m_is_revoked) {
interpreter.throw_exception<TypeError>(ErrorType::ProxyRevoked);
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
return {};
}
auto trap = m_handler.get("construct");
@ -508,21 +508,21 @@ Value ProxyObject::construct(Interpreter& interpreter, Function& new_target)
if (trap.is_empty() || trap.is_undefined() || trap.is_null())
return static_cast<Function&>(m_target).construct(interpreter, new_target);
if (!trap.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::ProxyInvalidTrap, "construct");
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "construct");
return {};
}
MarkedValueList arguments(interpreter.heap());
arguments.append(Value(&m_target));
auto arguments_array = Array::create(interpreter.global_object());
interpreter.for_each_argument([&](auto& argument) {
interpreter.vm().for_each_argument([&](auto& argument) {
arguments_array->indexed_properties().append(argument);
});
arguments.append(arguments_array);
arguments.append(Value(&new_target));
auto result = interpreter.call(trap.as_function(), Value(&m_handler), move(arguments));
if (!result.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::ProxyConstructBadReturnType);
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::ProxyConstructBadReturnType);
return {};
}
return result;

View file

@ -44,14 +44,14 @@ void Reference::put(Interpreter& interpreter, GlobalObject& global_object, Value
if (is_local_variable() || is_global_variable()) {
if (is_local_variable())
interpreter.set_variable(m_name.to_string(), value, global_object);
interpreter.vm().set_variable(m_name.to_string(), value, global_object);
else
global_object.put(m_name, value);
return;
}
if (!base().is_object() && interpreter.in_strict_mode()) {
interpreter.throw_exception<TypeError>(ErrorType::ReferencePrimitiveAssignment, m_name.to_string().characters());
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ReferencePrimitiveAssignment, m_name.to_string().characters());
return;
}
@ -62,14 +62,14 @@ void Reference::put(Interpreter& interpreter, GlobalObject& global_object, Value
object->put(m_name, value);
}
void Reference::throw_reference_error(Interpreter& interpreter, GlobalObject&)
void Reference::throw_reference_error(Interpreter& interpreter, GlobalObject& global_object)
{
auto property_name = m_name.to_string();
String message;
if (property_name.is_empty()) {
interpreter.throw_exception<ReferenceError>(ErrorType::ReferenceUnresolvable);
interpreter.vm().throw_exception<ReferenceError>(global_object, ErrorType::ReferenceUnresolvable);
} else {
interpreter.throw_exception<ReferenceError>(ErrorType::UnknownIdentifier, property_name.characters());
interpreter.vm().throw_exception<ReferenceError>(global_object, ErrorType::UnknownIdentifier, property_name.characters());
}
}
@ -85,7 +85,7 @@ Value Reference::get(Interpreter& interpreter, GlobalObject& global_object)
if (is_local_variable() || is_global_variable()) {
Value value;
if (is_local_variable())
value = interpreter.get_variable(m_name.to_string(), global_object);
value = interpreter.vm().get_variable(m_name.to_string(), global_object);
else
value = global_object.get(m_name);
if (interpreter.exception())

View file

@ -38,7 +38,7 @@ static Object* get_target_object_from(Interpreter& interpreter, const String& na
{
auto target = interpreter.argument(0);
if (!target.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::ReflectArgumentMustBeAnObject, name.characters());
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::ReflectArgumentMustBeAnObject, name.characters());
return nullptr;
}
return static_cast<Object*>(&target.as_object());
@ -48,7 +48,7 @@ static Function* get_target_function_from(Interpreter& interpreter, const String
{
auto target = interpreter.argument(0);
if (!target.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::ReflectArgumentMustBeAFunction, name.characters());
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::ReflectArgumentMustBeAFunction, name.characters());
return nullptr;
}
return &target.as_function();
@ -57,7 +57,7 @@ static Function* get_target_function_from(Interpreter& interpreter, const String
static void prepare_arguments_list(Interpreter& interpreter, Value value, MarkedValueList* arguments)
{
if (!value.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::ReflectBadArgumentsList);
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::ReflectBadArgumentsList);
return;
}
auto& arguments_list = value.as_object();
@ -130,12 +130,12 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::construct)
auto new_target_value = interpreter.argument(2);
if (!new_target_value.is_function()
|| (new_target_value.as_object().is_native_function() && !static_cast<NativeFunction&>(new_target_value.as_object()).has_constructor())) {
interpreter.throw_exception<TypeError>(ErrorType::ReflectBadNewTarget);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ReflectBadNewTarget);
return {};
}
new_target = &new_target_value.as_function();
}
return interpreter.construct(*target, *new_target, move(arguments), global_object);
return interpreter.vm().construct(*target, *new_target, move(arguments), global_object);
}
JS_DEFINE_NATIVE_FUNCTION(ReflectObject::define_property)
@ -144,7 +144,7 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::define_property)
if (!target)
return {};
if (!interpreter.argument(2).is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::ReflectBadDescriptorArgument);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ReflectBadDescriptorArgument);
return {};
}
auto property_key = StringOrSymbol::from_value(interpreter, interpreter.argument(1));
@ -268,7 +268,7 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set_prototype_of)
return {};
auto prototype_value = interpreter.argument(1);
if (!prototype_value.is_object() && !prototype_value.is_null()) {
interpreter.throw_exception<TypeError>(ErrorType::ObjectPrototypeWrongType);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ObjectPrototypeWrongType);
return {};
}
Object* prototype = nullptr;

View file

@ -41,7 +41,7 @@ static ScriptFunction* typed_this(Interpreter& interpreter, GlobalObject& global
if (!this_object)
return nullptr;
if (!this_object->is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAFunctionNoParam);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAFunctionNoParam);
return nullptr;
}
return static_cast<ScriptFunction*>(this_object);
@ -130,13 +130,13 @@ Value ScriptFunction::call(Interpreter& interpreter)
arguments.append({ parameter.name, value });
interpreter.current_environment()->set(parameter.name, { value, DeclarationKind::Var });
}
return interpreter.execute_statement(global_object(), m_body, arguments, ScopeType::Function);
return interpreter.vm().execute_statement(global_object(), m_body, arguments, ScopeType::Function);
}
Value ScriptFunction::construct(Interpreter& interpreter, Function&)
{
if (m_is_arrow_function) {
interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, m_name.characters());
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, m_name.characters());
return {};
}
return call(interpreter);

View file

@ -89,7 +89,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringConstructor::raw)
if (interpreter.exception())
return {};
if (raw.is_empty() || raw.is_undefined() || raw.is_null()) {
interpreter.throw_exception<TypeError>(ErrorType::StringRawCannotConvert, raw.is_null() ? "null" : "undefined");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::StringRawCannotConvert, raw.is_null() ? "null" : "undefined");
return {};
}
if (!raw.is_array())

View file

@ -54,7 +54,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringIteratorPrototype::next)
{
auto this_value = interpreter.this_value(global_object);
if (!this_value.is_object() || !this_value.as_object().is_string_iterator_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "String Iterator");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "String Iterator");
return {};
}

View file

@ -46,7 +46,7 @@ static StringObject* typed_this(Interpreter& interpreter, GlobalObject& global_o
if (!this_object)
return nullptr;
if (!this_object->is_string_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "String");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "String");
return nullptr;
}
return static_cast<StringObject*>(this_object);
@ -141,11 +141,11 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::repeat)
if (interpreter.exception())
return {};
if (count_value.as_double() < 0) {
interpreter.throw_exception<RangeError>(ErrorType::StringRepeatCountMustBe, "positive");
interpreter.vm().throw_exception<RangeError>(global_object, ErrorType::StringRepeatCountMustBe, "positive");
return {};
}
if (count_value.is_infinity()) {
interpreter.throw_exception<RangeError>(ErrorType::StringRepeatCountMustBe, "finite");
interpreter.vm().throw_exception<RangeError>(global_object, ErrorType::StringRepeatCountMustBe, "finite");
return {};
}
auto count = count_value.to_size_t(interpreter);
@ -460,7 +460,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::symbol_iterator)
{
auto this_object = interpreter.this_value(global_object);
if (this_object.is_undefined() || this_object.is_null()) {
interpreter.throw_exception<TypeError>(ErrorType::ToObjectNullOrUndef);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ToObjectNullOrUndef);
return {};
}

View file

@ -65,7 +65,7 @@ Value SymbolConstructor::call(Interpreter& interpreter)
Value SymbolConstructor::construct(Interpreter& interpreter, Function&)
{
interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, "Symbol");
interpreter.vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, "Symbol");
return {};
}
@ -85,7 +85,7 @@ JS_DEFINE_NATIVE_FUNCTION(SymbolConstructor::key_for)
{
auto argument = interpreter.argument(0);
if (!argument.is_symbol()) {
interpreter.throw_exception<TypeError>(ErrorType::NotASymbol, argument.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotASymbol, argument.to_string_without_side_effects().characters());
return {};
}

View file

@ -64,7 +64,7 @@ static SymbolObject* typed_this(Interpreter& interpreter, GlobalObject& global_o
if (!this_object)
return nullptr;
if (!this_object->is_symbol_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Symbol");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Symbol");
return nullptr;
}
return static_cast<SymbolObject*>(this_object);

View file

@ -59,7 +59,7 @@ JS_DEFINE_NATIVE_GETTER(Uint8ClampedArray::length_getter)
if (!this_object)
return {};
if (StringView(this_object->class_name()) != "Uint8ClampedArray") {
interpreter.throw_exception<TypeError>(ErrorType::NotA, "Uint8ClampedArray");
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotA, "Uint8ClampedArray");
return {};
}
return Value(static_cast<const Uint8ClampedArray*>(this_object)->length());

View file

@ -24,10 +24,18 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/StringBuilder.h>
#include <AK/ScopeGuard.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Reference.h>
#include <LibJS/Runtime/ScriptFunction.h>
#include <LibJS/Runtime/Symbol.h>
#include <LibJS/Runtime/VM.h>
//#define VM_DEBUG
namespace JS {
NonnullRefPtr<VM> VM::create()
@ -90,8 +98,19 @@ void VM::gather_roots(HashTable<Cell*>& roots)
roots.set(m_empty_string);
if (m_exception)
roots.set(m_exception);
for (auto* interpreter : m_interpreters)
interpreter->gather_roots(roots);
if (m_last_value.is_cell())
roots.set(m_last_value.as_cell());
for (auto& call_frame : m_call_stack) {
if (call_frame.this_value.is_cell())
roots.set(call_frame.this_value.as_cell());
for (auto& argument : call_frame.arguments) {
if (argument.is_cell())
roots.set(argument.as_cell());
}
roots.set(call_frame.environment);
}
#define __JS_ENUMERATE(SymbolName, snake_name) \
roots.set(well_known_symbol_##snake_name());
@ -113,4 +132,266 @@ Symbol* VM::get_global_symbol(const String& description)
return new_global_symbol;
}
bool VM::in_strict_mode() const
{
if (m_scope_stack.is_empty())
return true;
return m_scope_stack.last().scope_node->in_strict_mode();
}
void VM::enter_scope(const ScopeNode& scope_node, ArgumentVector arguments, ScopeType scope_type, GlobalObject& global_object)
{
for (auto& declaration : scope_node.functions()) {
auto* function = ScriptFunction::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), current_environment());
set_variable(declaration.name(), function, global_object);
}
if (scope_type == ScopeType::Function) {
m_scope_stack.append({ scope_type, scope_node, false });
return;
}
HashMap<FlyString, Variable> scope_variables_with_declaration_kind;
scope_variables_with_declaration_kind.ensure_capacity(16);
for (auto& declaration : scope_node.variables()) {
for (auto& declarator : declaration.declarations()) {
if (scope_node.is_program()) {
global_object.put(declarator.id().string(), js_undefined());
if (exception())
return;
} else {
scope_variables_with_declaration_kind.set(declarator.id().string(), { js_undefined(), declaration.declaration_kind() });
}
}
}
for (auto& argument : arguments) {
scope_variables_with_declaration_kind.set(argument.name, { argument.value, DeclarationKind::Var });
}
bool pushed_lexical_environment = false;
if (!scope_variables_with_declaration_kind.is_empty()) {
auto* block_lexical_environment = heap().allocate<LexicalEnvironment>(global_object, move(scope_variables_with_declaration_kind), current_environment());
m_call_stack.last().environment = block_lexical_environment;
pushed_lexical_environment = true;
}
m_scope_stack.append({ scope_type, scope_node, pushed_lexical_environment });
}
void VM::exit_scope(const ScopeNode& scope_node)
{
while (!m_scope_stack.is_empty()) {
auto popped_scope = m_scope_stack.take_last();
if (popped_scope.pushed_environment)
m_call_stack.last().environment = m_call_stack.last().environment->parent();
if (popped_scope.scope_node.ptr() == &scope_node)
break;
}
// If we unwind all the way, just reset m_unwind_until so that future "return" doesn't break.
if (m_scope_stack.is_empty())
m_unwind_until = ScopeType::None;
}
void VM::set_variable(const FlyString& name, Value value, GlobalObject& global_object, bool first_assignment)
{
if (m_call_stack.size()) {
for (auto* environment = current_environment(); environment; environment = environment->parent()) {
if (environment->type() == LexicalEnvironment::EnvironmentRecordType::Global)
break;
auto possible_match = environment->get(name);
if (possible_match.has_value()) {
if (!first_assignment && possible_match.value().declaration_kind == DeclarationKind::Const) {
throw_exception<TypeError>(global_object, ErrorType::InvalidAssignToConst);
return;
}
environment->set(name, { value, possible_match.value().declaration_kind });
return;
}
}
}
global_object.put(move(name), move(value));
}
Value VM::get_variable(const FlyString& name, GlobalObject& global_object)
{
if (m_call_stack.size()) {
for (auto* environment = current_environment(); environment; environment = environment->parent()) {
if (environment->type() == LexicalEnvironment::EnvironmentRecordType::Global)
break;
auto possible_match = environment->get(name);
if (possible_match.has_value())
return possible_match.value().value;
}
}
auto value = global_object.get(name);
if (m_underscore_is_last_value && name == "_" && value.is_empty())
return m_last_value;
return value;
}
Reference VM::get_reference(const FlyString& name)
{
if (m_call_stack.size()) {
for (auto* environment = current_environment(); environment; environment = environment->parent()) {
if (environment->type() == LexicalEnvironment::EnvironmentRecordType::Global)
break;
auto possible_match = environment->get(name);
if (possible_match.has_value())
return { Reference::LocalVariable, name };
}
}
return { Reference::GlobalVariable, name };
}
Value VM::execute_statement(GlobalObject& global_object, const Statement& statement, ArgumentVector arguments, ScopeType scope_type)
{
if (!statement.is_scope_node())
return statement.execute(interpreter(), global_object);
auto& block = static_cast<const ScopeNode&>(statement);
enter_scope(block, move(arguments), scope_type, global_object);
if (block.children().is_empty())
m_last_value = js_undefined();
for (auto& node : block.children()) {
m_last_value = node.execute(interpreter(), global_object);
if (should_unwind()) {
if (!block.label().is_null() && should_unwind_until(ScopeType::Breakable, block.label()))
stop_unwind();
break;
}
}
bool did_return = m_unwind_until == ScopeType::Function;
if (m_unwind_until == scope_type)
m_unwind_until = ScopeType::None;
exit_scope(block);
return did_return ? m_last_value : js_undefined();
}
Value VM::construct(Function& function, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject& global_object)
{
auto& call_frame = push_call_frame();
ArmedScopeGuard call_frame_popper = [&] {
pop_call_frame();
};
call_frame.function_name = function.name();
call_frame.arguments = function.bound_arguments();
if (arguments.has_value())
call_frame.arguments.append(arguments.value().values());
call_frame.environment = function.create_environment();
current_environment()->set_new_target(&new_target);
Object* new_object = nullptr;
if (function.constructor_kind() == Function::ConstructorKind::Base) {
new_object = Object::create_empty(global_object);
current_environment()->bind_this_value(new_object);
if (exception())
return {};
auto prototype = new_target.get("prototype");
if (exception())
return {};
if (prototype.is_object()) {
new_object->set_prototype(&prototype.as_object());
if (exception())
return {};
}
}
// If we are a Derived constructor, |this| has not been constructed before super is called.
Value this_value = function.constructor_kind() == Function::ConstructorKind::Base ? new_object : Value {};
call_frame.this_value = this_value;
auto result = function.construct(interpreter(), new_target);
this_value = current_environment()->get_this_binding();
pop_call_frame();
call_frame_popper.disarm();
// If we are constructing an instance of a derived class,
// set the prototype on objects created by constructors that return an object (i.e. NativeFunction subclasses).
if (function.constructor_kind() == Function::ConstructorKind::Base && new_target.constructor_kind() == Function::ConstructorKind::Derived && result.is_object()) {
current_environment()->replace_this_binding(result);
auto prototype = new_target.get("prototype");
if (exception())
return {};
if (prototype.is_object()) {
result.as_object().set_prototype(&prototype.as_object());
if (exception())
return {};
}
return result;
}
if (exception())
return {};
if (result.is_object())
return result;
return this_value;
}
void VM::throw_exception(Exception* exception)
{
#ifdef VM_DEBUG
if (exception->value().is_object() && exception->value().as_object().is_error()) {
auto& error = static_cast<Error&>(exception->value().as_object());
dbg() << "Throwing JavaScript Error: " << error.name() << ", " << error.message();
for (ssize_t i = m_call_stack.size() - 1; i >= 0; --i) {
auto function_name = m_call_stack[i].function_name;
if (function_name.is_empty())
function_name = "<anonymous>";
dbg() << " " << function_name;
}
}
#endif
m_exception = exception;
unwind(ScopeType::Try);
}
String VM::join_arguments() const
{
StringBuilder joined_arguments;
for (size_t i = 0; i < argument_count(); ++i) {
joined_arguments.append(argument(i).to_string_without_side_effects().characters());
if (i != argument_count() - 1)
joined_arguments.append(' ');
}
return joined_arguments.build();
}
Value VM::resolve_this_binding() const
{
return get_this_environment()->get_this_binding();
}
const LexicalEnvironment* VM::get_this_environment() const
{
// We will always return because the Global environment will always be reached, which has a |this| binding.
for (const LexicalEnvironment* environment = current_environment(); environment; environment = environment->parent()) {
if (environment->has_this_binding())
return environment;
}
ASSERT_NOT_REACHED();
}
Value VM::get_new_target() const
{
return get_this_environment()->new_target();
}
}

View file

@ -26,12 +26,45 @@
#pragma once
#include <AK/FlyString.h>
#include <AK/HashMap.h>
#include <AK/RefCounted.h>
#include <LibJS/Heap/Heap.h>
#include <LibJS/Runtime/ErrorTypes.h>
#include <LibJS/Runtime/Exception.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
enum class ScopeType {
None,
Function,
Block,
Try,
Breakable,
Continuable,
};
struct ScopeFrame {
ScopeType type;
NonnullRefPtr<ScopeNode> scope_node;
bool pushed_environment { false };
};
struct CallFrame {
FlyString function_name;
Value this_value;
Vector<Value> arguments;
LexicalEnvironment* environment { nullptr };
};
struct Argument {
FlyString name;
Value value;
};
typedef Vector<Argument, 8> ArgumentVector;
class VM : public RefCounted<VM> {
public:
static NonnullRefPtr<VM> create();
@ -50,7 +83,7 @@ public:
{
return m_exception;
}
void set_exception(Badge<Interpreter>, Exception* exception) { m_exception = exception; }
void clear_exception() { m_exception = nullptr; }
class InterpreterExecutionScope {
@ -73,6 +106,107 @@ public:
PrimitiveString& empty_string() { return *m_empty_string; }
CallFrame& push_call_frame()
{
m_call_stack.append({ {}, js_undefined(), {}, nullptr });
return m_call_stack.last();
}
void pop_call_frame() { m_call_stack.take_last(); }
const CallFrame& call_frame() { return m_call_stack.last(); }
const Vector<CallFrame>& call_stack() const { return m_call_stack; }
Vector<CallFrame>& call_stack() { return m_call_stack; }
const LexicalEnvironment* current_environment() const { return m_call_stack.last().environment; }
LexicalEnvironment* current_environment() { return m_call_stack.last().environment; }
bool in_strict_mode() const;
template<typename Callback>
void for_each_argument(Callback callback)
{
if (m_call_stack.is_empty())
return;
for (auto& value : m_call_stack.last().arguments)
callback(value);
}
size_t argument_count() const
{
if (m_call_stack.is_empty())
return 0;
return m_call_stack.last().arguments.size();
}
Value argument(size_t index) const
{
if (m_call_stack.is_empty())
return {};
auto& arguments = m_call_stack.last().arguments;
return index < arguments.size() ? arguments[index] : js_undefined();
}
Value this_value(Object& global_object) const
{
if (m_call_stack.is_empty())
return &global_object;
return m_call_stack.last().this_value;
}
Value last_value() const { return m_last_value; }
bool underscore_is_last_value() const { return m_underscore_is_last_value; }
void set_underscore_is_last_value(bool b) { m_underscore_is_last_value = b; }
void unwind(ScopeType type, FlyString label = {})
{
m_unwind_until = type;
m_unwind_until_label = label;
}
void stop_unwind() { m_unwind_until = ScopeType::None; }
bool should_unwind_until(ScopeType type, FlyString label) const
{
if (m_unwind_until_label.is_null())
return m_unwind_until == type;
return m_unwind_until == type && m_unwind_until_label == label;
}
bool should_unwind() const { return m_unwind_until != ScopeType::None; }
Value get_variable(const FlyString& name, GlobalObject&);
void set_variable(const FlyString& name, Value, GlobalObject&, bool first_assignment = false);
Reference get_reference(const FlyString& name);
void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&);
void exit_scope(const ScopeNode&);
template<typename T, typename... Args>
void throw_exception(GlobalObject& global_object, Args&&... args)
{
return throw_exception(global_object, T::create(global_object, forward<Args>(args)...));
}
void throw_exception(Exception*);
void throw_exception(GlobalObject& global_object, Value value)
{
return throw_exception(heap().allocate<Exception>(global_object, value));
}
template<typename T, typename... Args>
void throw_exception(GlobalObject& global_object, ErrorType type, Args&&... args)
{
return throw_exception(global_object, T::create(global_object, String::format(type.message(), forward<Args>(args)...)));
}
Value execute_statement(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block);
Value construct(Function&, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject&);
String join_arguments() const;
Value resolve_this_binding() const;
const LexicalEnvironment* get_this_environment() const;
Value get_new_target() const;
private:
VM();
@ -81,6 +215,15 @@ private:
Heap m_heap;
Vector<Interpreter*> m_interpreters;
Vector<ScopeFrame> m_scope_stack;
Vector<CallFrame> m_call_stack;
Value m_last_value;
ScopeType m_unwind_until { ScopeType::None };
FlyString m_unwind_until_label;
bool m_underscore_is_last_value { false };
HashMap<String, Symbol*> m_global_symbol_map;
PrimitiveString* m_empty_string { nullptr };

View file

@ -163,7 +163,7 @@ String Value::to_string(Interpreter& interpreter) const
case Type::String:
return m_value.as_string->string();
case Type::Symbol:
interpreter.throw_exception<TypeError>(ErrorType::Convert, "symbol", "string");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::Convert, "symbol", "string");
return {};
case Type::BigInt:
return m_value.as_bigint->big_integer().to_base10();
@ -215,7 +215,7 @@ Object* Value::to_object(Interpreter& interpreter, GlobalObject& global_object)
switch (m_type) {
case Type::Undefined:
case Type::Null:
interpreter.throw_exception<TypeError>(ErrorType::ToObjectNullOrUndef);
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ToObjectNullOrUndef);
return nullptr;
case Type::Boolean:
return BooleanObject::create(global_object, m_value.as_bool);
@ -271,10 +271,10 @@ Value Value::to_number(Interpreter& interpreter) const
return Value(parsed_double);
}
case Type::Symbol:
interpreter.throw_exception<TypeError>(ErrorType::Convert, "symbol", "number");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::Convert, "symbol", "number");
return {};
case Type::BigInt:
interpreter.throw_exception<TypeError>(ErrorType::Convert, "BigInt", "number");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::Convert, "BigInt", "number");
return {};
case Type::Object: {
auto primitive = m_value.as_object->to_primitive(PreferredType::Number);
@ -294,10 +294,10 @@ BigInt* Value::to_bigint(Interpreter& interpreter) const
return nullptr;
switch (primitive.type()) {
case Type::Undefined:
interpreter.throw_exception<TypeError>(ErrorType::Convert, "undefined", "BigInt");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::Convert, "undefined", "BigInt");
return nullptr;
case Type::Null:
interpreter.throw_exception<TypeError>(ErrorType::Convert, "null", "BigInt");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::Convert, "null", "BigInt");
return nullptr;
case Type::Boolean: {
auto value = primitive.as_bool() ? 1 : 0;
@ -306,18 +306,18 @@ BigInt* Value::to_bigint(Interpreter& interpreter) const
case Type::BigInt:
return &primitive.as_bigint();
case Type::Number:
interpreter.throw_exception<TypeError>(ErrorType::Convert, "number", "BigInt");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::Convert, "number", "BigInt");
return {};
case Type::String: {
auto& string = primitive.as_string().string();
if (!is_valid_bigint_value(string)) {
interpreter.throw_exception<SyntaxError>(ErrorType::BigIntInvalidValue, string.characters());
interpreter.vm().throw_exception<SyntaxError>(interpreter.global_object(), ErrorType::BigIntInvalidValue, string.characters());
return {};
}
return js_bigint(interpreter, Crypto::SignedBigInteger::from_base10(string.trim_whitespace()));
}
case Type::Symbol:
interpreter.throw_exception<TypeError>(ErrorType::Convert, "symbol", "BigInt");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::Convert, "symbol", "BigInt");
return {};
default:
ASSERT_NOT_REACHED();
@ -416,7 +416,7 @@ Value bitwise_and(Interpreter& interpreter, Value lhs, Value rhs)
}
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, lhs_numeric.as_bigint().big_integer().bitwise_and(rhs_numeric.as_bigint().big_integer()));
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "bitwise AND");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "bitwise AND");
return {};
}
@ -439,7 +439,7 @@ Value bitwise_or(Interpreter& interpreter, Value lhs, Value rhs)
}
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, lhs_numeric.as_bigint().big_integer().bitwise_or(rhs_numeric.as_bigint().big_integer()));
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "bitwise OR");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "bitwise OR");
return {};
}
@ -462,7 +462,7 @@ Value bitwise_xor(Interpreter& interpreter, Value lhs, Value rhs)
}
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, lhs_numeric.as_bigint().big_integer().bitwise_xor(rhs_numeric.as_bigint().big_integer()));
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "bitwise XOR");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "bitwise XOR");
return {};
}
@ -518,7 +518,7 @@ Value left_shift(Interpreter& interpreter, Value lhs, Value rhs)
}
if (both_bigint(lhs_numeric, rhs_numeric))
TODO();
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "left-shift");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "left-shift");
return {};
}
@ -539,7 +539,7 @@ Value right_shift(Interpreter& interpreter, Value lhs, Value rhs)
}
if (both_bigint(lhs_numeric, rhs_numeric))
TODO();
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "right-shift");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "right-shift");
return {};
}
@ -558,7 +558,7 @@ Value unsigned_right_shift(Interpreter& interpreter, Value lhs, Value rhs)
return lhs_numeric;
return Value((unsigned)lhs_numeric.as_double() >> (i32)rhs_numeric.as_double());
}
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperator, "unsigned right-shift");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperator, "unsigned right-shift");
return {};
}
@ -594,7 +594,7 @@ Value add(Interpreter& interpreter, Value lhs, Value rhs)
return Value(lhs_numeric.as_double() + rhs_numeric.as_double());
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, lhs_numeric.as_bigint().big_integer().plus(rhs_numeric.as_bigint().big_integer()));
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "addition");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "addition");
return {};
}
@ -610,7 +610,7 @@ Value sub(Interpreter& interpreter, Value lhs, Value rhs)
return Value(lhs_numeric.as_double() - rhs_numeric.as_double());
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, lhs_numeric.as_bigint().big_integer().minus(rhs_numeric.as_bigint().big_integer()));
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "subtraction");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "subtraction");
return {};
}
@ -626,7 +626,7 @@ Value mul(Interpreter& interpreter, Value lhs, Value rhs)
return Value(lhs_numeric.as_double() * rhs_numeric.as_double());
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, lhs_numeric.as_bigint().big_integer().multiplied_by(rhs_numeric.as_bigint().big_integer()));
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "multiplication");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "multiplication");
return {};
}
@ -642,7 +642,7 @@ Value div(Interpreter& interpreter, Value lhs, Value rhs)
return Value(lhs_numeric.as_double() / rhs_numeric.as_double());
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, lhs_numeric.as_bigint().big_integer().divided_by(rhs_numeric.as_bigint().big_integer()).quotient);
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "division");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "division");
return {};
}
@ -664,7 +664,7 @@ Value mod(Interpreter& interpreter, Value lhs, Value rhs)
}
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, lhs_numeric.as_bigint().big_integer().divided_by(rhs_numeric.as_bigint().big_integer()).remainder);
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "modulo");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "modulo");
return {};
}
@ -680,14 +680,14 @@ Value exp(Interpreter& interpreter, Value lhs, Value rhs)
return Value(pow(lhs_numeric.as_double(), rhs_numeric.as_double()));
if (both_bigint(lhs_numeric, rhs_numeric))
return js_bigint(interpreter, Crypto::NumberTheory::Power(lhs_numeric.as_bigint().big_integer(), rhs_numeric.as_bigint().big_integer()));
interpreter.throw_exception<TypeError>(ErrorType::BigIntBadOperatorOtherType, "exponentiation");
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::BigIntBadOperatorOtherType, "exponentiation");
return {};
}
Value in(Interpreter& interpreter, Value lhs, Value rhs)
{
if (!rhs.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::InOperatorWithObject);
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::InOperatorWithObject);
return {};
}
auto lhs_string = lhs.to_string(interpreter);
@ -699,13 +699,13 @@ Value in(Interpreter& interpreter, Value lhs, Value rhs)
Value instance_of(Interpreter& interpreter, Value lhs, Value rhs)
{
if (!rhs.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAnObject, rhs.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::NotAnObject, rhs.to_string_without_side_effects().characters());
return {};
}
auto has_instance_method = rhs.as_object().get(interpreter.vm().well_known_symbol_has_instance());
if (!has_instance_method.is_empty()) {
if (!has_instance_method.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, has_instance_method.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::NotAFunction, has_instance_method.to_string_without_side_effects().characters());
return {};
}
@ -713,7 +713,7 @@ Value instance_of(Interpreter& interpreter, Value lhs, Value rhs)
}
if (!rhs.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, rhs.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::NotAFunction, rhs.to_string_without_side_effects().characters());
return {};
}
return ordinary_has_instance(interpreter, lhs, rhs);
@ -739,7 +739,7 @@ Value ordinary_has_instance(Interpreter& interpreter, Value lhs, Value rhs)
return {};
if (!rhs_prototype.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::InstanceOfOperatorBadPrototype, rhs_prototype.to_string_without_side_effects().characters());
interpreter.vm().throw_exception<TypeError>(interpreter.global_object(), ErrorType::InstanceOfOperatorBadPrototype, rhs_prototype.to_string_without_side_effects().characters());
return {};
}
while (true) {