diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index 97305d5212..1ff2f8d52b 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -812,7 +812,7 @@ HostResolveImportedModule(JSContext* aCx, if (!string.init(aCx, aSpecifier)) { return nullptr; } - if (!aModule || !aCx) { + if (!script || !aCx) { // Our module context was ripped out from under us... return nullptr; } @@ -839,6 +839,34 @@ HostResolveImportedModule(JSContext* aCx, return ms->ModuleRecord(); } +bool +HostPopulateImportMeta(JSContext* aCx, JS::Handle aModule, + JS::Handle aMetaObject) +{ + MOZ_DIAGNOSTIC_ASSERT(aModule); + + JS::Value value = JS::GetModulePrivate(aModule); + if (value.isUndefined()) { + JS_ReportErrorASCII(aCx, "Module script not found"); + return false; + } + + auto script = static_cast(value.toPrivate()); + MOZ_DIAGNOSTIC_ASSERT(script->ModuleRecord() == aModule); + + nsAutoCString url; + MOZ_DIAGNOSTIC_ASSERT(script->BaseURL()); + MOZ_ALWAYS_SUCCEEDS(script->BaseURL()->GetAsciiSpec(url)); + + JS::Rooted urlString(aCx, JS_NewStringCopyZ(aCx, url.get())); + if (!urlString) { + JS_ReportOutOfMemory(aCx); + return false; + } + + return JS_DefineProperty(aCx, aMetaObject, "url", urlString, JSPROP_ENUMERATE); +} + static void EnsureModuleResolveHook(JSContext* aCx) { @@ -848,6 +876,7 @@ EnsureModuleResolveHook(JSContext* aCx) } JS::SetModuleResolveHook(rt, HostResolveImportedModule); + JS::SetModuleMetadataHook(aCx, HostPopulateImportMeta); } void diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 3e048daa5c..760fb9c04d 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -876,6 +876,25 @@ ModuleObject::evaluationError() const return getReservedSlot(EvaluationErrorSlot); } +JSObject* +ModuleObject::metaObject() const +{ + Value value = getReservedSlot(MetaObjectSlot); + if (value.isObject()) + return &value.toObject(); + + MOZ_ASSERT(value.isUndefined()); + return nullptr; +} + +void +ModuleObject::setMetaObject(JSObject* obj) +{ + MOZ_ASSERT(obj); + MOZ_ASSERT(!metaObject()); + setReservedSlot(MetaObjectSlot, ObjectValue(*obj)); +} + Scope* ModuleObject::enclosingScope() const { @@ -1479,3 +1498,24 @@ ArrayObject* ModuleBuilder::createArray(const GCVector& vector) return array; } + +JSObject* +js::GetOrCreateModuleMetaObject(JSContext* cx, HandleObject moduleArg) +{ + HandleModuleObject module = moduleArg.as(); + if (JSObject* obj = module->metaObject()) + return obj; + + RootedObject metaObject(cx, NewObjectWithGivenProto(cx, nullptr)); + if (!metaObject) + return nullptr; + + JS::ModuleMetadataHook func = cx->runtime()->moduleMetadataHook; + MOZ_ASSERT(func); + if (!func(cx, module, metaObject)) + return nullptr; + + module->setMetaObject(metaObject); + + return metaObject; +} diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index 30bc35cbbf..dfd76ee847 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -222,6 +222,7 @@ class ModuleObject : public NativeObject StatusSlot, EvaluationErrorSlot, ScriptSourceObjectSlot, + MetaObjectSlot, RequestedModulesSlot, ImportEntriesSlot, LocalExportEntriesSlot, @@ -274,6 +275,7 @@ class ModuleObject : public NativeObject bool hadEvaluationError() const; Value evaluationError() const; ScriptSourceObject* scriptSourceObject() const; + JSObject* metaObject() const; ArrayObject& requestedModules() const; ArrayObject& importEntries() const; ArrayObject& localExportEntries() const; @@ -286,6 +288,8 @@ class ModuleObject : public NativeObject static bool Instantiate(JSContext* cx, HandleModuleObject self); static bool Evaluate(JSContext* cx, HandleModuleObject self); + void setMetaObject(JSObject* obj); + // For BytecodeEmitter. bool noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun); @@ -367,6 +371,9 @@ class MOZ_STACK_CLASS ModuleBuilder ArrayObject* createArray(const GCVector& vector); }; +JSObject* +GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module); + } // namespace js template<> diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h index 5bd31f58ec..987351815c 100644 --- a/js/src/builtin/SelfHostingDefines.h +++ b/js/src/builtin/SelfHostingDefines.h @@ -101,8 +101,8 @@ #define MODULE_OBJECT_ENVIRONMENT_SLOT 1 #define MODULE_OBJECT_STATUS_SLOT 3 #define MODULE_OBJECT_EVALUATION_ERROR_SLOT 4 -#define MODULE_OBJECT_DFS_INDEX_SLOT 15 -#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 16 +#define MODULE_OBJECT_DFS_INDEX_SLOT 16 +#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 17 #define MODULE_STATUS_UNINSTANTIATED 0 #define MODULE_STATUS_INSTANTIATING 1 diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 40cf2a85e4..360c1b89a7 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -9083,7 +9083,8 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: break; case PNK_IMPORT_META: - MOZ_CRASH("NYI"); + if (!emit1(JSOP_IMPORTMETA)) + return false; break; case PNK_CALL_IMPORT: diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 61de870cdb..d027e09c25 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -4694,3 +4694,17 @@ BaselineCompiler::emit_JSOP_JUMPTARGET() masm.inc64(AbsoluteAddress(counterAddr)); return true; } + +bool +BaselineCompiler::emit_JSOP_IMPORTMETA() +{ + RootedModuleObject module(cx, GetModuleObjectForScript(script)); + MOZ_ASSERT(module); + + JSObject* metaObject = GetOrCreateModuleMetaObject(cx, module); + if (!metaObject) + return false; + + frame.push(ObjectValue(*metaObject)); + return true; +} diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index d3ab58aa4e..d1acec2f01 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -240,7 +240,8 @@ namespace jit { _(JSOP_DEBUGCHECKSELFHOSTED) \ _(JSOP_JUMPTARGET) \ _(JSOP_IS_CONSTRUCTING) \ - _(JSOP_TRY_DESTRUCTURING_ITERCLOSE) + _(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \ + _(JSOP_IMPORTMETA) class BaselineCompiler : public BaselineCompilerSpecific { diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 62470a2b81..a7bb77024d 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2205,6 +2205,9 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_CHECKOBJCOERCIBLE: return jsop_checkobjcoercible(); + case JSOP_IMPORTMETA: + return jsop_importmeta(); + case JSOP_DEBUGCHECKSELFHOSTED: { #ifdef DEBUG @@ -14246,6 +14249,21 @@ IonBuilder::jsop_debugger() return resumeAt(debugger, pc); } +bool +IonBuilder::jsop_importmeta() +{ + ModuleObject* module = GetModuleObjectForScript(script()); + MOZ_ASSERT(module); + + // The object must have been created already when we compiled for baseline. + JSObject* metaObject = module->metaObject(); + MOZ_ASSERT(metaObject); + + pushConstant(ObjectValue(*metaObject)); + + return true; +} + MInstruction* IonBuilder::addConvertElementsToDoubles(MDefinition* elements) { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index a07020a4e8..9d4a530b06 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -788,6 +788,7 @@ class IonBuilder MOZ_MUST_USE bool jsop_checkiscallable(uint8_t kind); MOZ_MUST_USE bool jsop_checkobjcoercible(); MOZ_MUST_USE bool jsop_pushcallobj(); + MOZ_MUST_USE bool jsop_importmeta(); /* Inlining. */ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index f9f5b986ea..1837fea056 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4698,6 +4698,20 @@ JS::SetModuleResolveHook(JSRuntime* rt, JS::ModuleResolveHook func) rt->moduleResolveHook = func; } +JS_PUBLIC_API(JS::ModuleMetadataHook) +JS::GetModuleMetadataHook(JSContext* cx) +{ + AssertHeapIsIdle(cx); + return cx->runtime()->moduleMetadataHook; +} + +JS_PUBLIC_API(void) +JS::SetModuleMetadataHook(JSContext* cx, JS::ModuleMetadataHook func) +{ + AssertHeapIsIdle(cx); + cx->runtime()->moduleMetadataHook = func; +} + JS_PUBLIC_API(bool) JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, JS::MutableHandleObject module) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 362a6c41c2..db6612b700 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4338,6 +4338,21 @@ GetModuleResolveHook(JSRuntime* rt); extern JS_PUBLIC_API(void) SetModuleResolveHook(JSRuntime* rt, ModuleResolveHook func); +using ModuleMetadataHook = bool (*)(JSContext*, HandleObject, HandleObject); + +/** + * Get the hook for populating the import.meta metadata object. + */ +extern JS_PUBLIC_API(ModuleMetadataHook) +GetModuleMetadataHook(JSContext* cx); + +/** + * Set the hook for populating the import.meta metadata object to the given + * function. + */ +extern JS_PUBLIC_API(void) +SetModuleMetadataHook(JSContext* cx, ModuleMetadataHook func); + /** * Parse the given source buffer as a module in the scope of the current global * of cx and return a source text module record. diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index d0d2dda637..9e8d5a0cba 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4102,6 +4102,23 @@ ShellGetModulePrivate(JSContext* cx, unsigned argc, Value* vp) return true; } +ShellModuleMetadataHook(JSContext* cx, HandleObject module, HandleObject metaObject) +{ + // For the shell, just use the script's filename as the base URL. + RootedScript script(cx, module->as().script()); + const char* filename = script->scriptSource()->filename(); + MOZ_ASSERT(filename); + + RootedString url(cx, NewStringCopyZ(cx, filename)); + if (!url) + return false; + + if (!JS_DefineProperty(cx, metaObject, "url", url, JSPROP_ENUMERATE)) + return false; + + return true; +} + static bool GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp) { @@ -8038,6 +8055,7 @@ main(int argc, char** argv, char** envp) js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback); JS::SetModuleResolveHook(cx->runtime(), CallModuleResolveHook); + JS::SetModuleMetadataHook(cx, ShellModuleMetadataHook); result = Shell(cx, &op, envp); diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index 4e74b95ab2..698b36cb29 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -3065,10 +3065,20 @@ WithEnvironmentObject::scope() const ModuleEnvironmentObject* js::GetModuleEnvironmentForScript(JSScript* script) +{ + ModuleObject* module = GetModuleObjectForScript(script); + if (!module) + return nullptr; + + return module->environment(); +} + +ModuleObject* +js::GetModuleObjectForScript(JSScript* script) { for (ScopeIter si(script); si; si++) { if (si.kind() == ScopeKind::Module) - return si.scope()->as().module()->environment(); + return si.scope()->as().module(); } return nullptr; } diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h index 752eb5c65c..b16d079ce6 100644 --- a/js/src/vm/EnvironmentObject.h +++ b/js/src/vm/EnvironmentObject.h @@ -1075,6 +1075,8 @@ CreateObjectsForEnvironmentChain(JSContext* cx, AutoObjectVector& chain, HandleObject terminatingEnv, MutableHandleObject envObj); +ModuleObject* GetModuleObjectForScript(JSScript* script); + ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script); MOZ_MUST_USE bool diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 3951af675e..6ed953bedb 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -33,6 +33,7 @@ #include "jsstr.h" #include "builtin/Eval.h" +#include "builtin/ModuleObject.h" #include "jit/AtomicOperations.h" #include "jit/BaselineJIT.h" #include "jit/Ion.h" @@ -4187,6 +4188,19 @@ CASE(JSOP_NEWTARGET) MOZ_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined()); END_CASE(JSOP_NEWTARGET) +CASE(JSOP_IMPORTMETA) +{ + ReservedRooted module(&rootObject0, GetModuleObjectForScript(script)); + MOZ_ASSERT(module); + + JSObject* metaObject = GetOrCreateModuleMetaObject(cx, module); + if (!metaObject) + goto error; + + PUSH_OBJECT(*metaObject); +} +END_CASE(JSOP_IMPORTMETA) + CASE(JSOP_SUPERFUN) { ReservedRooted superEnvFunc(&rootObject0, &GetSuperEnvFunction(cx, REGS)); diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index ba1c820218..9c9240702b 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -62,6 +62,7 @@ * Super * Arguments * Var Scope + * Modules * [Operators] * Comparison Operators * Arithmetic Operators @@ -2335,13 +2336,21 @@ * Operands: int32_t offset * Stack: cond => cond */ \ - macro(JSOP_COALESCE, 232, "coalesce", NULL, 5, 1, 1, JOF_JUMP|JOF_DETECTING) + macro(JSOP_COALESCE, 232, "coalesce", NULL, 5, 1, 1, JOF_JUMP|JOF_DETECTING) \ + /* + * Push "import.meta" + * + * Category: Variables and Scopes + * Type: Modules + * Operands: + * Stack: => import.meta + */ \ + macro(JSOP_IMPORTMETA, 233, "importmeta", NULL, 1, 0, 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(233) \ macro(234) \ macro(235) \ macro(236) \ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 2430c72846..c7e93d17be 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -243,7 +243,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) stackFormat_(parentRuntime ? js::StackFormat::Default : js::StackFormat::SpiderMonkey), - moduleResolveHook() + moduleResolveHook(), + moduleMetadataHook() { setGCStoreBufferPtr(&gc.storeBuffer); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 8ad1b00623..c76383898a 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1296,6 +1296,10 @@ struct JSRuntime : public JS::shadow::Runtime, // The implementation-defined abstract operation HostResolveImportedModule. JS::ModuleResolveHook moduleResolveHook; + + // A hook that implements the abstract operations + // HostGetImportMetaProperties and HostFinalizeImportMeta. + JS::ModuleMetadataHook moduleMetadataHook; }; namespace js {