Bug 1348407 - Initialize %ThrowTypeError% lazily, simplify CreateFunctionPrototype. r=jwalden
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 08 Jan 2018 10:26:00 +0100
changeset 449969 f0f8dc928f55d11f89e255cb540d3ba94be80902
parent 449968 ca0c739344f216aa733dc1c540cb69bc9193db71
child 449970 7d1773460b9ddb39163513801fed872f7f5397c9
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1348407
milestone59.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 1348407 - Initialize %ThrowTypeError% lazily, simplify CreateFunctionPrototype. r=jwalden
js/src/jit-test/tests/basic/bug1348407.js
js/src/jsfun.cpp
js/src/jsfun.h
js/src/vm/ArgumentsObject.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1348407.js
@@ -0,0 +1,6 @@
+if (!('oomTest' in this))
+    quit();
+x = evalcx("lazy");
+oomTest(function () {
+    x.eval("1");
+});
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -111,18 +111,18 @@ AdvanceToActiveCallLinear(JSContext* cx,
         if (!iter.isFunctionFrame())
             continue;
         if (iter.matchCallee(cx, fun))
             return true;
     }
     return false;
 }
 
-static void
-ThrowTypeErrorBehavior(JSContext* cx)
+void
+js::ThrowTypeErrorBehavior(JSContext* cx)
 {
     JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
                                      JSMSG_THROW_TYPE_ERROR);
 }
 
 static bool
 IsSloppyNormalFunction(JSFunction* fun)
 {
@@ -809,23 +809,16 @@ JSFunction::trace(JSTracer* trc)
 }
 
 static void
 fun_trace(JSTracer* trc, JSObject* obj)
 {
     obj->as<JSFunction>().trace(trc);
 }
 
-static bool
-ThrowTypeError(JSContext* cx, unsigned argc, Value* vp)
-{
-    ThrowTypeErrorBehavior(cx);
-    return false;
-}
-
 static JSObject*
 CreateFunctionConstructor(JSContext* cx, JSProtoKey key)
 {
     Rooted<GlobalObject*> global(cx, cx->global());
     RootedObject functionProto(cx, &global->getPrototype(JSProto_Function).toObject());
 
     RootedObject functionCtor(cx,
       NewFunctionWithProto(cx, Function, 1, JSFunction::NATIVE_CTOR,
@@ -901,61 +894,16 @@ CreateFunctionPrototype(JSContext* cx, J
     /*
      * The default 'new' group of Function.prototype is required by type
      * inference to have unknown properties, to simplify handling of e.g.
      * NewFunctionClone.
      */
     if (!JSObject::setNewGroupUnknown(cx, &JSFunction::class_, functionProto))
         return nullptr;
 
-    // Set the prototype before we call NewFunctionWithProto below. This
-    // ensures EmptyShape::getInitialShape can share function shapes.
-    self->setPrototype(key, ObjectValue(*functionProto));
-
-    // Construct the unique [[%ThrowTypeError%]] function object, used only for
-    // "callee" and "caller" accessors on strict mode arguments objects.  (The
-    // spec also uses this for "arguments" and "caller" on various functions,
-    // but we're experimenting with implementing them using accessors on
-    // |Function.prototype| right now.)
-    //
-    // Note that we can't use NewFunction here, even though we want the normal
-    // Function.prototype for our proto, because we're still in the middle of
-    // creating that as far as the world is concerned, so things will get all
-    // confused.
-    RootedFunction throwTypeError(cx,
-      NewFunctionWithProto(cx, ThrowTypeError, 0, JSFunction::NATIVE_FUN,
-                           nullptr, nullptr, functionProto, AllocKind::FUNCTION,
-                           SingletonObject));
-    if (!throwTypeError || !PreventExtensions(cx, throwTypeError))
-        return nullptr;
-
-    // The "length" property of %ThrowTypeError% is non-configurable, adjust
-    // the default property attributes accordingly.
-    Rooted<PropertyDescriptor> nonConfigurableDesc(cx);
-    nonConfigurableDesc.setAttributes(JSPROP_PERMANENT | JSPROP_IGNORE_READONLY |
-                                      JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_VALUE);
-
-    RootedId lengthId(cx, NameToId(cx->names().length));
-    ObjectOpResult lengthResult;
-    if (!NativeDefineProperty(cx, throwTypeError, lengthId, nonConfigurableDesc, lengthResult))
-        return nullptr;
-    MOZ_ASSERT(lengthResult);
-
-    // Non-standard: Also change "name" to non-configurable. ECMAScript defines
-    // %ThrowTypeError% as an anonymous function, i.e. it shouldn't actually
-    // get an own "name" property. To be consistent with other built-in,
-    // anonymous functions, we don't delete %ThrowTypeError%'s "name" property.
-    RootedId nameId(cx, NameToId(cx->names().name));
-    ObjectOpResult nameResult;
-    if (!NativeDefineProperty(cx, throwTypeError, nameId, nonConfigurableDesc, nameResult))
-        return nullptr;
-    MOZ_ASSERT(nameResult);
-
-    self->setThrowTypeError(throwTypeError);
-
     return functionProto;
 }
 
 static const ClassOps JSFunctionClassOps = {
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     fun_enumerate,
     nullptr,                 /* newEnumerate */
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -791,16 +791,19 @@ fun_toString(JSContext* cx, unsigned arg
 struct WellKnownSymbols;
 
 extern bool
 FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols);
 
 extern bool
 fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp);
 
+extern void
+ThrowTypeErrorBehavior(JSContext* cx);
+
 /*
  * Function extended with reserved slots for use by various kinds of functions.
  * Most functions do not have these extensions, but enough do that efficient
  * storage is required (no malloc'ed reserved slots).
  */
 class FunctionExtended : public JSFunction
 {
   public:
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -788,19 +788,23 @@ UnmappedArgumentsObject::obj_resolve(JSC
         attrs |= JSPROP_ENUMERATE;
     } else if (JSID_IS_ATOM(id, cx->names().length)) {
         if (argsobj->hasOverriddenLength())
             return true;
     } else {
         if (!JSID_IS_ATOM(id, cx->names().callee))
             return true;
 
+        JSObject* throwTypeError = GlobalObject::getOrCreateThrowTypeError(cx, cx->global());
+        if (!throwTypeError)
+            return false;
+
         attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER;
-        getter = CastAsGetterOp(argsobj->global().getThrowTypeError());
-        setter = CastAsSetterOp(argsobj->global().getThrowTypeError());
+        getter = CastAsGetterOp(throwTypeError);
+        setter = CastAsSetterOp(throwTypeError);
     }
 
     attrs |= JSPROP_RESOLVING;
     if (!NativeDefineAccessorProperty(cx, argsobj, id, getter, setter, attrs))
         return false;
 
     *resolvedp = true;
     return true;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -299,16 +299,67 @@ GlobalObject::initBuiltinConstructor(JSC
     if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING))
         return false;
 
     global->setConstructor(key, ObjectValue(*ctor));
     global->setPrototype(key, ObjectValue(*proto));
     return true;
 }
 
+static bool
+ThrowTypeError(JSContext* cx, unsigned argc, Value* vp)
+{
+    ThrowTypeErrorBehavior(cx);
+    return false;
+}
+
+/* static */ JSObject*
+GlobalObject::getOrCreateThrowTypeError(JSContext* cx, Handle<GlobalObject*> global)
+{
+    Value v = global->getReservedSlot(THROWTYPEERROR);
+    if (v.isObject())
+        return &v.toObject();
+    MOZ_ASSERT(v.isUndefined());
+
+    // Construct the unique [[%ThrowTypeError%]] function object, used only for
+    // "callee" and "caller" accessors on strict mode arguments objects.  (The
+    // spec also uses this for "arguments" and "caller" on various functions,
+    // but we're experimenting with implementing them using accessors on
+    // |Function.prototype| right now.)
+
+    RootedFunction throwTypeError(cx, NewNativeFunction(cx, ThrowTypeError, 0, nullptr));
+    if (!throwTypeError || !PreventExtensions(cx, throwTypeError))
+        return nullptr;
+
+    // The "length" property of %ThrowTypeError% is non-configurable, adjust
+    // the default property attributes accordingly.
+    Rooted<PropertyDescriptor> nonConfigurableDesc(cx);
+    nonConfigurableDesc.setAttributes(JSPROP_PERMANENT | JSPROP_IGNORE_READONLY |
+                                      JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_VALUE);
+
+    RootedId lengthId(cx, NameToId(cx->names().length));
+    ObjectOpResult lengthResult;
+    if (!NativeDefineProperty(cx, throwTypeError, lengthId, nonConfigurableDesc, lengthResult))
+        return nullptr;
+    MOZ_ASSERT(lengthResult);
+
+    // Non-standard: Also change "name" to non-configurable. ECMAScript defines
+    // %ThrowTypeError% as an anonymous function, i.e. it shouldn't actually
+    // get an own "name" property. To be consistent with other built-in,
+    // anonymous functions, we don't delete %ThrowTypeError%'s "name" property.
+    RootedId nameId(cx, NameToId(cx->names().name));
+    ObjectOpResult nameResult;
+    if (!NativeDefineProperty(cx, throwTypeError, nameId, nonConfigurableDesc, nameResult))
+        return nullptr;
+    MOZ_ASSERT(nameResult);
+
+    global->setReservedSlot(THROWTYPEERROR, ObjectValue(*throwTypeError));
+    return throwTypeError;
+}
+
 GlobalObject*
 GlobalObject::createInternal(JSContext* cx, const Class* clasp)
 {
     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
     MOZ_ASSERT(clasp->isTrace(JS_GlobalObjectTraceHook));
 
     JSObject* obj = NewObjectWithGivenProto(cx, clasp, nullptr, SingletonObject);
     if (!obj)
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -119,21 +119,16 @@ class GlobalObject : public NativeObject
      */
     static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
                   "global object slot counts are inconsistent");
 
   public:
     LexicalEnvironmentObject& lexicalEnvironment() const;
     GlobalScope& emptyGlobalScope() const;
 
-    void setThrowTypeError(JSFunction* fun) {
-        MOZ_ASSERT(getSlotRef(THROWTYPEERROR).isUndefined());
-        setSlot(THROWTYPEERROR, ObjectValue(*fun));
-    }
-
     void setOriginalEval(JSObject* evalobj) {
         MOZ_ASSERT(getSlotRef(EVAL).isUndefined());
         setSlot(EVAL, ObjectValue(*evalobj));
     }
 
     Value getConstructor(JSProtoKey key) const {
         MOZ_ASSERT(key <= JSProto_LIMIT);
         return getSlot(APPLICATION_SLOTS + key);
@@ -738,22 +733,17 @@ class GlobalObject : public NativeObject
                                       HandlePropertyName selfHostedName, HandleAtom name,
                                       unsigned nargs, MutableHandleValue funVal);
 
     bool hasRegExpStatics() const;
     static RegExpStatics* getRegExpStatics(JSContext* cx,
                                            Handle<GlobalObject*> global);
     RegExpStatics* getAlreadyCreatedRegExpStatics() const;
 
-    JSObject* getThrowTypeError() const {
-        const Value v = getReservedSlot(THROWTYPEERROR);
-        MOZ_ASSERT(v.isObject(),
-                   "attempting to access [[ThrowTypeError]] too early");
-        return &v.toObject();
-    }
+    static JSObject* getOrCreateThrowTypeError(JSContext* cx, Handle<GlobalObject*> global);
 
     static bool isRuntimeCodeGenEnabled(JSContext* cx, Handle<GlobalObject*> global);
 
     static bool getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global,
                                 MutableHandleObject eval);
 
     // Infallibly test whether the given value is the eval function for this global.
     bool valueIsEval(const Value& val);