Bug 1209107 - Only expose module environment object through testing functions r=shu
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 08 Oct 2015 10:49:49 +0100
changeset 266852 1ac68e528d122516c02444c0bec1e03e06645211
parent 266851 1b6a1a82017691789b2e5a30f1c9d74220fe3596
child 266853 846ed9a45d55a5b27187ebd62724fc083525164f
push id29499
push userkwierso@gmail.com
push dateThu, 08 Oct 2015 21:29:10 +0000
treeherdermozilla-central@46da59584acb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1209107
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1209107 - Only expose module environment object through testing functions r=shu
js/src/builtin/Module.js
js/src/builtin/ModuleObject.cpp
js/src/builtin/TestingFunctions.cpp
js/src/jit-test/tests/modules/module-declaration-instantiation.js
js/src/jit-test/tests/modules/module-environment.js
js/src/jit-test/tests/modules/module-evaluation.js
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/Module.js
+++ b/js/src/builtin/Module.js
@@ -92,22 +92,22 @@ function ModuleDeclarationInstantiation(
 {
     if (!IsObject(this) || !IsModule(this))
         return callFunction(CallModuleMethodIfWrapped, this, "ModuleDeclarationInstantiation");
 
     // Step 1
     let module = this;
 
     // Step 5
-    if (module.environment !== undefined)
+    if (GetModuleEnvironment(module) !== undefined)
         return;
 
     // Step 7
     CreateModuleEnvironment(module);
-    let env = module.environment;
+    let env = GetModuleEnvironment(module);
 
     // Step 8
     let requestedModules = module.requestedModules;
     for (let i = 0; i < requestedModules.length; i++) {
         let required = requestedModules[i];
         let requiredModule = HostResolveImportedModule(module, required);
         requiredModule.declarationInstantiation();
     }
@@ -126,17 +126,17 @@ function ModuleDeclarationInstantiation(
     // Step 12
     let importEntries = module.importEntries;
     for (let i = 0; i < importEntries.length; i++) {
         let imp = importEntries[i];
         let importedModule = HostResolveImportedModule(module, imp.moduleRequest);
         if (imp.importName === "*") {
             // TODO
             // let namespace = GetModuleNamespace(importedModule);
-            // CreateNamespaceBinding(module.environment, imp.localName, namespace);
+            // CreateNamespaceBinding(env, imp.localName, namespace);
         } else {
             let resolution = importedModule.resolveExport(imp.importName);
             if (resolution === null)
                 ThrowSyntaxError(JSMSG_MISSING_IMPORT);
             if (resolution === "ambiguous")
                 ThrowSyntaxError(JSMSG_AMBIGUOUS_IMPORT);
             CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
         }
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -405,31 +405,27 @@ ModuleObject::setEvaluated()
 
 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)
 DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries, IndirectExportEntriesSlot)
 DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries, StarExportEntriesSlot)
 
 JSObject*
 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),
         JS_PSG("indirectExportEntries", ModuleObject_indirectExportEntriesGetter, 0),
         JS_PSG("starExportEntries", ModuleObject_starExportEntriesGetter, 0),
         JS_PS_END
     };
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2881,16 +2881,90 @@ SetRNGState(JSContext* cx, unsigned argc
     if (!ToNumber(cx, args[0], &seed))
         return false;
 
     cx->compartment()->rngState = static_cast<uint64_t>(seed) & RNG_MASK;
     return true;
 }
 #endif
 
+static ModuleEnvironmentObject*
+GetModuleEnvironment(JSContext* cx, HandleValue moduleValue)
+{
+    RootedModuleObject module(cx, &moduleValue.toObject().as<ModuleObject>());
+
+    // Use the initial environment so that tests can check bindings exists
+    // before they have been instantiated.
+    RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
+    MOZ_ASSERT(env);
+    MOZ_ASSERT_IF(module->environment(), module->environment() == env);
+
+    return env;
+}
+
+static bool
+GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != 1) {
+        JS_ReportError(cx, "Wrong number of arguments");
+        return false;
+    }
+
+    if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
+        JS_ReportError(cx, "First argument should be a ModuleObject");
+        return false;
+    }
+
+    RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, args[0]));
+    Rooted<IdVector> ids(cx, IdVector(cx));
+    if (!JS_Enumerate(cx, env, &ids))
+        return false;
+
+    uint32_t length = ids.length();
+    RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, length));
+    if (!array)
+        return false;
+
+    array->setDenseInitializedLength(length);
+    for (uint32_t i = 0; i < length; i++)
+        array->initDenseElement(i, StringValue(JSID_TO_STRING(ids[i])));
+
+    args.rval().setObject(*array);
+    return true;
+}
+
+static bool
+GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != 2) {
+        JS_ReportError(cx, "Wrong number of arguments");
+        return false;
+    }
+
+    if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
+        JS_ReportError(cx, "First argument should be a ModuleObject");
+        return false;
+    }
+
+    if (!args[1].isString()) {
+        JS_ReportError(cx, "Second argument should be a string");
+        return false;
+    }
+
+    RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, args[0]));
+    RootedString name(cx, args[1].toString());
+    RootedId id(cx);
+    if (!JS_StringToId(cx, name, &id))
+        return false;
+
+    return GetProperty(cx, env, env, id, args.rval());
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment' [, 'shrinking'])",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc.\n"
 "  If 'shrinking' is passed as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
@@ -3354,16 +3428,24 @@ gc::ZealModeHelpText),
 "  Enables the deprecated, non-standard __noSuchMethod__ feature.\n"),
 
 #ifdef DEBUG
     JS_FN_HELP("setRNGState", SetRNGState, 1, 0,
 "setRNGState(seed)",
 "  Set this compartment's RNG state.\n"),
 #endif
 
+    JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames, 1, 0,
+"getModuleEnvironmentNames(module)",
+"  Get the list of a module environment's bound names for a specified module.\n"),
+
+    JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue, 2, 0,
+"getModuleEnvironmentValue(module, name)",
+"  Get the value of a bound name in a module environment.\n"),
+
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
--- a/js/src/jit-test/tests/modules/module-declaration-instantiation.js
+++ b/js/src/jit-test/tests/modules/module-declaration-instantiation.js
@@ -1,35 +1,30 @@
 // Exercise ModuleDeclarationInstantiation() operation.
 
-function parseAndInstantiate(source) {
-    let m = parseModule(source);
-    m.declarationInstantiation();
-    return m.environment;
-}
-
 function testModuleEnvironment(module, expected) {
-    var actual = Object.keys(module.environment);
+    var actual = getModuleEnvironmentNames(module);
     assertEq(actual.length, expected.length);
     for (var i = 0; i < actual.length; i++) {
         assertEq(actual[i], expected[i]);
     }
 }
 
 // Check the environment of an empty module.
-let e = parseAndInstantiate("");
-assertEq(Object.keys(e).length, 0);
+let m = parseModule("");
+m.declarationInstantiation();
+testModuleEnvironment(m, []);
 
 let moduleRepo = new Map();
 setModuleResolveHook(function(module, specifier) {
     if (specifier in moduleRepo)
         return moduleRepo[specifier];
     throw "Module " + specifier + " not found";
 });
 
-a = moduleRepo['a'] = parseModule("var x = 1; export { x };");
-b = moduleRepo['b'] = parseModule("import { x as y } from 'a';");
+let a = moduleRepo['a'] = parseModule("var x = 1; export { x };");
+let b = moduleRepo['b'] = parseModule("import { x as y } from 'a';");
 
 a.declarationInstantiation();
 b.declarationInstantiation();
 
 testModuleEnvironment(a, ['x']);
 testModuleEnvironment(b, ['y']);
--- a/js/src/jit-test/tests/modules/module-environment.js
+++ b/js/src/jit-test/tests/modules/module-environment.js
@@ -1,20 +1,18 @@
 load(libdir + "class.js");
 
 // Test top-level module environment
 
 function testInitialEnvironment(source, expected) {
-    print(source);
-    let m = parseModule(source);
-    let scope = m.initialEnvironment;
-    let keys = Object.keys(scope);
-    assertEq(keys.length, expected.length);
+    let module = parseModule(source);
+    let names = getModuleEnvironmentNames(module);
+    assertEq(names.length, expected.length);
     expected.forEach(function(name) {
-        assertEq(name in scope, true);
+        assertEq(names.includes(name), true);
     });
 }
 
 testInitialEnvironment('', []);
 testInitialEnvironment('var x = 1;', ['x']);
 testInitialEnvironment('let x = 1;', ['x']);
 testInitialEnvironment("if (true) { let x = 1; }", []);
 testInitialEnvironment("if (true) { var x = 1; }", ['x']);
--- a/js/src/jit-test/tests/modules/module-evaluation.js
+++ b/js/src/jit-test/tests/modules/module-evaluation.js
@@ -21,25 +21,25 @@ assertEq(typeof parseAndEvaluate(""), "u
 // 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");
+assertEq(typeof getModuleEnvironmentValue(m, "x"), "undefined");
 m.declarationInstantiation();
 m.evaluation();
-assertEq(m.environment.x, 4);
+assertEq(getModuleEnvironmentValue(m, "x"), 4);
 
 m = parseModule("export let x = 2 * 3;");
 m.declarationInstantiation();
 m.evaluation();
-assertEq(m.environment.x, 6);
+assertEq(getModuleEnvironmentValue(m, "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; }`);
 
@@ -94,9 +94,10 @@ assertDeepEq(parseAndEvaluate(`import { 
                                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);
+let f = getModuleEnvironmentValue(m, "f");
+assertEq(f(), 1);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1289,16 +1289,33 @@ intrinsic_HostResolveImportedModule(JSCo
         return false;
     }
 
     args.rval().set(result);
     return true;
 }
 
 static bool
+intrinsic_GetModuleEnvironment(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 1);
+    RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
+    RootedModuleEnvironmentObject env(cx, module->environment());
+    args.rval().setUndefined();
+    if (!env) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    args.rval().setObject(*env);
+    return true;
+}
+
+static bool
 intrinsic_CreateModuleEnvironment(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->createEnvironment();
     args.rval().setUndefined();
     return true;
@@ -1581,17 +1598,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0),
     JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0),
     JS_FN("regexp_construct_no_statics", regexp_construct_no_statics, 2,0),
 
     JS_FN("IsModule", intrinsic_IsModule, 1, 0),
     JS_FN("CallModuleMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<ModuleObject>>, 2, 0),
     JS_FN("HostResolveImportedModule", intrinsic_HostResolveImportedModule, 2, 0),
-    JS_FN("CreateModuleEnvironment", intrinsic_CreateModuleEnvironment, 2, 0),
+    JS_FN("GetModuleEnvironment", intrinsic_GetModuleEnvironment, 1, 0),
+    JS_FN("CreateModuleEnvironment", intrinsic_CreateModuleEnvironment, 1, 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
 };
 
 void