Issue #1691 - Part 4: Finish implementing call import. https://bugzilla.mozilla.org/show_bug.cgi?id=1499140

(cherry picked from commit 7bf7d64887944b127a72090dd62eb57f67c5089d)
This commit is contained in:
Brian Smith 2023-04-09 03:06:39 -05:00 committed by roytam1
commit 7a6dab0b38
35 changed files with 533 additions and 24 deletions

View file

@ -5,6 +5,7 @@
#include "builtin/ModuleObject.h"
#include "builtin/Promise.h"
#include "builtin/SelfHostingDefines.h"
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
@ -1030,6 +1031,22 @@ ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self)
return InvokeSelfHostedMethod(cx, self, cx->names().ModuleEvaluate);
}
/* static */ ModuleNamespaceObject*
ModuleObject::GetOrCreateModuleNamespace(JSContext* cx, HandleModuleObject self)
{
FixedInvokeArgs<1> args(cx);
args[0].setObject(*self);
RootedValue result(cx);
if (!CallSelfHostedFunction(cx, cx->names().GetModuleNamespace, UndefinedHandleValue, args,
&result))
{
return nullptr;
}
return &result.toObject().as<ModuleNamespaceObject>();
}
DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, EvaluationErrorSlot)
@ -1519,3 +1536,88 @@ js::GetOrCreateModuleMetaObject(JSContext* cx, HandleObject moduleArg)
return metaObject;
}
JSObject*
js::CallModuleResolveHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier)
{
JS::ModuleResolveHook moduleResolveHook = cx->runtime()->moduleResolveHook;
if (!moduleResolveHook) {
JS_ReportErrorASCII(cx, "Module resolve hook not set");
return nullptr;
}
RootedObject result(cx, moduleResolveHook(cx, referencingPrivate, specifier));
if (!result) {
return nullptr;
}
if (!result->is<ModuleObject>()) {
JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
return nullptr;
}
return result;
}
JSObject*
js::StartDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleValue specifierArg)
{
RootedObject promiseConstructor(cx, JS::GetPromiseConstructor(cx));
if (!promiseConstructor) {
return nullptr;
}
RootedObject promiseObject(cx, JS::NewPromiseObject(cx, nullptr));
if (!promiseObject) {
return nullptr;
}
Handle<PromiseObject*> promise = promiseObject.as<PromiseObject>();
RootedString specifier(cx, ToString(cx, specifierArg));
if (!specifier) {
if (!RejectPromiseWithPendingError(cx, promise))
return nullptr;
return promise;
}
JS::ModuleDynamicImportHook importHook = cx->runtime()->moduleDynamicImportHook;
MOZ_ASSERT(importHook);
if (!importHook(cx, referencingPrivate, specifier, promise)) {
if (!RejectPromiseWithPendingError(cx, promise))
return nullptr;
return promise;
}
return promise;
}
bool
js::FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
HandleObject promiseArg)
{
Handle<PromiseObject*> promise = promiseArg.as<PromiseObject>();
if (cx->isExceptionPending()) {
return RejectPromiseWithPendingError(cx, promise);
}
RootedObject result(cx, CallModuleResolveHook(cx, referencingPrivate, specifier));
if (!result) {
return RejectPromiseWithPendingError(cx, promise);
}
RootedModuleObject module(cx, &result->as<ModuleObject>());
if (module->status() != MODULE_STATUS_EVALUATED) {
JS_ReportErrorASCII(cx, "Unevaluated or errored module returned by module resolve hook");
return RejectPromiseWithPendingError(cx, promise);
}
RootedObject ns(cx, ModuleObject::GetOrCreateModuleNamespace(cx, module));
if (!ns) {
return RejectPromiseWithPendingError(cx, promise);
}
RootedValue value(cx, ObjectValue(*ns));
return PromiseObject::resolve(cx, promise, value);
}

View file

@ -288,6 +288,9 @@ class ModuleObject : public NativeObject
static bool Instantiate(JSContext* cx, HandleModuleObject self);
static bool Evaluate(JSContext* cx, HandleModuleObject self);
static ModuleNamespaceObject* GetOrCreateModuleNamespace(JSContext* cx,
HandleModuleObject self);
void setMetaObject(JSObject* obj);
// For BytecodeEmitter.
@ -374,6 +377,16 @@ class MOZ_STACK_CLASS ModuleBuilder
JSObject*
GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module);
JSObject*
CallModuleResolveHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier);
JSObject*
StartDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleValue specifier);
bool
FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
HandleObject promise);
} // namespace js
template<>

View file

@ -3863,6 +3863,16 @@ OriginalPromiseThenBuiltin(JSContext* cx, HandleValue promiseVal, HandleValue on
return true;
}
MOZ_MUST_USE bool
js::RejectPromiseWithPendingError(JSContext* cx, Handle<PromiseObject*> promise)
{
// Not much we can do about uncatchable exceptions, just bail.
RootedValue exn(cx);
if (!GetAndClearException(cx, &exn))
return false;
return PromiseObject::reject(cx, promise, exn);
}
static MOZ_MUST_USE bool PerformPromiseThenWithReaction(JSContext* cx,
Handle<PromiseObject*> promise,
Handle<PromiseReactionRecord*> reaction);

View file

@ -146,6 +146,9 @@ OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
MOZ_MUST_USE JSObject*
PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value);
MOZ_MUST_USE bool
RejectPromiseWithPendingError(JSContext* cx, Handle<PromiseObject*> promise);
MOZ_MUST_USE PromiseObject*
CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal);

View file

@ -9088,8 +9088,14 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
break;
case PNK_CALL_IMPORT:
reportError(nullptr, JSMSG_NO_DYNAMIC_IMPORT);
return false;
if (!cx->asJSContext()->runtime()->moduleDynamicImportHook) {
reportError(nullptr, JSMSG_NO_DYNAMIC_IMPORT);
return false;
}
if (!emitTree(pn->as<BinaryNode>().right()) || !emit1(JSOP_DYNAMIC_IMPORT)) {
return false;
}
break;
case PNK_SETTHIS:
if (!emitSetThis(&pn->as<BinaryNode>()))

View file

@ -595,7 +595,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
}
BinaryNodeType newCallImport(Node importHolder, Node singleArg) {
return new_<BinaryNode>(PNK_CALL_IMPORT, JSOP_NOP, importHolder, singleArg);
return new_<BinaryNode>(PNK_CALL_IMPORT, JSOP_DYNAMIC_IMPORT, importHolder, singleArg);
}
UnaryNodeType newExprStatement(Node expr, uint32_t end) {

View file

@ -837,7 +837,6 @@ class NameResolver
case PNK_CALL_IMPORT: {
BinaryNode* node = &cur->as<BinaryNode>();
MOZ_ASSERT(cur->isArity(PN_BINARY));
if (!resolve(node->right(), prefix))
return false;
break;

View file

@ -541,6 +541,8 @@ IsTypeofKind(ParseNodeKind kind)
* PNK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP
* pn_kid: array comprehension expression
* PNK_NOP (NullaryNode)
* PNK_IMPORT_META (BinaryNode)
* PNK_CALL_IMPORT (BinaryNode)
*/
enum ParseNodeArity
{

View file

@ -10515,6 +10515,10 @@ Parser<ParseHandler>::importExpr(YieldHandling yieldHandling)
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
if (!context->asJSContext()->runtime()->moduleDynamicImportHook && !abortIfSyntaxParser()) {
return null();
}
return handler.newCallImport(importHolder, arg);
} else {
error(JSMSG_UNEXPECTED_TOKEN, TokenKindToDesc(next));

View file

@ -4708,3 +4708,28 @@ BaselineCompiler::emit_JSOP_IMPORTMETA()
frame.push(ObjectValue(*metaObject));
return true;
}
typedef JSObject* (*StartDynamicModuleImportFn)(JSContext*, HandleValue, HandleValue);
static const VMFunction StartDynamicModuleImportInfo =
FunctionInfo<StartDynamicModuleImportFn>(js::StartDynamicModuleImport,
"StartDynamicModuleImport");
bool
BaselineCompiler::emit_JSOP_DYNAMIC_IMPORT()
{
RootedValue referencingPrivate(cx, FindScriptOrModulePrivateForScript(script));
// Put specifier value in R0.
frame.popRegsAndSync(1);
prepareVMCall();
pushArg(R0);
pushArg(referencingPrivate);
if (!callVM(StartDynamicModuleImportInfo)) {
return false;
}
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
frame.push(R0);
return true;
}

View file

@ -241,7 +241,8 @@ namespace jit {
_(JSOP_JUMPTARGET) \
_(JSOP_IS_CONSTRUCTING) \
_(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \
_(JSOP_IMPORTMETA)
_(JSOP_IMPORTMETA) \
_(JSOP_DYNAMIC_IMPORT)
class BaselineCompiler : public BaselineCompilerSpecific
{

View file

@ -2402,6 +2402,19 @@ CodeGenerator::visitNullarySharedStub(LNullarySharedStub* lir)
}
}
typedef JSObject* (*StartDynamicModuleImportFn)(JSContext*, HandleValue, HandleValue);
static const VMFunction StartDynamicModuleImportInfo =
FunctionInfo<StartDynamicModuleImportFn>(js::StartDynamicModuleImport,
"StartDynamicModuleImport");
void
CodeGenerator::visitDynamicImport(LDynamicImport* lir)
{
pushArg(ToValue(lir, LDynamicImport::SpecifierIndex));
pushArg(ToValue(lir, LDynamicImport::ReferencingPrivateIndex));
callVM(StartDynamicModuleImportInfo, lir);
}
typedef JSObject* (*LambdaFn)(JSContext*, HandleFunction, HandleObject);
static const VMFunction LambdaInfo = FunctionInfo<LambdaFn>(js::Lambda, "Lambda");

View file

@ -442,6 +442,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitRandom(LRandom* ins);
void visitSignExtend(LSignExtend* ins);
void visitDynamicImport(LDynamicImport* lir);
#ifdef DEBUG
void emitDebugForceBailing(LInstruction* lir);

View file

@ -19,6 +19,7 @@
#include "jit/Lowering.h"
#include "jit/MIRGraph.h"
#include "vm/ArgumentsObject.h"
#include "vm/EnvironmentObject.h"
#include "vm/Opcodes.h"
#include "vm/RegExpStatics.h"
#include "vm/TraceLogging.h"
@ -2208,6 +2209,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_IMPORTMETA:
return jsop_importmeta();
case JSOP_DYNAMIC_IMPORT:
return jsop_dynamic_import();
case JSOP_DEBUGCHECKSELFHOSTED:
{
#ifdef DEBUG
@ -14264,6 +14268,20 @@ IonBuilder::jsop_importmeta()
return true;
}
bool
IonBuilder::jsop_dynamic_import()
{
Value referencingPrivate = FindScriptOrModulePrivateForScript(script());
MConstant* ref = constant(referencingPrivate);
MDefinition* specifier = current->pop();
MDynamicImport* ins = MDynamicImport::New(alloc(), ref, specifier);
current->add(ins);
current->push(ins);
return resumeAfter(ins);
}
MInstruction*
IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
{

View file

@ -789,6 +789,7 @@ class IonBuilder
MOZ_MUST_USE bool jsop_checkobjcoercible();
MOZ_MUST_USE bool jsop_pushcallobj();
MOZ_MUST_USE bool jsop_importmeta();
MOZ_MUST_USE bool jsop_dynamic_import();
/* Inlining. */

View file

@ -2427,6 +2427,15 @@ LIRGenerator::visitNullarySharedStub(MNullarySharedStub* ins)
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitDynamicImport(MDynamicImport* ins)
{
LDynamicImport* lir = new(alloc()) LDynamicImport(useBoxAtStart(ins->referencingPrivate()),
useBoxAtStart(ins->specifier()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitLambda(MLambda* ins)
{

View file

@ -334,6 +334,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitCheckIsCallable(MCheckIsCallable* ins);
void visitCheckObjCoercible(MCheckObjCoercible* ins);
void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
void visitDynamicImport(MDynamicImport* ins);
};
} // namespace jit

View file

@ -8379,6 +8379,22 @@ class MSubstr
}
};
class MDynamicImport : public MBinaryInstruction,
public BoxInputsPolicy::Data
{
explicit MDynamicImport(MDefinition* referencingPrivate, MDefinition* specifier)
: MBinaryInstruction(referencingPrivate, specifier)
{
setResultType(MIRType::Object);
}
public:
INSTRUCTION_HEADER(DynamicImport)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, referencingPrivate))
NAMED_OPERANDS((1, specifier))
};
struct LambdaFunctionInfo
{
// The functions used in lambdas are the canonical original function in

View file

@ -284,6 +284,7 @@ namespace jit {
_(GlobalNameConflictsCheck) \
_(Debugger) \
_(NewTarget) \
_(DynamicImport) \
_(ArrowNewTarget) \
_(CheckReturn) \
_(CheckIsObj) \

View file

@ -4939,6 +4939,26 @@ class LNullarySharedStub : public LCallInstructionHelper<BOX_PIECES, 0, 0>
}
};
class LDynamicImport : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
{
public:
LIR_HEADER(DynamicImport)
static const size_t ReferencingPrivateIndex = 0;
static const size_t SpecifierIndex = BOX_PIECES;
explicit LDynamicImport(const LBoxAllocation& referencingPrivate,
const LBoxAllocation& specifier)
{
setBoxOperand(ReferencingPrivateIndex, referencingPrivate);
setBoxOperand(SpecifierIndex, specifier);
}
const MDynamicImport* mir() const {
return mir_->toDynamicImport();
}
};
class LLambdaForSingleton : public LCallInstructionHelper<1, 1, 0>
{
public:

View file

@ -401,6 +401,7 @@
_(GlobalNameConflictsCheck) \
_(Debugger) \
_(NewTarget) \
_(DynamicImport) \
_(ArrowNewTarget) \
_(CheckReturn) \
_(CheckIsObj) \

View file

@ -592,6 +592,7 @@ MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found f
MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
MSG_DEF(JSMSG_NO_DYNAMIC_IMPORT, 0, JSEXN_SYNTAXERR, "dynamic module import is not implemented")
MSG_DEF(JSMSG_IMPORT_SCRIPT_NOT_FOUND, 0, JSEXN_TYPEERR, "can't find referencing script for dynamic module import")
// Promise
MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.")

View file

@ -4712,6 +4712,29 @@ JS::SetModuleMetadataHook(JSContext* cx, JS::ModuleMetadataHook func)
cx->runtime()->moduleMetadataHook = func;
}
JS_PUBLIC_API(JS::ModuleDynamicImportHook)
JS::GetModuleDynamicImportHook(JSContext* cx)
{
AssertHeapIsIdle(cx);
return cx->runtime()->moduleDynamicImportHook;
}
JS_PUBLIC_API(void)
JS::SetModuleDynamicImportHook(JSContext* cx, JS::ModuleDynamicImportHook func)
{
AssertHeapIsIdle(cx);
cx->runtime()->moduleDynamicImportHook = func;
}
JS_PUBLIC_API(bool)
JS::FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
HandleObject promise)
{
AssertHeapIsIdle(cx);
return js::FinishDynamicModuleImport(cx, referencingPrivate, specifier, promise);
}
JS_PUBLIC_API(bool)
JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, JS::MutableHandleObject module)

View file

@ -4353,6 +4353,25 @@ GetModuleMetadataHook(JSContext* cx);
extern JS_PUBLIC_API(void)
SetModuleMetadataHook(JSContext* cx, ModuleMetadataHook func);
using ModuleDynamicImportHook = bool (*)(JSContext* cx, HandleValue referencingPrivate,
HandleString specifier, HandleObject promise);
/**
* Get the HostResolveImportedModule hook for the runtime.
*/
extern JS_PUBLIC_API(ModuleDynamicImportHook)
GetModuleDynamicImportHook(JSContext* cx);
/**
* Set the HostResolveImportedModule hook for the runtime to the given function.
*/
extern JS_PUBLIC_API(void)
SetModuleDynamicImportHook(JSContext* cx, ModuleDynamicImportHook func);
extern JS_PUBLIC_API(bool)
FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
HandleObject promise);
/**
* Parse the given source buffer as a module in the scope of the current global
* of cx and return a source text module record.

View file

@ -33,11 +33,28 @@ Reflect.Loader = new class {
return module;
}
["import"](name, referrer) {
["import"](name, referencingInfo) {
let module = this.loadAndParse(name);
module.declarationInstantiation();
return module.evaluation();
}
};
setModuleResolveHook((module, requestName) => Reflect.Loader.loadAndParse(requestName));
setModuleResolveHook((referencingInfo, requestName) => {
let path = ReflectLoader.resolve(requestName, referencingInfo);
return ReflectLoader.loadAndParse(path);
});
setModuleMetadataHook((module, metaObject) => {
ReflectLoader.populateImportMeta(module, metaObject);
});
setModuleDynamicImportHook((referencingInfo, specifier, promise) => {
try {
let path = ReflectLoader.resolve(specifier, referencingInfo);
ReflectLoader.loadAndExecute(path);
finishDynamicModuleImport(referencingInfo, specifier, promise);
} catch (err) {
abortDynamicModuleImport(referencingInfo, specifier, promise, err);
}
});

View file

@ -603,6 +603,32 @@ EnvironmentPreparer::invoke(HandleObject scope, Closure& closure)
return;
}
static bool
RegisterScriptPathWithModuleLoader(JSContext* cx, HandleScript script, const char* filename)
{
// Set the private value associated with a script to a object containing the
// script's filename so that the module loader can use it to resolve
// relative imports.
RootedString path(cx, JS_NewStringCopyZ(cx, filename));
if (!path) {
return false;
}
RootedObject infoObject(cx, JS_NewPlainObject(cx));
if (!infoObject) {
return false;
}
RootedValue pathValue(cx, StringValue(path));
if (!JS_DefineProperty(cx, infoObject, "path", pathValue, 0)) {
return false;
}
JS::SetScriptPrivate(script, ObjectValue(*infoObject));
return true;
}
static MOZ_MUST_USE bool
RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
{
@ -635,6 +661,10 @@ RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
MOZ_ASSERT(script);
}
if (!RegisterScriptPathWithModuleLoader(cx, script, filename)) {
return false;
}
#ifdef DEBUG
if (dumpEntrainedVariables)
AnalyzeEntrainedVariables(cx, script);
@ -4035,7 +4065,7 @@ SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp)
}
static JSObject*
CallModuleResolveHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier)
ShellModuleResolveHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier)
{
ShellContext* sc = GetShellContext(cx);
@ -4119,6 +4149,109 @@ ShellModuleMetadataHook(JSContext* cx, HandleObject module, HandleObject metaObj
return true;
}
static bool
SetModuleDynamicImportHook(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"setModuleDynamicImportHook", "0", "s");
return false;
}
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
const char* typeName = InformalValueTypeName(args[0]);
JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
return false;
}
Handle<GlobalObject*> global = cx->global();
global->setReservedSlot(GlobalAppSlotModuleDynamicImportHook, args[0]);
args.rval().setUndefined();
return true;
}
static bool
FinishDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 3) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"finishDynamicModuleImport", "0", "s");
return false;
}
if (!args[1].isString()) {
return ReportArgumentTypeError(cx, args[1], "String");
}
if (!args[2].isObject() || !args[2].toObject().is<PromiseObject>()) {
return ReportArgumentTypeError(cx, args[2], "PromiseObject");
}
RootedString specifier(cx, args[1].toString());
Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
}
static bool
AbortDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 4) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"abortDynamicModuleImport", "0", "s");
return false;
}
if (!args[1].isString()) {
return ReportArgumentTypeError(cx, args[1], "String");
}
if (!args[2].isObject() || !args[2].toObject().is<PromiseObject>()) {
return ReportArgumentTypeError(cx, args[2], "PromiseObject");
}
if (!args[3].isObject() || !args[3].toObject().is<ErrorObject>()) {
return ReportArgumentTypeError(cx, args[3], "ErrorObject");
}
RootedString specifier(cx, args[1].toString());
Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
Rooted<ErrorObject*> error(cx, &args[3].toObject().as<ErrorObject>());
Rooted<Value> value(cx, ObjectValue(*error));
cx->setPendingException(value);
return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
}
static bool
ShellModuleDynamicImportHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier,
HandleObject promise)
{
Handle<GlobalObject*> global = cx->global();
RootedValue hookValue(cx, global->getReservedSlot(GlobalAppSlotModuleDynamicImportHook));
if (hookValue.isUndefined()) {
JS_ReportErrorASCII(cx, "Module resolve hook not set");
return false;
}
MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
JS::AutoValueArray<3> args(cx);
args[0].set(referencingPrivate);
args[1].setString(specifier);
args[2].setObject(*promise);
RootedValue result(cx);
if (!JS_CallFunctionValue(cx, nullptr, hookValue, args, &result)) {
return false;
}
return true;
}
static bool
GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp)
{
@ -5984,7 +6117,7 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
" Parses source text as a module and returns a Module object."),
JS_FN_HELP("setModuleResolveHook", SetModuleResolveHook, 1, 0,
"setModuleResolveHook(function(module, specifier) {})",
"setModuleResolveHook(function(referrer, specifier))",
" Set the HostResolveImportedModule hook to |function|.\n"
" This hook is used to look up a previously loaded module object. It should\n"
" be implemented by the module loader."),
@ -5997,6 +6130,28 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
"getModulePrivate(scriptObject)",
" Get the private value associated with a module object.\n"),
JS_FN_HELP("setModuleMetadataHook", SetModuleMetadataHook, 1, 0,
"setModuleMetadataHook(function(module) {})",
" Set the HostPopulateImportMeta hook to |function|.\n"
" This hook is used to create the metadata object returned by import.meta for\n"
" a module. It should be implemented by the module loader."),
JS_FN_HELP("setModuleDynamicImportHook", SetModuleDynamicImportHook, 1, 0,
"setModuleDynamicImportHook(function(referrer, specifier, promise))",
" Set the HostImportModuleDynamically hook to |function|.\n"
" This hook is used to dynamically import a module. It should\n"
" be implemented by the module loader."),
JS_FN_HELP("finishDynamicModuleImport", FinishDynamicModuleImport, 3, 0,
"finishDynamicModuleImport(referrer, specifier, promise)",
" The module loader's dynamic import hook should call this when the module has"
" been loaded successfully."),
JS_FN_HELP("abortDynamicModuleImport", AbortDynamicModuleImport, 4, 0,
"abortDynamicModuleImport(referrer, specifier, promise, error)",
" The module loader's dynamic import hook should call this when the module "
" import has failed."),
JS_FN_HELP("getModuleLoadPath", GetModuleLoadPath, 0, 0,
"getModuleLoadPath()",
" Return any --module-load-path argument passed to the shell. Used by the\n"
@ -8054,7 +8209,8 @@ main(int argc, char** argv, char** envp)
js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
JS::SetModuleResolveHook(cx->runtime(), CallModuleResolveHook);
JS::SetModuleResolveHook(cx->runtime(), ShellModuleResolveHook);
JS::SetModuleDynamicImportHook(cx, ShellModuleDynamicImportHook);
JS::SetModuleMetadataHook(cx, ShellModuleMetadataHook);
result = Shell(cx, &op, envp);

View file

@ -155,6 +155,7 @@
macro(GeneratorFunction, GeneratorFunction, "GeneratorFunction") \
macro(get, get, "get") \
macro(getInternals, getInternals, "getInternals") \
macro(GetModuleNamespace, GetModuleNamespace, "GetModuleNamespace") \
macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
macro(getPrefix, getPrefix, "get ") \

View file

@ -3083,6 +3083,23 @@ js::GetModuleObjectForScript(JSScript* script)
return nullptr;
}
Value
js::FindScriptOrModulePrivateForScript(JSScript* script)
{
while (script) {
ScriptSourceObject* sso = &script->scriptSourceUnwrap();
Value value = sso->getPrivate();
if (!value.isUndefined()) {
return value;
}
MOZ_ASSERT(sso->introductionScript() != script);
script = sso->introductionScript();
}
return UndefinedValue();
}
bool
js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
MutableHandleValue res)

View file

@ -1075,7 +1075,11 @@ CreateObjectsForEnvironmentChain(JSContext* cx, AutoObjectVector& chain,
HandleObject terminatingEnv,
MutableHandleObject envObj);
ModuleObject* GetModuleObjectForScript(JSScript* script);
ModuleObject*
GetModuleObjectForScript(JSScript* script);
Value
FindScriptOrModulePrivateForScript(JSScript* script);
ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);

View file

@ -4201,6 +4201,22 @@ CASE(JSOP_IMPORTMETA)
}
END_CASE(JSOP_IMPORTMETA)
CASE(JSOP_DYNAMIC_IMPORT)
{
ReservedRooted<Value> referencingPrivate(&rootValue0);
referencingPrivate = FindScriptOrModulePrivateForScript(script);
ReservedRooted<Value> specifier(&rootValue1);
POP_COPY_TO(specifier);
JSObject* promise = StartDynamicModuleImport(cx, referencingPrivate, specifier);
if (!promise)
goto error;
PUSH_OBJECT(*promise);
}
END_CASE(JSOP_DYNAMIC_IMPORT)
CASE(JSOP_SUPERFUN)
{
ReservedRooted<JSObject*> superEnvFunc(&rootObject0, &GetSuperEnvFunction(cx, REGS));

View file

@ -2345,13 +2345,23 @@
* Operands:
* Stack: => import.meta
*/ \
macro(JSOP_IMPORTMETA, 233, "importmeta", NULL, 1, 0, 1, JOF_BYTE)
macro(JSOP_IMPORTMETA, 233, "importmeta", NULL, 1, 0, 1, JOF_BYTE) \
/*
* Dynamic import of the module specified by the string value on the top of
* the stack.
*
* Category: Variables and Scopes
* Type: Modules
* Operands:
* Stack: arg => rval
*/ \
macro(JSOP_DYNAMIC_IMPORT, 234, "call-import", NULL, 1, 1, 1, JOF_BYTE)
/*
* In certain circumstances it may be useful to "pad out" the opcode space to
* a power of two. Use this macro to do so.
*/
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
macro(234) \
macro(235) \
macro(236) \
macro(237) \

View file

@ -244,7 +244,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
js::StackFormat::Default :
js::StackFormat::SpiderMonkey),
moduleResolveHook(),
moduleMetadataHook()
moduleMetadataHook(),
moduleDynamicImportHook()
{
setGCStoreBufferPtr(&gc.storeBuffer);

View file

@ -1300,6 +1300,10 @@ struct JSRuntime : public JS::shadow::Runtime,
// A hook that implements the abstract operations
// HostGetImportMetaProperties and HostFinalizeImportMeta.
JS::ModuleMetadataHook moduleMetadataHook;
// A hook that implements the abstract operation
// HostImportModuleDynamically.
JS::ModuleDynamicImportHook moduleDynamicImportHook;
};
namespace js {

View file

@ -1999,15 +1999,9 @@ intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, Value* vp)
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
RootedString specifier(cx, args[1].toString());
JS::ModuleResolveHook moduleResolveHook = cx->runtime()->moduleResolveHook;
if (!moduleResolveHook) {
JS_ReportErrorASCII(cx, "Module resolve hook not set");
return false;
}
RootedObject result(cx);
RootedValue referencingPrivate(cx, JS::GetModulePrivate(module));
result = moduleResolveHook(cx, referencingPrivate, specifier);
RootedObject result(cx, CallModuleResolveHook(cx, referencingPrivate, specifier));
if (!result)
return false;

View file

@ -151,8 +151,8 @@ AssertScopeMatchesEnvironment(Scope* scope, JSObject* originalEnv)
break;
case ScopeKind::Module:
MOZ_ASSERT(env->as<ModuleEnvironmentObject>().module().script() ==
si.scope()->as<ModuleScope>().script());
MOZ_ASSERT(&env->as<ModuleEnvironmentObject>().module() ==
si.scope()->as<ModuleScope>().module());
env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
break;
}