Bug 1232446 - Re-enable method calls in SelfHosted code using new anti-content checks. (r=till)
authorEric Faust <efaustbmo@gmail.com>
Wed, 16 Dec 2015 08:14:43 -0800
changeset 315834 e2d7e9400d746483a0ee3dabf665e899f244bc1b
parent 315833 cdc3315bff369dfdd6b71bc638cd3e1ed5e6e388
child 315835 ad01f1203ae4c14f30faf2b2299e4fafa6710143
push id8468
push userhurley@todesschaf.org
push dateWed, 16 Dec 2015 19:25:06 +0000
reviewerstill
bugs1232446
milestone46.0a1
Bug 1232446 - Re-enable method calls in SelfHosted code using new anti-content checks. (r=till)
js/src/builtin/TypedObject.js
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/Parser.cpp
js/src/js.msg
js/src/vm/Xdr.h
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -645,24 +645,24 @@ function DescrToSource() {
   return DESCR_STRING_REPR(this);
 }
 
 // Warning: user exposed!
 function ArrayShorthand(...dims) {
   if (!IsObject(this) || !ObjectIsTypeDescr(this))
     ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
-  var T = GetTypedObjectModule();
+  var AT = GetTypedObjectModule().ArrayType;
 
   if (dims.length == 0)
     ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS);
 
   var accum = this;
   for (var i = dims.length - 1; i >= 0; i--)
-    accum = new T.ArrayType(accum, dims[i]);
+    accum = new AT(accum, dims[i]);
   return accum;
 }
 
 // This is the `storage()` function defined in the spec.  When
 // provided with a *transparent* typed object, it returns an object
 // containing buffer, byteOffset, byteLength. When given an opaque
 // typed object, it returns null. Otherwise it throws.
 //
@@ -1155,18 +1155,18 @@ function FilterTypedSeqImpl(array, func)
     var v = TypedObjectGet(elementType, array, inOffset);
     if (func(v, i, array)) {
       SET_BIT(flags, i);
       count++;
     }
     inOffset += size;
   }
 
-  var T = GetTypedObjectModule();
+  var AT = GetTypedObjectModule().ArrayType;
 
-  var resultType = new T.ArrayType(elementType, count);
+  var resultType = new AT(elementType, count);
   var result = new resultType();
   for (var i = 0, j = 0; i < array.length; i++) {
     if (GET_BIT(flags, i))
       result[j++] = array[i];
   }
   return result;
 }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7049,16 +7049,28 @@ BytecodeEmitter::emitDeleteExpression(Pa
         if (!emit1(JSOP_POP))
             return false;
     }
 
     return emit1(JSOP_TRUE);
 }
 
 bool
+BytecodeEmitter::emitDebugOnlyCheckSelfHosted()
+{
+#ifdef DEBUG
+    if (emitterMode == BytecodeEmitter::SelfHosting) {
+        if (!emit1(JSOP_DEBUGCHECKSELFHOSTED))
+            return false;
+    }
+#endif
+    return true;
+}
+
+bool
 BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
 {
     // Special-casing of callFunction to emit bytecode that directly
     // invokes the callee with the correct |this| object and arguments.
     // callFunction(fun, thisArg, arg0, arg1) thus becomes:
     // - emit lookup for fun
     // - emit lookup for thisArg
     // - emit lookups for arg0, arg1
@@ -7070,22 +7082,20 @@ BytecodeEmitter::emitSelfHostedCallFunct
         return false;
     }
 
     ParseNode* pn2 = pn->pn_head;
     ParseNode* funNode = pn2->pn_next;
     if (!emitTree(funNode))
         return false;
 
-#ifdef DEBUG
     if (pn2->name() != cx->names().callContentFunction) {
-        if (!emit1(JSOP_DEBUGCHECKSELFHOSTED))
-            return false;
-    }
-#endif
+        if (!emitDebugOnlyCheckSelfHosted())
+            return false;
+    }
 
     ParseNode* thisArg = funNode->pn_next;
     if (!emitTree(thisArg))
         return false;
 
     bool oldEmittingForInit = emittingForInit;
     emittingForInit = false;
 
@@ -7199,29 +7209,37 @@ BytecodeEmitter::emitCallOrNew(ParseNode
       case PNK_DOT:
         if (pn2->as<PropertyAccess>().isSuper()) {
             if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop))
                 return false;
         } else {
             if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP))
                 return false;
         }
+
+        if (!emitDebugOnlyCheckSelfHosted())
+            return false;
+
         break;
       case PNK_ELEM:
         if (pn2->as<PropertyByValue>().isSuper()) {
             if (!emitSuperElemOp(pn2, JSOP_GETELEM_SUPER, /* isCall = */ callop))
                 return false;
         } else {
             if (!emitElemOp(pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM))
                 return false;
             if (callop) {
                 if (!emit1(JSOP_SWAP))
                     return false;
             }
         }
+
+        if (!emitDebugOnlyCheckSelfHosted())
+            return false;
+
         break;
       case PNK_FUNCTION:
         /*
          * Top level lambdas which are immediately invoked should be
          * treated as only running once. Every time they execute we will
          * create new types and scripts for their contents, to increase
          * the quality of type information within them and enable more
          * backend optimizations. Note that this does not depend on the
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -593,16 +593,17 @@ struct BytecodeEmitter
     bool emitLogical(ParseNode* pn);
     bool emitSequenceExpr(ParseNode* pn);
 
     MOZ_NEVER_INLINE bool emitIncOrDec(ParseNode* pn);
 
     bool emitConditionalExpression(ConditionalExpression& conditional);
 
     bool emitCallOrNew(ParseNode* pn);
+    bool emitDebugOnlyCheckSelfHosted();
     bool emitSelfHostedCallFunction(ParseNode* pn);
     bool emitSelfHostedResumeGenerator(ParseNode* pn);
     bool emitSelfHostedForceInterpreter(ParseNode* pn);
 
     bool emitComprehensionFor(ParseNode* compFor);
     bool emitComprehensionForIn(ParseNode* pn);
     bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* letBlockScope);
     bool emitComprehensionForOf(ParseNode* pn);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -8753,21 +8753,16 @@ Parser<ParseHandler>::memberExpr(YieldHa
                     handler.setOp(nextMember, JSOP_SPREADSUPERCALL);
 
                 Node thisName = newThisName();
                 if (!thisName)
                     return null();
                 return handler.newSetThis(thisName, nextMember);
             }
 
-            if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
-                report(ParseError, false, null(), JSMSG_SELFHOSTED_METHOD_CALL);
-                return null();
-            }
-
             nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate();
             if (!nextMember)
                 return null();
 
             JSOp op = JSOP_CALL;
             if (PropertyName* name = handler.maybeNameAnyParentheses(lhs)) {
                 if (tt == TOK_LP && name == context->names().eval) {
                     /* Select JSOP_EVAL and flag pc as needsCallObject. */
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -309,17 +309,16 @@ MSG_DEF(JSMSG_PAREN_IN_PAREN,          0
 MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list")
 MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after module specifier list")
 MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_TYPEERR, "redeclaration of identifier '{0}' in catch")
 MSG_DEF(JSMSG_REDECLARED_PARAM,        1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}")
 MSG_DEF(JSMSG_RESERVED_ID,             1, JSEXN_SYNTAXERR, "{0} is a reserved identifier")
 MSG_DEF(JSMSG_REST_WITH_DEFAULT,       0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
 MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, 1, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level {0} declarations")
 MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
-MSG_DEF(JSMSG_SELFHOSTED_METHOD_CALL,  0, JSEXN_SYNTAXERR, "self-hosted code may not contain direct method calls")
 MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND,     0, JSEXN_SYNTAXERR, "missing ; after for-loop condition")
 MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT,     0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")
 MSG_DEF(JSMSG_SEMI_BEFORE_STMNT,       0, JSEXN_SYNTAXERR, "missing ; before statement")
 MSG_DEF(JSMSG_SOURCE_TOO_LONG,         0, JSEXN_RANGEERR, "source is too long")
 MSG_DEF(JSMSG_STMT_AFTER_RETURN,       0, JSEXN_NONE, "unreachable code after return statement")
 MSG_DEF(JSMSG_STRICT_CODE_WITH,        0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements")
 MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
 MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR,    0, JSEXN_SYNTAXERR, "missing } in template string")
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,21 +24,21 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 332;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 333;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 421,
+static_assert(JSErr_Limit == 420,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)