Issue #1691 - Part 3: Finish implementing import meta. https://bugzilla.mozilla.org/show_bug.cgi?id=1427610

(cherry picked from commit a8625238ac846fb7eb103646ddb8634a5860f067)
This commit is contained in:
Brian Smith 2023-04-08 15:01:22 -05:00 committed by roytam1
commit 3f4985ce6c
18 changed files with 207 additions and 9 deletions

View file

@ -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<JSObject*> aModule,
JS::Handle<JSObject*> 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<ModuleScript*>(value.toPrivate());
MOZ_DIAGNOSTIC_ASSERT(script->ModuleRecord() == aModule);
nsAutoCString url;
MOZ_DIAGNOSTIC_ASSERT(script->BaseURL());
MOZ_ALWAYS_SUCCEEDS(script->BaseURL()->GetAsciiSpec(url));
JS::Rooted<JSString*> 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

View file

@ -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<T>& vector)
return array;
}
JSObject*
js::GetOrCreateModuleMetaObject(JSContext* cx, HandleObject moduleArg)
{
HandleModuleObject module = moduleArg.as<ModuleObject>();
if (JSObject* obj = module->metaObject())
return obj;
RootedObject metaObject(cx, NewObjectWithGivenProto<PlainObject>(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;
}

View file

@ -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<T>& vector);
};
JSObject*
GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module);
} // namespace js
template<>

View file

@ -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

View file

@ -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:

View file

@ -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;
}

View file

@ -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
{

View file

@ -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)
{

View file

@ -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. */

View file

@ -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)

View file

@ -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.

View file

@ -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<ModuleObject>().script());
const char* filename = script->scriptSource()->filename();
MOZ_ASSERT(filename);
RootedString url(cx, NewStringCopyZ<CanGC>(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);

View file

@ -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<ModuleScope>().module()->environment();
return si.scope()->as<ModuleScope>().module();
}
return nullptr;
}

View file

@ -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

View file

@ -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<JSObject*> 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<JSObject*> superEnvFunc(&rootObject0, &GetSuperEnvFunction(cx, REGS));

View file

@ -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) \

View file

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

View file

@ -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 {