Bug 930414 - Make |this| undefined in modules r=shu
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 21 Oct 2015 10:21:44 +0100
changeset 303933 75365568c45bae97207fb35d49d1f182eae7bde6
parent 303932 de036df63e4d68749475b1e67962745d9121e399
child 303934 c7fc7f42fddbed3298db3875c6b4e37c8fe04d67
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs930414
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 930414 - Make |this| undefined in modules r=shu
js/src/jit-test/tests/modules/module-this.js
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineFrame.h
js/src/jit/IonBuilder.cpp
js/src/jit/JitFrames.h
js/src/jit/RematerializedFrame.h
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/module-this.js
@@ -0,0 +1,15 @@
+// Test 'this' is undefined in modules.
+
+function parseAndEvaluate(source) {
+    let m = parseModule(source);
+    m.declarationInstantiation();
+    return m.evaluation();
+}
+
+assertEq(typeof(parseAndEvaluate("this")), "undefined");
+
+let m = parseModule("export function getThis() { return this; }");
+m.declarationInstantiation();
+m.evaluation();
+let f = getModuleEnvironmentValue(m, "getThis");
+assertEq(typeof(f()), "undefined");
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1307,16 +1307,22 @@ BaselineCompiler::emitCheckThis()
 
     masm.bind(&thisOK);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_THIS()
 {
+    // |this| is undefined in modules.
+    if (module()) {
+        frame.push(UndefinedValue());
+        return true;
+    }
+
     if (function() && function()->isArrow()) {
         // Arrow functions store their (lexical) |this| value in an
         // extended slot.
         frame.syncStack(0);
         Register scratch = R0.scratchReg();
         masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), scratch);
         masm.loadValue(Address(scratch, FunctionExtended::offsetOfArrowThisSlot()), R0);
         frame.push(R0);
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -391,16 +391,19 @@ class BaselineFrame
         flags_ &= ~HAS_OVERRIDE_PC;
     }
 
     void trace(JSTracer* trc, JitFrameIterator& frame);
 
     bool isFunctionFrame() const {
         return CalleeTokenIsFunction(calleeToken());
     }
+    bool isModuleFrame() const {
+        return CalleeTokenIsModuleScript(calleeToken());
+    }
     bool isGlobalFrame() const {
         return !CalleeTokenIsFunction(calleeToken());
     }
      bool isEvalFrame() const {
         return flags_ & EVAL;
     }
     bool isStrictEvalFrame() const {
         return isEvalFrame() && script()->strict();
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12784,16 +12784,21 @@ IonBuilder::jsop_checkaliasedlet(ScopeCo
         setLexicalCheck(let);
 
     return true;
 }
 
 bool
 IonBuilder::jsop_this()
 {
+    if (info().module()) {
+        pushConstant(UndefinedValue());
+        return true;
+    }
+
     if (!info().funMaybeLazy())
         return abort("JSOP_THIS outside of a JSFunction.");
 
     if (info().funMaybeLazy()->isArrow()) {
         // Arrow functions store their lexical |this| in an extended slot.
         MLoadArrowThis* thisObj = MLoadArrowThis::New(alloc(), getCallee());
         current->add(thisObj);
         current->push(thisObj);
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -63,16 +63,22 @@ CalleeTokenToFunction(CalleeToken token)
     return (JSFunction*)(uintptr_t(token) & CalleeTokenMask);
 }
 static inline JSScript*
 CalleeTokenToScript(CalleeToken token)
 {
     MOZ_ASSERT(GetCalleeTokenTag(token) == CalleeToken_Script);
     return (JSScript*)(uintptr_t(token) & CalleeTokenMask);
 }
+static inline bool
+CalleeTokenIsModuleScript(CalleeToken token)
+{
+    CalleeTokenTag tag = GetCalleeTokenTag(token);
+    return tag == CalleeToken_Script && CalleeTokenToScript(token)->module();
+}
 
 static inline JSScript*
 ScriptFromCalleeToken(CalleeToken token)
 {
     switch (GetCalleeTokenTag(token)) {
       case CalleeToken_Script:
         return CalleeTokenToScript(token);
       case CalleeToken_Function:
--- a/js/src/jit/RematerializedFrame.h
+++ b/js/src/jit/RematerializedFrame.h
@@ -136,16 +136,19 @@ class RematerializedFrame
         MOZ_ASSERT(hasArgsObj());
         MOZ_ASSERT(script()->needsArgsObj());
         return *argsObj_;
     }
 
     bool isFunctionFrame() const {
         return !!script_->functionNonDelazifying();
     }
+    bool isModuleFrame() const {
+        return !!script_->module();
+    }
     bool isGlobalFrame() const {
         return !isFunctionFrame();
     }
     bool isNonEvalFunctionFrame() const {
         // Ion doesn't support eval frames.
         return isFunctionFrame();
     }
 
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -35,16 +35,21 @@ ComputeThis(JSContext* cx, AbstractFrame
         /*
          * Arrow functions store their (lexical) |this| value in an
          * extended slot.
          */
         frame.thisValue() = frame.fun()->getExtendedSlot(0);
         return true;
     }
 
+    if (frame.isModuleFrame()) {
+        MOZ_ASSERT(frame.thisValue().isUndefined());
+        return true;
+    }
+
     if (frame.thisValue().isObject())
         return true;
     RootedValue thisv(cx, frame.thisValue());
     if (frame.isFunctionFrame()) {
         if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin())
             return true;
         /*
          * Eval function frames have their own |this| slot, which is a copy of the function's
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1024,23 +1024,31 @@ js::Execute(JSContext* cx, HandleScript 
 #ifdef DEBUG
     JSObject* s = scopeChain;
     do {
         assertSameCompartment(cx, s);
         MOZ_ASSERT_IF(!s->enclosingScope(), s->is<GlobalObject>());
     } while ((s = s->enclosingScope()));
 #endif
 
-    /* Use the scope chain as 'this', modulo outerization. */
-    JSObject* thisObj = GetThisObject(cx, scopeChain);
-    if (!thisObj)
-        return false;
-    Value thisv = ObjectValue(*thisObj);
-
-    ExecuteType type = script->module() ? EXECUTE_MODULE : EXECUTE_GLOBAL;
+    ExecuteType type;
+    Value thisv;
+    if (script->module()) {
+        type = EXECUTE_MODULE;
+        thisv = UndefinedValue();
+    } else {
+        type = EXECUTE_GLOBAL;
+
+        /* Use the scope chain as 'this', modulo outerization. */
+        JSObject* thisObj = GetThisObject(cx, scopeChain);
+        if (!thisObj)
+            return false;
+        thisv = ObjectValue(*thisObj);
+    }
+
     return ExecuteKernel(cx, script, *scopeChain, thisv, NullValue(), type,
                          NullFramePtr() /* evalInFrame */, rval);
 }
 
 bool
 js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
 {
     const Class* clasp = obj->getClass();
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -579,16 +579,26 @@ AbstractFramePtr::isFunctionFrame() cons
     if (isInterpreterFrame())
         return asInterpreterFrame()->isFunctionFrame();
     if (isBaselineFrame())
         return asBaselineFrame()->isFunctionFrame();
     return asRematerializedFrame()->isFunctionFrame();
 }
 
 inline bool
+AbstractFramePtr::isModuleFrame() const
+{
+    if (isInterpreterFrame())
+        return asInterpreterFrame()->isModuleFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->isModuleFrame();
+    return asRematerializedFrame()->isModuleFrame();
+}
+
+inline bool
 AbstractFramePtr::isGlobalFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isGlobalFrame();
     if (isBaselineFrame())
         return asBaselineFrame()->isGlobalFrame();
     return asRematerializedFrame()->isGlobalFrame();
 }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -32,16 +32,18 @@ using mozilla::PodCopy;
 
 /*****************************************************************************/
 
 void
 InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr evalInFramePrev,
                                    const Value& thisv, const Value& newTargetValue, HandleObject scopeChain,
                                    ExecuteType type)
 {
+    MOZ_ASSERT_IF(type & MODULE, thisv.isUndefined());
+
     /*
      * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
      * script in the context of another frame and the frame type is determined
      * by the context.
      */
     flags_ = type | HAS_SCOPECHAIN;
 
     JSObject* callee = nullptr;
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -199,16 +199,17 @@ class AbstractFramePtr
     inline CallObject& callObj() const;
     inline bool initFunctionScopeObjects(JSContext* cx);
     inline void pushOnScopeChain(ScopeObject& scope);
 
     inline JSCompartment* compartment() const;
 
     inline bool hasCallObj() const;
     inline bool isFunctionFrame() const;
+    inline bool isModuleFrame() const;
     inline bool isGlobalFrame() const;
     inline bool isEvalFrame() const;
     inline bool isDebuggerEvalFrame() const;
     inline bool hasCachedSavedFrame() const;
     inline void setHasCachedSavedFrame();
 
     inline JSScript* script() const;
     inline JSFunction* fun() const;
@@ -731,17 +732,17 @@ class InterpreterFrame
     }
 
     JSObject& constructorThis() const {
         MOZ_ASSERT(hasArgs());
         return argv()[-1].toObject();
     }
 
     Value& thisValue() const {
-        if (flags_ & (EVAL | GLOBAL))
+        if (flags_ & (EVAL | GLOBAL | MODULE))
             return ((Value*)this)[-1];
         return argv()[-1];
     }
 
     void setDerivedConstructorThis(HandleObject thisv) {
         MOZ_ASSERT(isNonEvalFunctionFrame());
         MOZ_ASSERT(script()->isDerivedClassConstructor());
         MOZ_ASSERT(callee().isClassConstructor());