diff options
author | Andreas Kling <kling@serenityos.org> | 2020-09-27 15:18:55 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-27 20:26:58 +0200 |
commit | 6861c619c6768ae47b546ff27ff5c48bcdc62e15 (patch) | |
tree | 54dea8a80fc66bf15c481f69b8e1c0c600bcd115 | |
parent | 838d9fa251ed34289cb9c77eb46f889dc9e79416 (diff) | |
download | serenity-6861c619c6768ae47b546ff27ff5c48bcdc62e15.zip |
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.
48 files changed, 757 insertions, 718 deletions
diff --git a/Applications/Browser/BrowserConsoleClient.cpp b/Applications/Browser/BrowserConsoleClient.cpp index d7b54c06ce..8380216122 100644 --- a/Applications/Browser/BrowserConsoleClient.cpp +++ b/Applications/Browser/BrowserConsoleClient.cpp @@ -41,7 +41,7 @@ namespace Browser { JS::Value BrowserConsoleClient::log() { - m_console_widget.print_html(interpreter().join_arguments()); + m_console_widget.print_html(interpreter().vm().join_arguments()); return JS::js_undefined(); } @@ -50,7 +50,7 @@ JS::Value BrowserConsoleClient::info() StringBuilder html; html.append("<span class=\"info\">"); html.append("(i) "); - html.append(interpreter().join_arguments()); + html.append(interpreter().vm().join_arguments()); html.append("</span>"); m_console_widget.print_html(html.string_view()); return JS::js_undefined(); @@ -61,7 +61,7 @@ JS::Value BrowserConsoleClient::debug() StringBuilder html; html.append("<span class=\"debug\">"); html.append("(d) "); - html.append(interpreter().join_arguments()); + html.append(interpreter().vm().join_arguments()); html.append("</span>"); m_console_widget.print_html(html.string_view()); return JS::js_undefined(); @@ -72,7 +72,7 @@ JS::Value BrowserConsoleClient::warn() StringBuilder html; html.append("<span class=\"warn\">"); html.append("(w) "); - html.append(interpreter().join_arguments()); + html.append(interpreter().vm().join_arguments()); html.append("</span>"); m_console_widget.print_html(html.string_view()); return JS::js_undefined(); @@ -83,7 +83,7 @@ JS::Value BrowserConsoleClient::error() StringBuilder html; html.append("<span class=\"error\">"); html.append("(e) "); - html.append(interpreter().join_arguments()); + html.append(interpreter().vm().join_arguments()); html.append("</span>"); m_console_widget.print_html(html.string_view()); return JS::js_undefined(); @@ -98,7 +98,7 @@ JS::Value BrowserConsoleClient::clear() JS::Value BrowserConsoleClient::trace() { StringBuilder html; - html.append(interpreter().join_arguments()); + html.append(interpreter().vm().join_arguments()); auto trace = get_trace(); for (auto& function_name : trace) { if (function_name.is_empty()) diff --git a/Applications/Browser/ConsoleWidget.cpp b/Applications/Browser/ConsoleWidget.cpp index e19a68ea38..5ef3e545a7 100644 --- a/Applications/Browser/ConsoleWidget.cpp +++ b/Applications/Browser/ConsoleWidget.cpp @@ -92,7 +92,7 @@ ConsoleWidget::ConsoleWidget() auto hint = error.source_location_hint(js_source); if (!hint.is_empty()) output_html.append(String::format("<pre>%s</pre>", escape_html_entities(hint).characters())); - m_interpreter->throw_exception<JS::SyntaxError>(error.to_string()); + m_interpreter->vm().throw_exception<JS::SyntaxError>(m_interpreter->global_object(), error.to_string()); } else { m_interpreter->run(m_interpreter->global_object(), *program); } @@ -106,7 +106,7 @@ ConsoleWidget::ConsoleWidget() return; } - print_html(JS::MarkupGenerator::html_from_value(m_interpreter->last_value())); + print_html(JS::MarkupGenerator::html_from_value(m_interpreter->vm().last_value())); }; auto& clear_button = bottom_container.add<GUI::Button>(); diff --git a/Applications/Spreadsheet/JSIntegration.cpp b/Applications/Spreadsheet/JSIntegration.cpp index 54f48a5ab5..4ffa06308d 100644 --- a/Applications/Spreadsheet/JSIntegration.cpp +++ b/Applications/Spreadsheet/JSIntegration.cpp @@ -87,12 +87,12 @@ void SheetGlobalObject::initialize() JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::parse_cell_name) { if (interpreter.argument_count() != 1) { - interpreter.throw_exception<JS::TypeError>("Expected exactly one argument to parse_cell_name()"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, "Expected exactly one argument to parse_cell_name()"); return {}; } auto name_value = interpreter.argument(0); if (!name_value.is_string()) { - interpreter.throw_exception<JS::TypeError>("Expected a String argument to parse_cell_name()"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, "Expected a String argument to parse_cell_name()"); return {}; } auto position = Sheet::parse_cell_name(name_value.as_string().string()); @@ -125,12 +125,12 @@ void WorkbookObject::initialize(JS::GlobalObject& global_object) JS_DEFINE_NATIVE_FUNCTION(WorkbookObject::sheet) { if (interpreter.argument_count() != 1) { - interpreter.throw_exception<JS::TypeError>("Expected exactly one argument to sheet()"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, "Expected exactly one argument to sheet()"); return {}; } auto name_value = interpreter.argument(0); if (!name_value.is_string() && !name_value.is_number()) { - interpreter.throw_exception<JS::TypeError>("Expected a String or Number argument to sheet()"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, "Expected a String or Number argument to sheet()"); return {}; } @@ -139,7 +139,7 @@ JS_DEFINE_NATIVE_FUNCTION(WorkbookObject::sheet) return {}; if (!this_object->inherits("WorkbookObject")) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, "WorkbookObject"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WorkbookObject"); return {}; } diff --git a/Applications/Spreadsheet/Spreadsheet.cpp b/Applications/Spreadsheet/Spreadsheet.cpp index 07a0164ccf..bc97b60330 100644 --- a/Applications/Spreadsheet/Spreadsheet.cpp +++ b/Applications/Spreadsheet/Spreadsheet.cpp @@ -164,7 +164,7 @@ JS::Value Sheet::evaluate(const StringView& source, Cell* on_behalf_of) return exc; } - auto value = interpreter().last_value(); + auto value = interpreter().vm().last_value(); if (value.is_empty()) return JS::js_undefined(); return value; diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index d2a8a85cac..3ec1aa3efc 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -86,7 +86,7 @@ static String get_function_name(Interpreter& interpreter, Value value) Value ScopeNode::execute(Interpreter& interpreter, GlobalObject& global_object) const { - return interpreter.execute_statement(global_object, *this); + return interpreter.vm().execute_statement(global_object, *this); } Value FunctionDeclaration::execute(Interpreter&, GlobalObject&) const @@ -113,7 +113,7 @@ CallExpression::ThisAndCallee CallExpression::compute_this_and_callee(Interprete if (m_callee->is_super_expression()) { // If we are calling super, |this| has not been initalized yet, and would not be meaningful to provide. - auto new_target = interpreter.get_new_target(); + auto new_target = interpreter.vm().get_new_target(); ASSERT(new_target.is_function()); return { js_undefined(), new_target }; } @@ -125,7 +125,7 @@ CallExpression::ThisAndCallee CallExpression::compute_this_and_callee(Interprete if (interpreter.exception()) return {}; if (is_super_property_lookup && (lookup_target.is_null() || lookup_target.is_undefined())) { - interpreter.throw_exception<TypeError>(ErrorType::ObjectPrototypeNullOrUndefinedOnSuperPropertyAccess, lookup_target.to_string_without_side_effects().characters()); + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ObjectPrototypeNullOrUndefinedOnSuperPropertyAccess, lookup_target.to_string_without_side_effects().characters()); return {}; } @@ -160,9 +160,9 @@ Value CallExpression::execute(Interpreter& interpreter, GlobalObject& global_obj } else { expression_string = static_cast<const MemberExpression&>(*m_callee).to_string_approximation(); } - interpreter.throw_exception<TypeError>(ErrorType::IsNotAEvaluatedFrom, callee.to_string_without_side_effects().characters(), call_type, expression_string.characters()); + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::IsNotAEvaluatedFrom, callee.to_string_without_side_effects().characters(), call_type, expression_string.characters()); } else { - interpreter.throw_exception<TypeError>(ErrorType::IsNotA, callee.to_string_without_side_effects().characters(), call_type); + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::IsNotA, callee.to_string_without_side_effects().characters(), call_type); } return {}; } @@ -192,17 +192,17 @@ Value CallExpression::execute(Interpreter& interpreter, GlobalObject& global_obj Object* new_object = nullptr; Value result; if (is_new_expression()) { - result = interpreter.construct(function, function, move(arguments), global_object); + result = interpreter.vm().construct(function, function, move(arguments), global_object); if (result.is_object()) new_object = &result.as_object(); } else if (m_callee->is_super_expression()) { auto* super_constructor = interpreter.current_environment()->current_function()->prototype(); // FIXME: Functions should track their constructor kind. if (!super_constructor || !super_constructor->is_function()) { - interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, "Super constructor"); + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAConstructor, "Super constructor"); return {}; } - result = interpreter.construct(static_cast<Function&>(*super_constructor), function, move(arguments), global_object); + result = interpreter.vm().construct(static_cast<Function&>(*super_constructor), function, move(arguments), global_object); if (interpreter.exception()) return {}; @@ -227,7 +227,7 @@ Value ReturnStatement::execute(Interpreter& interpreter, GlobalObject& global_ob auto value = argument() ? argument()->execute(interpreter, global_object) : js_undefined(); if (interpreter.exception()) return {}; - interpreter.unwind(ScopeType::Function); + interpreter.vm().unwind(ScopeType::Function); return value; } @@ -238,10 +238,10 @@ Value IfStatement::execute(Interpreter& interpreter, GlobalObject& global_object return {}; if (predicate_result.to_boolean()) - return interpreter.execute_statement(global_object, *m_consequent); + return interpreter.vm().execute_statement(global_object, *m_consequent); if (m_alternate) - return interpreter.execute_statement(global_object, *m_alternate); + return interpreter.vm().execute_statement(global_object, *m_alternate); return js_undefined(); } @@ -252,7 +252,7 @@ Value WhileStatement::execute(Interpreter& interpreter, GlobalObject& global_obj while (m_test->execute(interpreter, global_object).to_boolean()) { if (interpreter.exception()) return {}; - last_value = interpreter.execute_statement(global_object, *m_body); + last_value = interpreter.vm().execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; } @@ -266,7 +266,7 @@ Value DoWhileStatement::execute(Interpreter& interpreter, GlobalObject& global_o do { if (interpreter.exception()) return {}; - last_value = interpreter.execute_statement(global_object, *m_body); + last_value = interpreter.vm().execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; } while (m_test->execute(interpreter, global_object).to_boolean()); @@ -283,12 +283,12 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec NonnullRefPtrVector<VariableDeclaration> decls; decls.append(*static_cast<const VariableDeclaration*>(m_init.ptr())); wrapper->add_variables(decls); - interpreter.enter_scope(*wrapper, {}, ScopeType::Block, global_object); + interpreter.vm().enter_scope(*wrapper, {}, ScopeType::Block, global_object); } auto wrapper_cleanup = ScopeGuard([&] { if (wrapper) - interpreter.exit_scope(*wrapper); + interpreter.vm().exit_scope(*wrapper); }); Value last_value = js_undefined(); @@ -306,14 +306,14 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec return {}; if (!test_result.to_boolean()) break; - last_value = interpreter.execute_statement(global_object, *m_body); + last_value = interpreter.vm().execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; - if (interpreter.should_unwind()) { - if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) { - interpreter.stop_unwind(); - } else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) { - interpreter.stop_unwind(); + if (interpreter.vm().should_unwind()) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + interpreter.vm().stop_unwind(); + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + interpreter.vm().stop_unwind(); break; } else { return js_undefined(); @@ -327,14 +327,14 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec } } else { while (true) { - last_value = interpreter.execute_statement(global_object, *m_body); + last_value = interpreter.vm().execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; - if (interpreter.should_unwind()) { - if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) { - interpreter.stop_unwind(); - } else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) { - interpreter.stop_unwind(); + if (interpreter.vm().should_unwind()) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + interpreter.vm().stop_unwind(); + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + interpreter.vm().stop_unwind(); break; } else { return js_undefined(); @@ -359,7 +359,7 @@ static FlyString variable_from_for_declaration(Interpreter& interpreter, GlobalO ASSERT(!variable_declaration->declarations().is_empty()); if (variable_declaration->declaration_kind() != DeclarationKind::Var) { wrapper = create_ast_node<BlockStatement>(); - interpreter.enter_scope(*wrapper, {}, ScopeType::Block, global_object); + interpreter.vm().enter_scope(*wrapper, {}, ScopeType::Block, global_object); } variable_declaration->execute(interpreter, global_object); variable_name = variable_declaration->declarations().first().id().string(); @@ -381,7 +381,7 @@ Value ForInStatement::execute(Interpreter& interpreter, GlobalObject& global_obj auto variable_name = variable_from_for_declaration(interpreter, global_object, m_lhs, wrapper); auto wrapper_cleanup = ScopeGuard([&] { if (wrapper) - interpreter.exit_scope(*wrapper); + interpreter.vm().exit_scope(*wrapper); }); auto last_value = js_undefined(); auto rhs_result = m_rhs->execute(interpreter, global_object); @@ -391,17 +391,17 @@ Value ForInStatement::execute(Interpreter& interpreter, GlobalObject& global_obj while (object) { auto property_names = object->get_own_properties(*object, Object::PropertyKind::Key, true); for (auto& property_name : property_names.as_object().indexed_properties()) { - interpreter.set_variable(variable_name, property_name.value_and_attributes(object).value, global_object); + interpreter.vm().set_variable(variable_name, property_name.value_and_attributes(object).value, global_object); if (interpreter.exception()) return {}; - last_value = interpreter.execute_statement(global_object, *m_body); + last_value = interpreter.vm().execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; - if (interpreter.should_unwind()) { - if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) { - interpreter.stop_unwind(); - } else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) { - interpreter.stop_unwind(); + if (interpreter.vm().should_unwind()) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + interpreter.vm().stop_unwind(); + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + interpreter.vm().stop_unwind(); break; } else { return js_undefined(); @@ -425,7 +425,7 @@ Value ForOfStatement::execute(Interpreter& interpreter, GlobalObject& global_obj auto variable_name = variable_from_for_declaration(interpreter, global_object, m_lhs, wrapper); auto wrapper_cleanup = ScopeGuard([&] { if (wrapper) - interpreter.exit_scope(*wrapper); + interpreter.vm().exit_scope(*wrapper); }); auto last_value = js_undefined(); auto rhs_result = m_rhs->execute(interpreter, global_object); @@ -433,15 +433,15 @@ Value ForOfStatement::execute(Interpreter& interpreter, GlobalObject& global_obj return {}; get_iterator_values(global_object, rhs_result, [&](Value value) { - interpreter.set_variable(variable_name, value, global_object); - last_value = interpreter.execute_statement(global_object, *m_body); + interpreter.vm().set_variable(variable_name, value, global_object); + last_value = interpreter.vm().execute_statement(global_object, *m_body); if (interpreter.exception()) return IterationDecision::Break; - if (interpreter.should_unwind()) { - if (interpreter.should_unwind_until(ScopeType::Continuable, m_label)) { - interpreter.stop_unwind(); - } else if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) { - interpreter.stop_unwind(); + if (interpreter.vm().should_unwind()) { + if (interpreter.vm().should_unwind_until(ScopeType::Continuable, m_label)) { + interpreter.vm().stop_unwind(); + } else if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + interpreter.vm().stop_unwind(); return IterationDecision::Break; } else { return IterationDecision::Break; @@ -453,7 +453,7 @@ Value ForOfStatement::execute(Interpreter& interpreter, GlobalObject& global_obj if (interpreter.exception()) return {}; - if (interpreter.should_unwind()) + if (interpreter.vm().should_unwind()) return js_undefined(); return last_value; } @@ -560,7 +560,7 @@ Reference Expression::to_reference(Interpreter&, GlobalObject&) const Reference Identifier::to_reference(Interpreter& interpreter, GlobalObject&) const { - return interpreter.get_reference(string()); + return interpreter.vm().get_reference(string()); } Reference MemberExpression::to_reference(Interpreter& interpreter, GlobalObject& global_object) const @@ -601,7 +601,7 @@ Value UnaryExpression::execute(Interpreter& interpreter, GlobalObject& global_ob // FIXME: standard recommends checking with is_unresolvable but it ALWAYS return false here if (reference.is_local_variable() || reference.is_global_variable()) { auto name = reference.name(); - lhs_result = interpreter.get_variable(name.to_string(), global_object).value_or(js_undefined()); + lhs_result = interpreter.vm().get_variable(name.to_string(), global_object).value_or(js_undefined()); if (interpreter.exception()) return {}; } @@ -684,7 +684,7 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob if (interpreter.exception()) return {}; if (!super_constructor.is_function() && !super_constructor.is_null()) { - interpreter.throw_exception<TypeError>(ErrorType::ClassDoesNotExtendAConstructorOrNull, super_constructor.to_string_without_side_effects().characters()); + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ClassDoesNotExtendAConstructorOrNull, super_constructor.to_string_without_side_effects().characters()); return {}; } class_constructor->set_constructor_kind(Function::ConstructorKind::Derived); @@ -712,7 +712,7 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob return {}; if (!class_prototype.is_object()) { - interpreter.throw_exception<TypeError>(ErrorType::NotAnObject, "Class prototype"); + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAnObject, "Class prototype"); return {}; } for (const auto& method : m_methods) { @@ -1153,9 +1153,9 @@ void ForOfStatement::dump(int indent) const Value Identifier::execute(Interpreter& interpreter, GlobalObject& global_object) const { - auto value = interpreter.get_variable(string(), global_object); + auto value = interpreter.vm().get_variable(string(), global_object); if (value.is_empty()) { - interpreter.throw_exception<ReferenceError>(ErrorType::UnknownIdentifier, string().characters()); + interpreter.vm().throw_exception<ReferenceError>(global_object, ErrorType::UnknownIdentifier, string().characters()); return {}; } return value; @@ -1180,7 +1180,7 @@ Value SpreadExpression::execute(Interpreter& interpreter, GlobalObject& global_o Value ThisExpression::execute(Interpreter& interpreter, GlobalObject&) const { - return interpreter.resolve_this_binding(); + return interpreter.vm().resolve_this_binding(); } void ThisExpression::dump(int indent) const @@ -1279,7 +1279,7 @@ Value AssignmentExpression::execute(Interpreter& interpreter, GlobalObject& glob return {}; if (reference.is_unresolvable()) { - interpreter.throw_exception<ReferenceError>(ErrorType::InvalidLeftHandAssignment); + interpreter.vm().throw_exception<ReferenceError>(global_object, ErrorType::InvalidLeftHandAssignment); return {}; } update_function_name(rhs_result, get_function_name(interpreter, reference.name().to_value(interpreter))); @@ -1410,7 +1410,7 @@ Value VariableDeclaration::execute(Interpreter& interpreter, GlobalObject& globa return {}; auto variable_name = declarator.id().string(); update_function_name(initalizer_result, variable_name); - interpreter.set_variable(variable_name, initalizer_result, global_object, true); + interpreter.vm().set_variable(variable_name, initalizer_result, global_object, true); } } return js_undefined(); @@ -1723,7 +1723,7 @@ Value TaggedTemplateLiteral::execute(Interpreter& interpreter, GlobalObject& glo if (interpreter.exception()) return {}; if (!tag.is_function()) { - interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, tag.to_string_without_side_effects().characters()); + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::NotAFunction, tag.to_string_without_side_effects().characters()); return {}; } auto& tag_function = tag.as_function(); @@ -1793,12 +1793,12 @@ void ThrowStatement::dump(int indent) const Value TryStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const { - interpreter.execute_statement(global_object, m_block, {}, ScopeType::Try); + interpreter.vm().execute_statement(global_object, m_block, {}, ScopeType::Try); if (auto* exception = interpreter.exception()) { if (m_handler) { interpreter.vm().clear_exception(); ArgumentVector arguments { { m_handler->parameter(), exception->value() } }; - interpreter.execute_statement(global_object, m_handler->body(), move(arguments)); + interpreter.vm().execute_statement(global_object, m_handler->body(), move(arguments)); } } @@ -1807,14 +1807,14 @@ Value TryStatement::execute(Interpreter& interpreter, GlobalObject& global_objec // execute() the finalizer without an exception in our way. auto* previous_exception = interpreter.exception(); interpreter.vm().clear_exception(); - interpreter.stop_unwind(); + interpreter.vm().stop_unwind(); m_finalizer->execute(interpreter, global_object); // If we previously had an exception and the finalizer didn't // throw a new one, restore the old one. // FIXME: This will print debug output in throw_exception() for // a seconds time with INTERPRETER_DEBUG enabled. if (previous_exception && !interpreter.exception()) - interpreter.throw_exception(previous_exception); + interpreter.vm().throw_exception(previous_exception); } return js_undefined(); @@ -1830,9 +1830,9 @@ Value CatchClause::execute(Interpreter&, GlobalObject&) const Value ThrowStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const { auto value = m_argument->execute(interpreter, global_object); - if (interpreter.exception()) + if (interpreter.vm().exception()) return {}; - interpreter.throw_exception(value); + interpreter.vm().throw_exception(global_object, value); return {}; } @@ -1858,9 +1858,9 @@ Value SwitchStatement::execute(Interpreter& interpreter, GlobalObject& global_ob statement.execute(interpreter, global_object); if (interpreter.exception()) return {}; - if (interpreter.should_unwind()) { - if (interpreter.should_unwind_until(ScopeType::Breakable, m_label)) { - interpreter.stop_unwind(); + if (interpreter.vm().should_unwind()) { + if (interpreter.vm().should_unwind_until(ScopeType::Breakable, m_label)) { + interpreter.vm().stop_unwind(); return {}; } return {}; @@ -1878,13 +1878,13 @@ Value SwitchCase::execute(Interpreter&, GlobalObject&) const Value BreakStatement::execute(Interpreter& interpreter, GlobalObject&) const { - interpreter.unwind(ScopeType::Breakable, m_target_label); + interpreter.vm().unwind(ScopeType::Breakable, m_target_label); return js_undefined(); } Value ContinueStatement::execute(Interpreter& interpreter, GlobalObject&) const { - interpreter.unwind(ScopeType::Continuable, m_target_label); + interpreter.vm().unwind(ScopeType::Continuable, m_target_label); return js_undefined(); } diff --git a/Libraries/LibJS/Console.cpp b/Libraries/LibJS/Console.cpp index cd40739ebc..eff5bec2e9 100644 --- a/Libraries/LibJS/Console.cpp +++ b/Libraries/LibJS/Console.cpp @@ -123,7 +123,7 @@ bool Console::counter_reset(String label) Vector<String> ConsoleClient::get_trace() const { Vector<String> trace; - auto& call_stack = m_console.interpreter().call_stack(); + auto& call_stack = m_console.interpreter().vm().call_stack(); // -2 to skip the console.trace() call frame for (ssize_t i = call_stack.size() - 2; i >= 0; --i) trace.append(call_stack[i].function_name); diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 4309074129..f000c42501 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -56,172 +56,32 @@ Interpreter::~Interpreter() Value Interpreter::run(GlobalObject& global_object, const Program& program) { - VM::InterpreterExecutionScope scope(*this); - - ASSERT(!exception()); - - if (m_call_stack.is_empty()) { - CallFrame global_call_frame; - global_call_frame.this_value = &global_object; - global_call_frame.function_name = "(global execution context)"; - global_call_frame.environment = heap().allocate<LexicalEnvironment>(global_object, LexicalEnvironment::EnvironmentRecordType::Global); - global_call_frame.environment->bind_this_value(&global_object); - if (exception()) - return {}; - m_call_stack.append(move(global_call_frame)); - } - - return program.execute(*this, global_object); -} - -Value Interpreter::execute_statement(GlobalObject& global_object, const Statement& statement, ArgumentVector arguments, ScopeType scope_type) -{ - if (!statement.is_scope_node()) - return statement.execute(*this, 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(*this, 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(); -} - -void Interpreter::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 }); - } + ASSERT(!vm().exception()); - 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 Interpreter::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 Interpreter::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()) { - auto possible_match = environment->get(name); - if (possible_match.has_value()) { - if (!first_assignment && possible_match.value().declaration_kind == DeclarationKind::Const) { - throw_exception<TypeError>(ErrorType::InvalidAssignToConst); - return; - } - - environment->set(name, { value, possible_match.value().declaration_kind }); - return; - } - } - } + VM::InterpreterExecutionScope scope(*this); - global_object.put(move(name), move(value)); -} + CallFrame global_call_frame; + global_call_frame.this_value = &global_object; + global_call_frame.function_name = "(global execution context)"; + global_call_frame.environment = heap().allocate<LexicalEnvironment>(global_object, LexicalEnvironment::EnvironmentRecordType::Global); + global_call_frame.environment->bind_this_value(&global_object); + if (vm().exception()) + return {}; + vm().call_stack().append(move(global_call_frame)); -Value Interpreter::get_variable(const FlyString& name, GlobalObject& global_object) -{ - if (m_call_stack.size()) { - for (auto* environment = current_environment(); environment; environment = environment->parent()) { - 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; + auto result = program.execute(*this, global_object); + vm().pop_call_frame(); + return result; } -Reference Interpreter::get_reference(const FlyString& name) +GlobalObject& Interpreter::global_object() { - if (m_call_stack.size()) { - for (auto* environment = current_environment(); environment; environment = environment->parent()) { - auto possible_match = environment->get(name); - if (possible_match.has_value()) - return { Reference::LocalVariable, name }; - } - } - return { Reference::GlobalVariable, name }; + return static_cast<GlobalObject&>(*m_global_object.cell()); } -void Interpreter::gather_roots(HashTable<Cell*>& roots) +const GlobalObject& Interpreter::global_object() const { - 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); - } + return static_cast<const GlobalObject&>(*m_global_object.cell()); } Value Interpreter::call_internal(Function& function, Value this_value, Optional<MarkedValueList> arguments) @@ -230,7 +90,7 @@ Value Interpreter::call_internal(Function& function, Value this_value, Optional< VM::InterpreterExecutionScope scope(*this); - auto& call_frame = push_call_frame(); + auto& call_frame = vm().push_call_frame(); call_frame.function_name = function.name(); call_frame.this_value = function.bound_this().value_or(this_value); call_frame.arguments = function.bound_arguments(); @@ -242,127 +102,8 @@ Value Interpreter::call_internal(Function& function, Value this_value, Optional< call_frame.environment->bind_this_value(call_frame.this_value); auto result = function.call(*this); - pop_call_frame(); + vm().pop_call_frame(); return result; } -Value Interpreter::construct(Function& function, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject& global_object) -{ - auto& call_frame = push_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(*this, new_target); - - this_value = current_environment()->get_this_binding(); - pop_call_frame(); - - // 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 Interpreter::throw_exception(Exception* exception) -{ -#ifdef INTERPRETER_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 - vm().set_exception({}, exception); - unwind(ScopeType::Try); -} - -GlobalObject& Interpreter::global_object() -{ - return static_cast<GlobalObject&>(*m_global_object.cell()); -} - -const GlobalObject& Interpreter::global_object() const -{ - return static_cast<const GlobalObject&>(*m_global_object.cell()); -} - -String Interpreter::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 Interpreter::resolve_this_binding() const -{ - return get_this_environment()->get_this_binding(); -} - -const LexicalEnvironment* Interpreter::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 Interpreter::get_new_target() const -{ - return get_this_environment()->new_target(); -} - } diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 6ba52bc5a3..8d8b40acb2 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -45,35 +45,6 @@ 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 Interpreter : public Weakable<Interpreter> { public: template<typename GlobalObjectType, typename... Args> @@ -106,122 +77,23 @@ public: Value run(GlobalObject&, const Program&); - Value execute_statement(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block); - GlobalObject& global_object(); const GlobalObject& global_object() const; VM& vm() { return *m_vm; } + const VM& vm() const { return *m_vm; } Heap& heap() { return vm().heap(); } Exception* exception() { return vm().exception(); } - 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 gather_roots(HashTable<Cell*>&); - - void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&); - void exit_scope(const ScopeNode&); - - Value construct(Function&, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject&); - - 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() { 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 - { - if (m_scope_stack.is_empty()) - return true; - return m_scope_stack.last().scope_node->in_strict_mode(); - } - - 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; - } - - template<typename T, typename... Args> - void throw_exception(Args&&... args) - { - return throw_exception(T::create(global_object(), forward<Args>(args)...)); - } - - void throw_exception(Exception*); - void throw_exception(Value value) - { - return throw_exception(heap().allocate<Exception>(global_object(), value)); - } - - template<typename T, typename... Args> - void throw_exception(ErrorType type, Args&&... args) - { - return throw_exception(T::create(global_object(), String::format(type.message(), forward<Args>(args)...))); - } - - 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; } - Console& console() { return m_console; } const Console& console() const { return m_console; } - String join_arguments() const; - - Value resolve_this_binding() const; - const LexicalEnvironment* get_this_environment() const; - Value get_new_target() const; + bool in_strict_mode() const { return vm().in_strict_mode(); } + size_t argument_count() const { return vm().argument_count(); } + Value argument(size_t index) const { return vm().argument(index); } + Value this_value(Object& global_object) const { return vm().this_value(global_object); } + LexicalEnvironment* current_environment() { return vm().current_environment(); } + const CallFrame& call_frame() { return vm().call_frame(); } private: explicit Interpreter(VM&); @@ -230,18 +102,8 @@ private: NonnullRefPtr<VM> m_vm; - Value m_last_value; - - Vector<ScopeFrame> m_scope_stack; - Vector<CallFrame> m_call_stack; - Handle<Object> m_global_object; - ScopeType m_unwind_until { ScopeType::None }; - FlyString m_unwind_until_label; - - bool m_underscore_is_last_value { false }; - Console m_console; }; diff --git a/Libraries/LibJS/Runtime/Array.cpp b/Libraries/LibJS/Runtime/Array.cpp index 98948de0ca..0a0ac53b63 100644 --- a/Libraries/LibJS/Runtime/Array.cpp +++ b/Libraries/LibJS/Runtime/Array.cpp @@ -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()); diff --git a/Libraries/LibJS/Runtime/ArrayConstructor.cpp b/Libraries/LibJS/Runtime/ArrayConstructor.cpp index 951c0998a1..d0785fa4da 100644 --- a/Libraries/LibJS/Runtime/ArrayConstructor.cpp +++ b/Libraries/LibJS/Runtime/ArrayConstructor.cpp @@ -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()); diff --git a/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp b/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp index 7e50923b96..62b7400098 100644 --- a/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp +++ b/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp @@ -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(); diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Libraries/LibJS/Runtime/ArrayPrototype.cpp index d30eff31d2..fac4b742c6 100644 --- a/Libraries/LibJS/Runtime/ArrayPrototype.cpp +++ b/Libraries/LibJS/Runtime/ArrayPrototype.cpp @@ -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 {}; } diff --git a/Libraries/LibJS/Runtime/BigIntConstructor.cpp b/Libraries/LibJS/Runtime/BigIntConstructor.cpp index 8eed34a1a7..198c6179e9 100644 --- a/Libraries/LibJS/Runtime/BigIntConstructor.cpp +++ b/Libraries/LibJS/Runtime/BigIntConstructor.cpp @@ -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 {}; } diff --git a/Libraries/LibJS/Runtime/BigIntPrototype.cpp b/Libraries/LibJS/Runtime/BigIntPrototype.cpp index eb2ddc0c17..b2e28bc5ef 100644 --- a/Libraries/LibJS/Runtime/BigIntPrototype.cpp +++ b/Libraries/LibJS/Runtime/BigIntPrototype.cpp @@ -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); diff --git a/Libraries/LibJS/Runtime/BooleanPrototype.cpp b/Libraries/LibJS/Runtime/BooleanPrototype.cpp index 8d99679fd1..cadc070dfe 100644 --- a/Libraries/LibJS/Runtime/BooleanPrototype.cpp +++ b/Libraries/LibJS/Runtime/BooleanPrototype.cpp @@ -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 {}; } diff --git a/Libraries/LibJS/Runtime/DatePrototype.cpp b/Libraries/LibJS/Runtime/DatePrototype.cpp index 3bc8351967..e3c54e24df 100644 --- a/Libraries/LibJS/Runtime/DatePrototype.cpp +++ b/Libraries/LibJS/Runtime/DatePrototype.cpp @@ -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); diff --git a/Libraries/LibJS/Runtime/ErrorPrototype.cpp b/Libraries/LibJS/Runtime/ErrorPrototype.cpp index 867dee8774..baf213f224 100644 --- a/Libraries/LibJS/Runtime/ErrorPrototype.cpp +++ b/Libraries/LibJS/Runtime/ErrorPrototype.cpp @@ -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(); diff --git a/Libraries/LibJS/Runtime/Exception.cpp b/Libraries/LibJS/Runtime/Exception.cpp index a92ec65c2b..190d9c421d 100644 --- a/Libraries/LibJS/Runtime/Exception.cpp +++ b/Libraries/LibJS/Runtime/Exception.cpp @@ -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()) diff --git a/Libraries/LibJS/Runtime/Exception.h b/Libraries/LibJS/Runtime/Exception.h index 2d9f23d01a..76ff86e71f 100644 --- a/Libraries/LibJS/Runtime/Exception.h +++ b/Libraries/LibJS/Runtime/Exception.h @@ -26,6 +26,7 @@ #pragma once +#include <AK/Vector.h> #include <LibJS/Runtime/Cell.h> #include <LibJS/Runtime/Value.h> diff --git a/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Libraries/LibJS/Runtime/FunctionConstructor.cpp index 5af0bfd5a5..861f3c13ea 100644 --- a/Libraries/LibJS/Runtime/FunctionConstructor.cpp +++ b/Libraries/LibJS/Runtime/FunctionConstructor.cpp @@ -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()); diff --git a/Libraries/LibJS/Runtime/FunctionPrototype.cpp b/Libraries/LibJS/Runtime/FunctionPrototype.cpp index f96fbcece7..dc3d5f5489 100644 --- a/Libraries/LibJS/Runtime/FunctionPrototype.cpp +++ b/Libraries/LibJS/Runtime/FunctionPrototype.cpp @@ -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); diff --git a/Libraries/LibJS/Runtime/IteratorOperations.cpp b/Libraries/LibJS/Runtime/IteratorOperations.cpp index 1cb821577e..c662521add 100644 --- a/Libraries/LibJS/Runtime/IteratorOperations.cpp +++ b/Libraries/LibJS/Runtime/IteratorOperations.cpp @@ -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; } diff --git a/Libraries/LibJS/Runtime/JSONObject.cpp b/Libraries/LibJS/Runtime/JSONObject.cpp index 183e814bbe..38120be3eb 100644 --- a/Libraries/LibJS/Runtime/JSONObject.cpp +++ b/Libraries/LibJS/Runtime/JSONObject.cpp @@ -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()); diff --git a/Libraries/LibJS/Runtime/LexicalEnvironment.cpp b/Libraries/LibJS/Runtime/LexicalEnvironment.cpp index 813e52ddf5..470f8ba6ee 100644 --- a/Libraries/LibJS/Runtime/LexicalEnvironment.cpp +++ b/Libraries/LibJS/Runtime/LexicalEnvironment.cpp @@ -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; diff --git a/Libraries/LibJS/Runtime/LexicalEnvironment.h b/Libraries/LibJS/Runtime/LexicalEnvironment.h index a4c9deda13..a6d2173f2c 100644 --- a/Libraries/LibJS/Runtime/LexicalEnvironment.h +++ b/Libraries/LibJS/Runtime/LexicalEnvironment.h @@ -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; diff --git a/Libraries/LibJS/Runtime/NumberPrototype.cpp b/Libraries/LibJS/Runtime/NumberPrototype.cpp index 5f2d92e8df..dad115c058 100644 --- a/Libraries/LibJS/Runtime/NumberPrototype.cpp +++ b/Libraries/LibJS/Runtime/NumberPrototype.cpp @@ -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 {}; } diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index a77c78df95..9e9aaca8ca 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -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(); } } diff --git a/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Libraries/LibJS/Runtime/ObjectConstructor.cpp index 7c09c7da6e..3981a72dc2 100644 --- a/Libraries/LibJS/Runtime/ObjectConstructor.cpp +++ b/Libraries/LibJS/Runtime/ObjectConstructor.cpp @@ -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); diff --git a/Libraries/LibJS/Runtime/ProxyConstructor.cpp b/Libraries/LibJS/Runtime/ProxyConstructor.cpp index c5b76307d5..d1c35803ce 100644 --- a/Libraries/LibJS/Runtime/ProxyConstructor.cpp +++ b/Libraries/LibJS/Runtime/ProxyConstructor.cpp @@ -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()); diff --git a/Libraries/LibJS/Runtime/ProxyObject.cpp b/Libraries/LibJS/Runtime/ProxyObject.cpp index 98318d1cb8..f6b2e874fa 100644 --- a/Libraries/LibJS/Runtime/ProxyObject.cpp +++ b/Libraries/LibJS/Runtime/ProxyObject.cpp @@ -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; diff --git a/Libraries/LibJS/Runtime/Reference.cpp b/Libraries/LibJS/Runtime/Reference.cpp index e670a02370..1d21b4a380 100644 --- a/Libraries/LibJS/Runtime/Reference.cpp +++ b/Libraries/LibJS/Runtime/Reference.cpp @@ -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()) diff --git a/Libraries/LibJS/Runtime/ReflectObject.cpp b/Libraries/LibJS/Runtime/ReflectObject.cpp index ec667690a8..4c08a8d7ec 100644 --- a/Libraries/LibJS/Runtime/ReflectObject.cpp +++ b/Libraries/LibJS/Runtime/ReflectObject.cpp @@ -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; diff --git a/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Libraries/LibJS/Runtime/ScriptFunction.cpp index 8f670f1bc6..ed03190b4f 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.cpp +++ b/Libraries/LibJS/Runtime/ScriptFunction.cpp @@ -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); diff --git a/Libraries/LibJS/Runtime/StringConstructor.cpp b/Libraries/LibJS/Runtime/StringConstructor.cpp index 1edc7a141f..1e7f7404bb 100644 --- a/Libraries/LibJS/Runtime/StringConstructor.cpp +++ b/Libraries/LibJS/Runtime/StringConstructor.cpp @@ -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()) diff --git a/Libraries/LibJS/Runtime/StringIteratorPrototype.cpp b/Libraries/LibJS/Runtime/StringIteratorPrototype.cpp index 05f10c88df..29d3d2fd1a 100644 --- a/Libraries/LibJS/Runtime/StringIteratorPrototype.cpp +++ b/Libraries/LibJS/Runtime/StringIteratorPrototype.cpp @@ -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 {}; } diff --git a/Libraries/LibJS/Runtime/StringPrototype.cpp b/Libraries/LibJS/Runtime/StringPrototype.cpp index 10a2e2ff71..441a82f424 100644 --- a/Libraries/LibJS/Runtime/StringPrototype.cpp +++ b/Libraries/LibJS/Runtime/StringPrototype.cpp @@ -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 {}; } diff --git a/Libraries/LibJS/Runtime/SymbolConstructor.cpp b/Libraries/LibJS/Runtime/SymbolConstructor.cpp index 47ff1283b2..64de9db9ff 100644 --- a/Libraries/LibJS/Runtime/SymbolConstructor.cpp +++ b/Libraries/LibJS/Runtime/SymbolConstructor.cpp @@ -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 {}; } diff --git a/Libraries/LibJS/Runtime/SymbolPrototype.cpp b/Libraries/LibJS/Runtime/SymbolPrototype.cpp index 78cfff84e1..b13faa7c78 100644 --- a/Libraries/LibJS/Runtime/SymbolPrototype.cpp +++ b/Libraries/LibJS/Runtime/SymbolPrototype.cpp @@ -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); diff --git a/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp b/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp index e6163f5923..d16bed089f 100644 --- a/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp +++ b/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp @@ -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()); diff --git a/Libraries/LibJS/Runtime/VM.cpp b/Libraries/LibJS/Runtime/VM.cpp index 37c7341872..9989ca9172 100644 --- a/Libraries/LibJS/Runtime/VM.cpp +++ b/Libraries/LibJS/Runtime/VM.cpp @@ -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(); +} + } diff --git a/Libraries/LibJS/Runtime/VM.h b/Libraries/LibJS/Runtime/VM.h index b099643472..07e522140a 100644 --- a/Libraries/LibJS/Runtime/VM.h +++ b/Libraries/LibJS/Runtime/VM.h @@ -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 }; diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index e637362a20..0b39b08a44 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/Libraries/LibJS/Runtime/Value.cpp @@ -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) { diff --git a/Libraries/LibWeb/Bindings/WindowObject.cpp b/Libraries/LibWeb/Bindings/WindowObject.cpp index 7050b200b6..25540e6293 100644 --- a/Libraries/LibWeb/Bindings/WindowObject.cpp +++ b/Libraries/LibWeb/Bindings/WindowObject.cpp @@ -107,7 +107,7 @@ static DOM::Window* impl_from(JS::Interpreter& interpreter, JS::GlobalObject& gl return nullptr; } if (StringView("WindowObject") != this_object->class_name()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, "WindowObject"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WindowObject"); return nullptr; } return &static_cast<WindowObject*>(this_object)->impl(); @@ -148,14 +148,14 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_interval) if (!impl) return {}; if (!interpreter.argument_count()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountAtLeastOne, "setInterval"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "setInterval"); return {}; } auto* callback_object = interpreter.argument(0).to_object(interpreter, global_object); if (!callback_object) return {}; if (!callback_object->is_function()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotAFunctionNoParam); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam); return {}; } i32 interval = 0; @@ -177,14 +177,14 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_timeout) if (!impl) return {}; if (!interpreter.argument_count()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountAtLeastOne, "setTimeout"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "setTimeout"); return {}; } auto* callback_object = interpreter.argument(0).to_object(interpreter, global_object); if (!callback_object) return {}; if (!callback_object->is_function()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotAFunctionNoParam); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam); return {}; } i32 interval = 0; @@ -206,7 +206,7 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::clear_timeout) if (!impl) return {}; if (!interpreter.argument_count()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountAtLeastOne, "clearTimeout"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "clearTimeout"); return {}; } i32 timer_id = interpreter.argument(0).to_i32(interpreter); @@ -222,7 +222,7 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::clear_interval) if (!impl) return {}; if (!interpreter.argument_count()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountAtLeastOne, "clearInterval"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "clearInterval"); return {}; } i32 timer_id = interpreter.argument(0).to_i32(interpreter); @@ -238,14 +238,14 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::request_animation_frame) if (!impl) return {}; if (!interpreter.argument_count()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountOne, "requestAnimationFrame"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "requestAnimationFrame"); return {}; } auto* callback_object = interpreter.argument(0).to_object(interpreter, global_object); if (!callback_object) return {}; if (!callback_object->is_function()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotAFunctionNoParam); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam); return {}; } return JS::Value(impl->request_animation_frame(*static_cast<JS::Function*>(callback_object))); @@ -257,7 +257,7 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::cancel_animation_frame) if (!impl) return {}; if (!interpreter.argument_count()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountOne, "cancelAnimationFrame"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "cancelAnimationFrame"); return {}; } auto id = interpreter.argument(0).to_i32(interpreter); @@ -273,7 +273,7 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::atob) if (!impl) return {}; if (!interpreter.argument_count()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountOne, "atob"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "atob"); return {}; } auto string = interpreter.argument(0).to_string(interpreter); @@ -291,7 +291,7 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::btoa) if (!impl) return {}; if (!interpreter.argument_count()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountOne, "btoa"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "btoa"); return {}; } auto string = interpreter.argument(0).to_string(interpreter); @@ -302,7 +302,7 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::btoa) byte_string.ensure_capacity(string.length()); for (u32 code_point : Utf8View(string)) { if (code_point > 0xff) { - interpreter.throw_exception<JS::InvalidCharacterError>(JS::ErrorType::NotAByteString, "btoa"); + interpreter.vm().throw_exception<JS::InvalidCharacterError>(global_object, JS::ErrorType::NotAByteString, "btoa"); return {}; } byte_string.append(code_point); diff --git a/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp b/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp index 7d0d066da2..d9e3310e1e 100644 --- a/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp +++ b/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp @@ -64,7 +64,7 @@ static XMLHttpRequest* impl_from(JS::Interpreter& interpreter, JS::GlobalObject& if (!this_object) return nullptr; if (StringView("XMLHttpRequestWrapper") != this_object->class_name()) { - interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, "XMLHttpRequest"); + interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "XMLHttpRequest"); return nullptr; } return &static_cast<XMLHttpRequestWrapper*>(this_object)->impl(); diff --git a/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp index fa5e73d09d..65cd7f3838 100644 --- a/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp +++ b/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp @@ -506,7 +506,7 @@ void generate_implementation(const IDL::Interface& interface) out() << " if (!this_object)"; out() << " return {};"; out() << " if (!this_object->inherits(\"" << wrapper_class << "\")) {"; - out() << " interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, \"" << interface.fully_qualified_name << "\");"; + out() << " interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, \"" << interface.fully_qualified_name << "\");"; out() << " return nullptr;"; out() << " }"; out() << " return &static_cast<" << wrapper_class << "*>(this_object)->impl();"; @@ -526,7 +526,7 @@ void generate_implementation(const IDL::Interface& interface) generate_return(); } else if (parameter.type.name == "EventListener") { out() << " if (!" << js_name << js_suffix << ".is_function()) {"; - out() << " interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, \"Function\");"; + out() << " interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, \"Function\");"; generate_return(); out() << " }"; out() << " auto " << cpp_name << " = adopt(*new EventListener(JS::make_handle(&" << js_name << js_suffix << ".as_function())));"; @@ -535,7 +535,7 @@ void generate_implementation(const IDL::Interface& interface) out() << " if (interpreter.exception())"; generate_return(); out() << " if (!" << cpp_name << "_object->inherits(\"" << parameter.type.name << "Wrapper\")) {"; - out() << " interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, \"" << parameter.type.name << "\");"; + out() << " interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, \"" << parameter.type.name << "\");"; generate_return(); out() << " }"; out() << " auto& " << cpp_name << " = static_cast<" << parameter.type.name << "Wrapper*>(" << cpp_name << "_object)->impl();"; @@ -658,9 +658,9 @@ void generate_implementation(const IDL::Interface& interface) if (function.length() > 0) { out() << " if (interpreter.argument_count() < " << function.length() << ") {"; if (function.length() == 1) - out() << " interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountOne, \"" << function.name << "\");"; + out() << " interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, \"" << function.name << "\");"; else - out() << " interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountMany, \"" << function.name << "\", \"" << function.length() << "\");"; + out() << " interpreter.vm().throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountMany, \"" << function.name << "\", \"" << function.length() << "\");"; out() << " return {};"; out() << " }"; } diff --git a/Userland/js.cpp b/Userland/js.cpp index 5a9dc05834..24b0d05e1e 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -349,7 +349,7 @@ static bool parse_and_run(JS::Interpreter& interpreter, const StringView& source auto hint = error.source_location_hint(source); if (!hint.is_empty()) printf("%s\n", hint.characters()); - interpreter.throw_exception<JS::SyntaxError>(error.to_string()); + vm->throw_exception<JS::SyntaxError>(interpreter.global_object(), error.to_string()); } else { interpreter.run(interpreter.global_object(), *program); } @@ -362,11 +362,11 @@ static bool parse_and_run(JS::Interpreter& interpreter, const StringView& source for (auto& function_name : trace) printf(" -> %s\n", function_name.characters()); } - interpreter.vm().clear_exception(); + vm->clear_exception(); return false; } if (s_print_last_result) - print(interpreter.last_value()); + print(vm->last_value()); return true; } @@ -470,32 +470,32 @@ public: virtual JS::Value log() override { - puts(interpreter().join_arguments().characters()); + puts(interpreter().vm().join_arguments().characters()); return JS::js_undefined(); } virtual JS::Value info() override { - printf("(i) %s\n", interpreter().join_arguments().characters()); + printf("(i) %s\n", interpreter().vm().join_arguments().characters()); return JS::js_undefined(); } virtual JS::Value debug() override { printf("\033[36;1m"); - puts(interpreter().join_arguments().characters()); + puts(interpreter().vm().join_arguments().characters()); printf("\033[0m"); return JS::js_undefined(); } virtual JS::Value warn() override { printf("\033[33;1m"); - puts(interpreter().join_arguments().characters()); + puts(interpreter().vm().join_arguments().characters()); printf("\033[0m"); return JS::js_undefined(); } virtual JS::Value error() override { printf("\033[31;1m"); - puts(interpreter().join_arguments().characters()); + puts(interpreter().vm().join_arguments().characters()); printf("\033[0m"); return JS::js_undefined(); } @@ -507,7 +507,7 @@ public: } virtual JS::Value trace() override { - puts(interpreter().join_arguments().characters()); + puts(interpreter().vm().join_arguments().characters()); auto trace = get_trace(); for (auto& function_name : trace) { if (function_name.is_empty()) @@ -558,7 +558,7 @@ int main(int argc, char** argv) interrupt_interpreter = [&] { auto error = JS::Error::create(interpreter->global_object(), "Error", "Received SIGINT"); - interpreter->throw_exception(error); + vm->throw_exception(interpreter->global_object(), error); }; if (script_path == nullptr) { @@ -567,7 +567,7 @@ int main(int argc, char** argv) ReplConsoleClient console_client(interpreter->console()); interpreter->console().set_client(console_client); interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation); - interpreter->set_underscore_is_last_value(true); + interpreter->vm().set_underscore_is_last_value(true); s_editor = Line::Editor::construct(); @@ -811,7 +811,7 @@ int main(int argc, char** argv) switch (mode) { case CompleteProperty: { - auto maybe_variable = interpreter->get_variable(variable_name, interpreter->global_object()); + auto maybe_variable = vm->get_variable(variable_name, interpreter->global_object()); if (maybe_variable.is_empty()) { maybe_variable = interpreter->global_object().get(FlyString(variable_name)); if (maybe_variable.is_empty()) diff --git a/Userland/test-js.cpp b/Userland/test-js.cpp index a6b9b11f17..6c4b1e056c 100644 --- a/Userland/test-js.cpp +++ b/Userland/test-js.cpp @@ -260,7 +260,7 @@ static Result<NonnullRefPtr<JS::Program>, ParserError> parse_file(const String& static Optional<JsonValue> get_test_results(JS::Interpreter& interpreter) { - auto result = interpreter.get_variable("__TestResults__", interpreter.global_object()); + auto result = interpreter.vm().get_variable("__TestResults__", interpreter.global_object()); auto json_string = JS::JSONObject::stringify_impl(interpreter, interpreter.global_object(), result, JS::js_undefined(), JS::js_undefined()); auto json = JsonValue::from_string(json_string); @@ -310,7 +310,7 @@ JSFileResult TestRunner::run_file_test(const String& test_path) JSFileResult file_result { test_path.substring(m_test_root.length() + 1, test_path.length() - m_test_root.length() - 1) }; // Collect logged messages - auto& arr = interpreter->get_variable("__UserOutput__", interpreter->global_object()).as_array(); + auto& arr = interpreter->vm().get_variable("__UserOutput__", interpreter->global_object()).as_array(); for (auto& entry : arr.indexed_properties()) { auto message = entry.value_and_attributes(&interpreter->global_object()).value; file_result.logged_messages.append(message.to_string_without_side_effects()); @@ -579,7 +579,6 @@ void TestRunner::print_test_results() const int main(int argc, char** argv) { bool print_times = false; - struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_flags = SA_NOCLDWAIT; diff --git a/Userland/test-web.cpp b/Userland/test-web.cpp index 7f493ac6ac..49f77668e5 100644 --- a/Userland/test-web.cpp +++ b/Userland/test-web.cpp @@ -174,11 +174,13 @@ static void cleanup_and_exit() exit(1); } +#if 0 static void handle_sigabrt(int) { dbg() << "test-web: SIGABRT received, cleaning up."; cleanup_and_exit(); } +#endif static double get_time_in_ms() { @@ -288,7 +290,7 @@ static Result<NonnullRefPtr<JS::Program>, ParserError> parse_file(const String& static Optional<JsonValue> get_test_results(JS::Interpreter& interpreter) { - auto result = interpreter.get_variable("__TestResults__", interpreter.global_object()); + auto result = interpreter.vm().get_variable("__TestResults__", interpreter.global_object()); auto json_string = JS::JSONObject::stringify_impl(interpreter, interpreter.global_object(), result, JS::js_undefined(), JS::js_undefined()); auto json = JsonValue::from_string(json_string); @@ -336,7 +338,7 @@ JSFileResult TestRunner::run_file_test(const String& test_path) // Setup the test on the current page to get "__PageToLoad__". old_interpreter.run(old_interpreter.global_object(), *m_web_test_common); old_interpreter.run(old_interpreter.global_object(), *file_program.value()); - auto page_to_load = URL(old_interpreter.get_variable("__PageToLoad__", old_interpreter.global_object()).as_string().string()); + auto page_to_load = URL(old_interpreter.vm().get_variable("__PageToLoad__", old_interpreter.global_object()).as_string().string()); if (!page_to_load.is_valid()) { printf("Invalid page URL for %s", test_path.characters()); cleanup_and_exit(); @@ -360,7 +362,7 @@ JSFileResult TestRunner::run_file_test(const String& test_path) new_interpreter.run(new_interpreter.global_object(), *m_web_test_common); new_interpreter.run(new_interpreter.global_object(), *file_program.value()); - auto& before_initial_page_load = new_interpreter.get_variable("__BeforeInitialPageLoad__", new_interpreter.global_object()).as_function(); + auto& before_initial_page_load = new_interpreter.vm().get_variable("__BeforeInitialPageLoad__", new_interpreter.global_object()).as_function(); (void)new_interpreter.call(before_initial_page_load, JS::js_undefined()); if (new_interpreter.exception()) new_interpreter.vm().clear_exception(); @@ -370,7 +372,7 @@ JSFileResult TestRunner::run_file_test(const String& test_path) m_page_view->set_document(&parser.document()); // Finally run the test by calling "__AfterInitialPageLoad__" - auto& after_initial_page_load = new_interpreter.get_variable("__AfterInitialPageLoad__", new_interpreter.global_object()).as_function(); + auto& after_initial_page_load = new_interpreter.vm().get_variable("__AfterInitialPageLoad__", new_interpreter.global_object()).as_function(); (void)new_interpreter.call(after_initial_page_load, JS::js_undefined()); if (new_interpreter.exception()) new_interpreter.vm().clear_exception(); @@ -384,7 +386,7 @@ JSFileResult TestRunner::run_file_test(const String& test_path) file_result = { test_path.substring(m_web_test_root.length() + 1, test_path.length() - m_web_test_root.length() - 1) }; // Collect logged messages - auto& arr = new_interpreter.get_variable("__UserOutput__", new_interpreter.global_object()).as_array(); + auto& arr = new_interpreter.vm().get_variable("__UserOutput__", new_interpreter.global_object()).as_array(); for (auto& entry : arr.indexed_properties()) { auto message = entry.value_and_attributes(&new_interpreter.global_object()).value; file_result.logged_messages.append(message.to_string_without_side_effects()); @@ -660,6 +662,7 @@ int main(int argc, char** argv) bool print_times = false; bool show_window = false; +#if 0 struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_flags = SA_NOCLDWAIT; @@ -669,6 +672,7 @@ int main(int argc, char** argv) perror("sigaction"); return 1; } +#endif Core::ArgsParser args_parser; args_parser.add_option(print_times, "Show duration of each test", "show-time", 't'); |