mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 01:08:11 +00:00
LibJS/Bytecode: Add codegen for ImportCall
Also moved most of the AST ImportCall::execute() into a helper so we can share the code.
This commit is contained in:
parent
eb9298b54e
commit
8a5e71256d
8 changed files with 160 additions and 89 deletions
|
@ -3485,11 +3485,6 @@ Completion ImportCall::execute(Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
InterpreterNodeScope node_scope { interpreter, *this };
|
InterpreterNodeScope node_scope { interpreter, *this };
|
||||||
auto& vm = interpreter.vm();
|
auto& vm = interpreter.vm();
|
||||||
auto& realm = *vm.current_realm();
|
|
||||||
|
|
||||||
// 2.1.1.1 EvaluateImportCall ( specifierExpression [ , optionsExpression ] ), https://tc39.es/proposal-import-assertions/#sec-evaluate-import-call
|
|
||||||
// 1. Let referencingScriptOrModule be GetActiveScriptOrModule().
|
|
||||||
auto referencing_script_or_module = vm.get_active_script_or_module();
|
|
||||||
|
|
||||||
// 2. Let specifierRef be the result of evaluating specifierExpression.
|
// 2. Let specifierRef be the result of evaluating specifierExpression.
|
||||||
// 3. Let specifier be ? GetValue(specifierRef).
|
// 3. Let specifier be ? GetValue(specifierRef).
|
||||||
|
@ -3506,88 +3501,7 @@ Completion ImportCall::execute(Interpreter& interpreter) const
|
||||||
// a. Let options be undefined.
|
// a. Let options be undefined.
|
||||||
// Note: options_value is undefined by default.
|
// Note: options_value is undefined by default.
|
||||||
|
|
||||||
// 6. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
return perform_import_call(vm, *specifier, options_value);
|
||||||
auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
|
|
||||||
|
|
||||||
// 7. Let specifierString be Completion(ToString(specifier)).
|
|
||||||
// 8. IfAbruptRejectPromise(specifierString, promiseCapability).
|
|
||||||
auto specifier_string = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, specifier->to_deprecated_string(vm));
|
|
||||||
|
|
||||||
// 9. Let assertions be a new empty List.
|
|
||||||
Vector<ModuleRequest::Assertion> assertions;
|
|
||||||
|
|
||||||
// 10. If options is not undefined, then
|
|
||||||
if (!options_value.is_undefined()) {
|
|
||||||
// a. If Type(options) is not Object,
|
|
||||||
if (!options_value.is_object()) {
|
|
||||||
auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptions")));
|
|
||||||
// i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
|
||||||
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
|
|
||||||
|
|
||||||
// ii. Return promiseCapability.[[Promise]].
|
|
||||||
return Value { promise_capability->promise() };
|
|
||||||
}
|
|
||||||
|
|
||||||
// b. Let assertionsObj be Get(options, "assert").
|
|
||||||
// c. IfAbruptRejectPromise(assertionsObj, promiseCapability).
|
|
||||||
auto assertion_object = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, options_value.get(vm, vm.names.assert));
|
|
||||||
|
|
||||||
// d. If assertionsObj is not undefined,
|
|
||||||
if (!assertion_object.is_undefined()) {
|
|
||||||
// i. If Type(assertionsObj) is not Object,
|
|
||||||
if (!assertion_object.is_object()) {
|
|
||||||
auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptionsAssertions")));
|
|
||||||
// 1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
|
||||||
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
|
|
||||||
|
|
||||||
// 2. Return promiseCapability.[[Promise]].
|
|
||||||
return Value { promise_capability->promise() };
|
|
||||||
}
|
|
||||||
|
|
||||||
// ii. Let keys be EnumerableOwnPropertyNames(assertionsObj, key).
|
|
||||||
// iii. IfAbruptRejectPromise(keys, promiseCapability).
|
|
||||||
auto keys = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, assertion_object.as_object().enumerable_own_property_names(Object::PropertyKind::Key));
|
|
||||||
|
|
||||||
// iv. Let supportedAssertions be ! HostGetSupportedImportAssertions().
|
|
||||||
auto supported_assertions = vm.host_get_supported_import_assertions();
|
|
||||||
|
|
||||||
// v. For each String key of keys,
|
|
||||||
for (auto const& key : keys) {
|
|
||||||
auto property_key = MUST(key.to_property_key(vm));
|
|
||||||
|
|
||||||
// 1. Let value be Get(assertionsObj, key).
|
|
||||||
// 2. IfAbruptRejectPromise(value, promiseCapability).
|
|
||||||
auto value = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, assertion_object.get(vm, property_key));
|
|
||||||
|
|
||||||
// 3. If Type(value) is not String, then
|
|
||||||
if (!value.is_string()) {
|
|
||||||
auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAString.message(), "Import Assertion option value")));
|
|
||||||
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
|
||||||
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
|
|
||||||
|
|
||||||
// b. Return promiseCapability.[[Promise]].
|
|
||||||
return Value { promise_capability->promise() };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. If supportedAssertions contains key, then
|
|
||||||
if (supported_assertions.contains_slow(property_key.to_string())) {
|
|
||||||
// a. Append { [[Key]]: key, [[Value]]: value } to assertions.
|
|
||||||
assertions.empend(property_key.to_string(), TRY(value.as_string().deprecated_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// e. Sort assertions by the code point order of the [[Key]] of each element. NOTE: This sorting is observable only in that hosts are prohibited from distinguishing among assertions by the order they occur in.
|
|
||||||
// Note: This is done when constructing the ModuleRequest.
|
|
||||||
}
|
|
||||||
|
|
||||||
// 11. Let moduleRequest be a new ModuleRequest Record { [[Specifier]]: specifierString, [[Assertions]]: assertions }.
|
|
||||||
ModuleRequest request { specifier_string, assertions };
|
|
||||||
|
|
||||||
// 12. Perform HostImportModuleDynamically(referencingScriptOrModule, moduleRequest, promiseCapability).
|
|
||||||
MUST_OR_THROW_OOM(interpreter.vm().host_import_module_dynamically(referencing_script_or_module, move(request), promise_capability));
|
|
||||||
|
|
||||||
// 13. Return promiseCapability.[[Promise]].
|
|
||||||
return Value { promise_capability->promise() };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.2.3.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-literals-runtime-semantics-evaluation
|
// 13.2.3.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-literals-runtime-semantics-evaluation
|
||||||
|
|
|
@ -2025,6 +2025,7 @@ public:
|
||||||
|
|
||||||
virtual void dump(int indent) const override;
|
virtual void dump(int indent) const override;
|
||||||
virtual Completion execute(Interpreter&) const override;
|
virtual Completion execute(Interpreter&) const override;
|
||||||
|
virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool is_import_call() const override { return true; }
|
virtual bool is_import_call() const override { return true; }
|
||||||
|
|
|
@ -2725,4 +2725,22 @@ Bytecode::CodeGenerationErrorOr<void> OptionalChain::generate_bytecode(Bytecode:
|
||||||
return generate_optional_chain(generator, *this, current_value_register, current_base_register);
|
return generate_optional_chain(generator, *this, current_value_register, current_base_register);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bytecode::CodeGenerationErrorOr<void> ImportCall::generate_bytecode(Bytecode::Generator& generator) const
|
||||||
|
{
|
||||||
|
TRY(m_specifier->generate_bytecode(generator));
|
||||||
|
auto specifier_reg = generator.allocate_register();
|
||||||
|
generator.emit<Bytecode::Op::Store>(specifier_reg);
|
||||||
|
|
||||||
|
if (m_options) {
|
||||||
|
TRY(m_options->generate_bytecode(generator));
|
||||||
|
} else {
|
||||||
|
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
|
||||||
|
}
|
||||||
|
auto options_reg = generator.allocate_register();
|
||||||
|
generator.emit<Bytecode::Op::Store>(options_reg);
|
||||||
|
|
||||||
|
generator.emit<Bytecode::Op::ImportCall>(specifier_reg, options_reg);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
O(GetVariable) \
|
O(GetVariable) \
|
||||||
O(GreaterThan) \
|
O(GreaterThan) \
|
||||||
O(GreaterThanEquals) \
|
O(GreaterThanEquals) \
|
||||||
|
O(ImportCall) \
|
||||||
O(In) \
|
O(In) \
|
||||||
O(Increment) \
|
O(Increment) \
|
||||||
O(InstanceOf) \
|
O(InstanceOf) \
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
||||||
*
|
*
|
||||||
|
@ -244,6 +244,23 @@ ThrowCompletionOr<void> Append::execute_impl(Bytecode::Interpreter& interpreter)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> ImportCall::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
auto& vm = interpreter.vm();
|
||||||
|
auto specifier = interpreter.reg(m_specifier);
|
||||||
|
auto options_value = interpreter.reg(m_options);
|
||||||
|
interpreter.accumulator() = TRY(perform_import_call(vm, specifier, options_value));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportCall::replace_references_impl(Register from, Register to)
|
||||||
|
{
|
||||||
|
if (m_specifier == from)
|
||||||
|
m_specifier = to;
|
||||||
|
if (m_options == from)
|
||||||
|
m_options = to;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Since the accumulator is a Value, we store an object there and have to convert back and forth between that an Iterator records. Not great.
|
// FIXME: Since the accumulator is a Value, we store an object there and have to convert back and forth between that an Iterator records. Not great.
|
||||||
// Make sure to put this into the accumulator before the iterator object disappears from the stack to prevent the members from being GC'd.
|
// Make sure to put this into the accumulator before the iterator object disappears from the stack to prevent the members from being GC'd.
|
||||||
static Object* iterator_to_object(VM& vm, Iterator iterator)
|
static Object* iterator_to_object(VM& vm, Iterator iterator)
|
||||||
|
@ -1552,4 +1569,9 @@ DeprecatedString BlockDeclarationInstantiation::to_deprecated_string_impl(Byteco
|
||||||
return "BlockDeclarationInstantiation"sv;
|
return "BlockDeclarationInstantiation"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeprecatedString ImportCall::to_deprecated_string_impl(Bytecode::Executable const&) const
|
||||||
|
{
|
||||||
|
return DeprecatedString::formatted("ImportCall specifier:{} options:{}"sv, m_specifier, m_options);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
* Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
|
||||||
*
|
*
|
||||||
|
@ -351,6 +351,25 @@ private:
|
||||||
bool m_is_spread = false;
|
bool m_is_spread = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ImportCall final : public Instruction {
|
||||||
|
public:
|
||||||
|
ImportCall(Register specifier, Register options)
|
||||||
|
: Instruction(Type::ImportCall)
|
||||||
|
, m_specifier(specifier)
|
||||||
|
, m_options(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||||
|
DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const;
|
||||||
|
void replace_references_impl(BasicBlock const&, BasicBlock const&) { }
|
||||||
|
void replace_references_impl(Register, Register);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Register m_specifier;
|
||||||
|
Register m_options;
|
||||||
|
};
|
||||||
|
|
||||||
class IteratorToArray final : public Instruction {
|
class IteratorToArray final : public Instruction {
|
||||||
public:
|
public:
|
||||||
IteratorToArray()
|
IteratorToArray()
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
#include <LibJS/Runtime/ObjectEnvironment.h>
|
#include <LibJS/Runtime/ObjectEnvironment.h>
|
||||||
|
#include <LibJS/Runtime/PromiseCapability.h>
|
||||||
|
#include <LibJS/Runtime/PromiseConstructor.h>
|
||||||
#include <LibJS/Runtime/PropertyDescriptor.h>
|
#include <LibJS/Runtime/PropertyDescriptor.h>
|
||||||
#include <LibJS/Runtime/PropertyKey.h>
|
#include <LibJS/Runtime/PropertyKey.h>
|
||||||
#include <LibJS/Runtime/ProxyObject.h>
|
#include <LibJS/Runtime/ProxyObject.h>
|
||||||
|
@ -1472,4 +1474,96 @@ Completion dispose_resources(VM& vm, GCPtr<DeclarativeEnvironment> disposable, C
|
||||||
return completion;
|
return completion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<Value> perform_import_call(VM& vm, Value specifier, Value options_value)
|
||||||
|
{
|
||||||
|
auto& realm = *vm.current_realm();
|
||||||
|
|
||||||
|
// 2.1.1.1 EvaluateImportCall ( specifierExpression [ , optionsExpression ] ), https://tc39.es/proposal-import-assertions/#sec-evaluate-import-call
|
||||||
|
// 1. Let referencingScriptOrModule be GetActiveScriptOrModule().
|
||||||
|
auto referencing_script_or_module = vm.get_active_script_or_module();
|
||||||
|
|
||||||
|
// 6. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||||
|
auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
|
||||||
|
|
||||||
|
// 7. Let specifierString be Completion(ToString(specifier)).
|
||||||
|
// 8. IfAbruptRejectPromise(specifierString, promiseCapability).
|
||||||
|
auto specifier_string = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, specifier.to_deprecated_string(vm));
|
||||||
|
|
||||||
|
// 9. Let assertions be a new empty List.
|
||||||
|
Vector<ModuleRequest::Assertion> assertions;
|
||||||
|
|
||||||
|
// 10. If options is not undefined, then
|
||||||
|
if (!options_value.is_undefined()) {
|
||||||
|
// a. If Type(options) is not Object,
|
||||||
|
if (!options_value.is_object()) {
|
||||||
|
auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptions")));
|
||||||
|
// i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||||
|
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
|
||||||
|
|
||||||
|
// ii. Return promiseCapability.[[Promise]].
|
||||||
|
return Value { promise_capability->promise() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// b. Let assertionsObj be Get(options, "assert").
|
||||||
|
// c. IfAbruptRejectPromise(assertionsObj, promiseCapability).
|
||||||
|
auto assertion_object = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, options_value.get(vm, vm.names.assert));
|
||||||
|
|
||||||
|
// d. If assertionsObj is not undefined,
|
||||||
|
if (!assertion_object.is_undefined()) {
|
||||||
|
// i. If Type(assertionsObj) is not Object,
|
||||||
|
if (!assertion_object.is_object()) {
|
||||||
|
auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptionsAssertions")));
|
||||||
|
// 1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||||
|
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
|
||||||
|
|
||||||
|
// 2. Return promiseCapability.[[Promise]].
|
||||||
|
return Value { promise_capability->promise() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ii. Let keys be EnumerableOwnPropertyNames(assertionsObj, key).
|
||||||
|
// iii. IfAbruptRejectPromise(keys, promiseCapability).
|
||||||
|
auto keys = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, assertion_object.as_object().enumerable_own_property_names(Object::PropertyKind::Key));
|
||||||
|
|
||||||
|
// iv. Let supportedAssertions be ! HostGetSupportedImportAssertions().
|
||||||
|
auto supported_assertions = vm.host_get_supported_import_assertions();
|
||||||
|
|
||||||
|
// v. For each String key of keys,
|
||||||
|
for (auto const& key : keys) {
|
||||||
|
auto property_key = MUST(key.to_property_key(vm));
|
||||||
|
|
||||||
|
// 1. Let value be Get(assertionsObj, key).
|
||||||
|
// 2. IfAbruptRejectPromise(value, promiseCapability).
|
||||||
|
auto value = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, assertion_object.get(vm, property_key));
|
||||||
|
|
||||||
|
// 3. If Type(value) is not String, then
|
||||||
|
if (!value.is_string()) {
|
||||||
|
auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAString.message(), "Import Assertion option value")));
|
||||||
|
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||||
|
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
|
||||||
|
|
||||||
|
// b. Return promiseCapability.[[Promise]].
|
||||||
|
return Value { promise_capability->promise() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. If supportedAssertions contains key, then
|
||||||
|
if (supported_assertions.contains_slow(property_key.to_string())) {
|
||||||
|
// a. Append { [[Key]]: key, [[Value]]: value } to assertions.
|
||||||
|
assertions.empend(property_key.to_string(), TRY(value.as_string().deprecated_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// e. Sort assertions by the code point order of the [[Key]] of each element. NOTE: This sorting is observable only in that hosts are prohibited from distinguishing among assertions by the order they occur in.
|
||||||
|
// Note: This is done when constructing the ModuleRequest.
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11. Let moduleRequest be a new ModuleRequest Record { [[Specifier]]: specifierString, [[Assertions]]: assertions }.
|
||||||
|
ModuleRequest request { specifier_string, assertions };
|
||||||
|
|
||||||
|
// 12. Perform HostImportModuleDynamically(referencingScriptOrModule, moduleRequest, promiseCapability).
|
||||||
|
MUST_OR_THROW_OOM(vm.host_import_module_dynamically(referencing_script_or_module, move(request), promise_capability));
|
||||||
|
|
||||||
|
// 13. Return promiseCapability.[[Promise]].
|
||||||
|
return Value { promise_capability->promise() };
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,8 @@ Completion dispose(VM& vm, Value, NonnullGCPtr<FunctionObject> method);
|
||||||
Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposable, Completion completion);
|
Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposable, Completion completion);
|
||||||
Completion dispose_resources(VM& vm, GCPtr<DeclarativeEnvironment> disposable, Completion completion);
|
Completion dispose_resources(VM& vm, GCPtr<DeclarativeEnvironment> disposable, Completion completion);
|
||||||
|
|
||||||
|
ThrowCompletionOr<Value> perform_import_call(VM&, Value specifier, Value options_value);
|
||||||
|
|
||||||
enum class CanonicalIndexMode {
|
enum class CanonicalIndexMode {
|
||||||
DetectNumericRoundtrip,
|
DetectNumericRoundtrip,
|
||||||
IgnoreNumericRoundtrip,
|
IgnoreNumericRoundtrip,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue