mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 10:45:42 +00:00
Bug 930414 - Implement ModuleEvaluation method r=shu
This commit is contained in:
parent
72772b7a50
commit
279474c28d
@ -142,3 +142,30 @@ function ModuleDeclarationInstantiation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 15.2.1.16.5 ModuleEvaluation()
|
||||
function ModuleEvaluation()
|
||||
{
|
||||
if (!IsObject(this) || !IsModule(this))
|
||||
return callFunction(CallModuleMethodIfWrapped, this, "ModuleEvaluation");
|
||||
|
||||
// Step 1
|
||||
let module = this;
|
||||
|
||||
// Step 4
|
||||
if (module.evaluated)
|
||||
return undefined;
|
||||
|
||||
// Step 5
|
||||
SetModuleEvaluated(this);
|
||||
|
||||
// Step 6
|
||||
let requestedModules = module.requestedModules;
|
||||
for (let i = 0; i < requestedModules.length; i++) {
|
||||
let required = requestedModules[i];
|
||||
let requiredModule = HostResolveImportedModule(module, required);
|
||||
requiredModule.evaluation();
|
||||
}
|
||||
|
||||
return EvaluateModule(module);
|
||||
}
|
||||
|
@ -304,6 +304,7 @@ ModuleObject::init(HandleScript script)
|
||||
{
|
||||
MOZ_ASSERT(!script->enclosingStaticScope());
|
||||
initReservedSlot(ScriptSlot, PrivateValue(script));
|
||||
initReservedSlot(EvaluatedSlot, BooleanValue(false));
|
||||
}
|
||||
|
||||
void
|
||||
@ -340,6 +341,12 @@ ModuleObject::script() const
|
||||
return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleObject::evaluated() const
|
||||
{
|
||||
return getReservedSlot(EvaluatedSlot).toBoolean();
|
||||
}
|
||||
|
||||
ModuleEnvironmentObject&
|
||||
ModuleObject::initialEnvironment() const
|
||||
{
|
||||
@ -386,8 +393,23 @@ ModuleObject::createEnvironment()
|
||||
setReservedSlot(EnvironmentSlot, getReservedSlot(InitialEnvironmentSlot));
|
||||
}
|
||||
|
||||
void
|
||||
ModuleObject::setEvaluated()
|
||||
{
|
||||
MOZ_ASSERT(!evaluated());
|
||||
setReservedSlot(EvaluatedSlot, TrueHandleValue);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleObject::evaluate(JSContext* cx, MutableHandleValue rval)
|
||||
{
|
||||
RootedScript script(cx, this->script());
|
||||
return JS_ExecuteScript(cx, script, rval);
|
||||
}
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, initialEnvironment, InitialEnvironmentSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, environment, EnvironmentSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluated, EvaluatedSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot)
|
||||
@ -400,6 +422,7 @@ js::InitModuleClass(JSContext* cx, HandleObject obj)
|
||||
static const JSPropertySpec protoAccessors[] = {
|
||||
JS_PSG("initialEnvironment", ModuleObject_initialEnvironmentGetter, 0),
|
||||
JS_PSG("environment", ModuleObject_environmentGetter, 0),
|
||||
JS_PSG("evaluated", ModuleObject_evaluatedGetter, 0),
|
||||
JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
|
||||
JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
|
||||
JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),
|
||||
@ -411,6 +434,7 @@ js::InitModuleClass(JSContext* cx, HandleObject obj)
|
||||
static const JSFunctionSpec protoFunctions[] = {
|
||||
JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 3, 0),
|
||||
JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleDeclarationInstantiation", 0, 0),
|
||||
JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluation", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
@ -88,6 +88,7 @@ class ModuleObject : public NativeObject
|
||||
ScriptSlot = 0,
|
||||
InitialEnvironmentSlot,
|
||||
EnvironmentSlot,
|
||||
EvaluatedSlot,
|
||||
RequestedModulesSlot,
|
||||
ImportEntriesSlot,
|
||||
LocalExportEntriesSlot,
|
||||
@ -113,6 +114,7 @@ class ModuleObject : public NativeObject
|
||||
JSScript* script() const;
|
||||
ModuleEnvironmentObject& initialEnvironment() const;
|
||||
ModuleEnvironmentObject* environment() const;
|
||||
bool evaluated() const;
|
||||
ArrayObject& requestedModules() const;
|
||||
ArrayObject& importEntries() const;
|
||||
ArrayObject& localExportEntries() const;
|
||||
@ -123,6 +125,9 @@ class ModuleObject : public NativeObject
|
||||
|
||||
void createEnvironment();
|
||||
|
||||
void setEvaluated();
|
||||
bool evaluate(JSContext*cx, MutableHandleValue rval);
|
||||
|
||||
private:
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
static void finalize(js::FreeOp* fop, JSObject* obj);
|
||||
@ -152,6 +157,7 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
|
||||
JSContext* cx_;
|
||||
RootedAtomVector requestedModules_;
|
||||
|
||||
RootedAtomVector importedBoundNames_;
|
||||
RootedImportEntryVector importEntries_;
|
||||
RootedExportEntryVector exportEntries_;
|
||||
|
26
js/src/jit-test/tests/modules/ambiguous-star-export.js
Normal file
26
js/src/jit-test/tests/modules/ambiguous-star-export.js
Normal file
@ -0,0 +1,26 @@
|
||||
// Test ambigious export * statements.
|
||||
|
||||
"use strict";
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
let moduleRepo = new Map();
|
||||
setModuleResolveHook(function(module, specifier) {
|
||||
if (specifier in moduleRepo)
|
||||
return moduleRepo[specifier];
|
||||
throw "Module " + specifier + " not found";
|
||||
});
|
||||
|
||||
let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
|
||||
let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
|
||||
let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
|
||||
let ms = [a, b, c];
|
||||
ms.map((m) => m.declarationInstantiation());
|
||||
ms.map((m) => m.evaluation(), moduleRepo.values());
|
||||
|
||||
let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
|
||||
d.declarationInstantiation();
|
||||
assertEq(d.evaluation(), 1);
|
||||
|
||||
let e = moduleRepo['e'] = parseModule("import { b } from 'c';");
|
||||
assertThrowsInstanceOf(() => e.declarationInstantiation(), SyntaxError);
|
99
js/src/jit-test/tests/modules/module-evaluation.js
Normal file
99
js/src/jit-test/tests/modules/module-evaluation.js
Normal file
@ -0,0 +1,99 @@
|
||||
// Exercise ModuleEvaluation() concrete method.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
let moduleRepo = new Map();
|
||||
setModuleResolveHook(function(module, specifier) {
|
||||
if (specifier in moduleRepo)
|
||||
return moduleRepo[specifier];
|
||||
throw "Module " + specifier + " not found";
|
||||
});
|
||||
|
||||
function parseAndEvaluate(source) {
|
||||
let m = parseModule(source);
|
||||
m.declarationInstantiation();
|
||||
return m.evaluation();
|
||||
}
|
||||
|
||||
// Check the evaluation of an empty module succeeds.
|
||||
assertEq(typeof parseAndEvaluate(""), "undefined");
|
||||
|
||||
// Check evaluation returns evaluation result the first time, then undefined.
|
||||
let m = parseModule("1");
|
||||
m.declarationInstantiation();
|
||||
assertEq(m.evaluation(), 1);
|
||||
assertEq(typeof m.evaluation(), "undefined");
|
||||
|
||||
// Check top level variables are initialized by evaluation.
|
||||
m = parseModule("export var x = 2 + 2;");
|
||||
assertEq(typeof m.initialEnvironment.x, "undefined");
|
||||
m.declarationInstantiation();
|
||||
m.evaluation();
|
||||
assertEq(m.environment.x, 4);
|
||||
|
||||
m = parseModule("export let x = 2 * 3;");
|
||||
m.declarationInstantiation();
|
||||
m.evaluation();
|
||||
assertEq(m.environment.x, 6);
|
||||
|
||||
// Set up a module to import from.
|
||||
let a = moduleRepo['a'] =
|
||||
parseModule(`var x = 1;
|
||||
export { x };
|
||||
export default 2;
|
||||
export function f(x) { return x + 1; }`);
|
||||
|
||||
// Check we can evaluate top level definitions.
|
||||
parseAndEvaluate("var foo = 1;");
|
||||
parseAndEvaluate("let foo = 1;");
|
||||
parseAndEvaluate("const foo = 1");
|
||||
parseAndEvaluate("function foo() {}");
|
||||
parseAndEvaluate("class foo { constructor() {} }");
|
||||
|
||||
// Check we can evaluate all module-related syntax.
|
||||
parseAndEvaluate("export var foo = 1;");
|
||||
parseAndEvaluate("export let foo = 1;");
|
||||
parseAndEvaluate("export const foo = 1;");
|
||||
parseAndEvaluate("var x = 1; export { x };");
|
||||
parseAndEvaluate("export default 1");
|
||||
parseAndEvaluate("export default class { constructor() {} };");
|
||||
parseAndEvaluate("export default function() {};");
|
||||
parseAndEvaluate("export default class foo { constructor() {} };");
|
||||
parseAndEvaluate("export default function foo() {};");
|
||||
parseAndEvaluate("import a from 'a';");
|
||||
parseAndEvaluate("import { x } from 'a';");
|
||||
parseAndEvaluate("import * as ns from 'a';");
|
||||
parseAndEvaluate("export * from 'a'");
|
||||
|
||||
// Test default import
|
||||
m = parseModule("import a from 'a'; a;")
|
||||
m.declarationInstantiation();
|
||||
assertEq(m.evaluation(), 2);
|
||||
|
||||
// Test named import
|
||||
m = parseModule("import { x as y } from 'a'; y;")
|
||||
m.declarationInstantiation();
|
||||
assertEq(m.evaluation(), 1);
|
||||
|
||||
// Call exported function
|
||||
m = parseModule("import { f } from 'a'; f(3);")
|
||||
m.declarationInstantiation();
|
||||
assertEq(m.evaluation(), 4);
|
||||
|
||||
// Test importing an indirect export
|
||||
moduleRepo['b'] = parseModule("export { x as z } from 'a';");
|
||||
assertEq(parseAndEvaluate("import { z } from 'b'; z"), 1);
|
||||
|
||||
// Test cyclic dependencies
|
||||
moduleRepo['c1'] = parseModule("export var x = 1; export {y} from 'c2'");
|
||||
moduleRepo['c2'] = parseModule("export var y = 2; export {x} from 'c1'");
|
||||
assertDeepEq(parseAndEvaluate(`import { x as x1, y as y1 } from 'c1';
|
||||
import { x as x2, y as y2 } from 'c2';
|
||||
[x1, y1, x2, y2]`),
|
||||
[1, 2, 1, 2]);
|
||||
|
||||
// Import access in functions
|
||||
m = parseModule("import { x } from 'a'; function f() { return x; }")
|
||||
m.declarationInstantiation();
|
||||
m.evaluation();
|
||||
assertEq(m.environment.f(), 1);
|
@ -672,6 +672,11 @@ BaselineCompiler::initScopeChain()
|
||||
if (!callVMNonOp(InitFunctionScopeObjectsInfo, phase))
|
||||
return false;
|
||||
}
|
||||
} else if (module()) {
|
||||
// Modules use a pre-created scope object.
|
||||
Register scope = R1.scratchReg();
|
||||
masm.movePtr(ImmGCPtr(&module()->initialEnvironment()), scope);
|
||||
masm.storePtr(scope, frame.addressOfScopeChain());
|
||||
} else {
|
||||
// ScopeChain pointer in BaselineFrame has already been initialized
|
||||
// in prologue.
|
||||
|
@ -48,7 +48,8 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
|
||||
|
||||
// Initialize the scope chain slot if either the function needs a CallObject
|
||||
// or the script uses the scope chain. The latter case is handled below.
|
||||
usesScopeChain_ = (script_->functionDelazifying() &&
|
||||
usesScopeChain_ = script_->module() ||
|
||||
(script_->functionDelazifying() &&
|
||||
script_->functionDelazifying()->needsCallObject());
|
||||
MOZ_ASSERT_IF(script_->hasAnyAliasedBindings(), usesScopeChain_);
|
||||
|
||||
|
@ -247,6 +247,9 @@ class CompileInfo
|
||||
JSFunction* funMaybeLazy() const {
|
||||
return fun_;
|
||||
}
|
||||
ModuleObject* module() const {
|
||||
return script_->module();
|
||||
}
|
||||
bool constructing() const {
|
||||
return constructing_;
|
||||
}
|
||||
|
@ -1229,6 +1229,9 @@ IonBuilder::initScopeChain(MDefinition* callee)
|
||||
if (!scope)
|
||||
return false;
|
||||
}
|
||||
} else if (ModuleObject* module = info().module()) {
|
||||
// Modules use a pre-created scope object.
|
||||
scope = constant(ObjectValue(module->initialEnvironment()));
|
||||
} else {
|
||||
// For global scripts without a non-syntactic global scope, the scope
|
||||
// chain is the global object.
|
||||
|
@ -4691,6 +4691,9 @@ IsCacheableNameReadSlot(HandleObject scopeChain, HandleObject obj,
|
||||
if (!IsCacheableGetPropReadSlotForIon(obj, holder, shape) &&
|
||||
!IsCacheableNoProperty(obj, holder, shape, pc, output))
|
||||
return false;
|
||||
} else if (obj->is<ModuleEnvironmentObject>()) {
|
||||
// We don't yet support lookups in a module environment.
|
||||
return false;
|
||||
} else if (obj->is<CallObject>()) {
|
||||
MOZ_ASSERT(obj == holder);
|
||||
if (!shape->hasDefaultGetter())
|
||||
|
@ -123,6 +123,10 @@ class BaselineCompilerShared
|
||||
return script->functionNonDelazifying();
|
||||
}
|
||||
|
||||
ModuleObject* module() const {
|
||||
return script->module();
|
||||
}
|
||||
|
||||
PCMappingSlotInfo getStackTopSlotInfo() {
|
||||
MOZ_ASSERT(frame.numUnsyncedSlots() <= 2);
|
||||
switch (frame.numUnsyncedSlots()) {
|
||||
|
@ -1013,7 +1013,8 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& scopeChainArg, Value*
|
||||
return false;
|
||||
Value thisv = ObjectValue(*thisObj);
|
||||
|
||||
return ExecuteKernel(cx, script, *scopeChain, thisv, NullValue(), EXECUTE_GLOBAL,
|
||||
ExecuteType type = script->module() ? EXECUTE_MODULE : EXECUTE_GLOBAL;
|
||||
return ExecuteKernel(cx, script, *scopeChain, thisv, NullValue(), type,
|
||||
NullFramePtr() /* evalInFrame */, rval);
|
||||
}
|
||||
|
||||
|
@ -1320,6 +1320,26 @@ intrinsic_CreateImportBinding(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_SetModuleEvaluated(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
|
||||
module->setEvaluated();
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_EvaluateModule(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
|
||||
return module->evaluate(cx, args.rval());
|
||||
}
|
||||
|
||||
// The self-hosting global isn't initialized with the normal set of builtins.
|
||||
// Instead, individual C++-implemented functions that're required by
|
||||
// self-hosted code are defined as global functions. Accessing these
|
||||
@ -1568,6 +1588,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("HostResolveImportedModule", intrinsic_HostResolveImportedModule, 2, 0),
|
||||
JS_FN("CreateModuleEnvironment", intrinsic_CreateModuleEnvironment, 2, 0),
|
||||
JS_FN("CreateImportBinding", intrinsic_CreateImportBinding, 4, 0),
|
||||
JS_FN("SetModuleEvaluated", intrinsic_SetModuleEvaluated, 1, 0),
|
||||
JS_FN("EvaluateModule", intrinsic_EvaluateModule, 1, 0),
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user