Bug 1595745 - Part 2: Change Number to use ClassSpec. r=mgaudet
☠☠ backed out by ec8cad689121 ☠ ☠
authorAndré Bargull <andre.bargull@gmail.com>
Fri, 15 Nov 2019 15:00:39 +0000
changeset 502196 61d25028669b165048be8775429f79b1b136e13e
parent 502195 f082e5173ed480d3461749eff791207458c72b5e
child 502197 338ad438e066e879702224ba34317d3956bb2519
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmgaudet
bugs1595745
milestone72.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 1595745 - Part 2: Change Number to use ClassSpec. r=mgaudet In addition to a custom 'createProperty' hook, the Number ClassSpec also uses a 'FinishClassInitOp' to initialise Number-related global properties like `isNaN` and to initialise functions which are shared between Number and the global object (i.e. `parseInt`). Differential Revision: https://phabricator.services.mozilla.com/D52658
js/public/ProtoKey.h
js/src/jsnum.cpp
js/src/vm/NumberObject.h
--- a/js/public/ProtoKey.h
+++ b/js/public/ProtoKey.h
@@ -52,17 +52,17 @@
   IMAGINARY(Null, InitNullClass, dummy)                                      \
   REAL(Object, InitViaClassSpec, OCLASP(Plain))                              \
   REAL(Function, InitViaClassSpec, &JSFunction::class_)                      \
   REAL(Array, InitViaClassSpec, OCLASP(Array))                               \
   REAL(Boolean, InitViaClassSpec, OCLASP(Boolean))                           \
   REAL(JSON, InitJSONClass, CLASP(JSON))                                     \
   REAL(Date, InitViaClassSpec, OCLASP(Date))                                 \
   REAL(Math, InitMathClass, CLASP(Math))                                     \
-  REAL(Number, InitNumberClass, OCLASP(Number))                              \
+  REAL(Number, InitViaClassSpec, OCLASP(Number))                             \
   REAL(String, InitStringClass, OCLASP(String))                              \
   REAL(RegExp, InitViaClassSpec, OCLASP(RegExp))                             \
   REAL(Error, InitViaClassSpec, ERROR_CLASP(JSEXN_ERR))                      \
   REAL(InternalError, InitViaClassSpec, ERROR_CLASP(JSEXN_INTERNALERR))      \
   REAL(EvalError, InitViaClassSpec, ERROR_CLASP(JSEXN_EVALERR))              \
   REAL(RangeError, InitViaClassSpec, ERROR_CLASP(JSEXN_RANGEERR))            \
   REAL(ReferenceError, InitViaClassSpec, ERROR_CLASP(JSEXN_REFERENCEERR))    \
   REAL(SyntaxError, InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR))          \
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -625,17 +625,18 @@ bool js::num_parseInt(JSContext* cx, uns
 
 static const JSFunctionSpec number_functions[] = {
     JS_SELF_HOSTED_FN(js_isNaN_str, "Global_isNaN", 1, JSPROP_RESOLVING),
     JS_SELF_HOSTED_FN(js_isFinite_str, "Global_isFinite", 1, JSPROP_RESOLVING),
     JS_FS_END};
 
 const JSClass NumberObject::class_ = {
     js_Number_str,
-    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number)};
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
+    JS_NULL_CLASS_OPS, &NumberObject::classSpec_};
 
 static bool Number(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() > 0) {
     // BigInt proposal section 6.2, steps 2a-c.
     if (!ToNumeric(cx, args[0])) {
       return false;
@@ -1334,34 +1335,28 @@ void js::FinishRuntimeNumberState(JSRunt
    * The free also releases the memory for decimalSeparator and numGrouping
    * strings.
    */
   char* storage = const_cast<char*>(rt->thousandsSeparator.ref());
   js_free(storage);
 }
 #endif
 
-JSObject* js::InitNumberClass(JSContext* cx, Handle<GlobalObject*> global) {
-  Rooted<NumberObject*> numberProto(cx);
-  numberProto = GlobalObject::createBlankPrototype<NumberObject>(cx, global);
+JSObject* NumberObject::createPrototype(JSContext* cx, JSProtoKey key) {
+  NumberObject* numberProto =
+      GlobalObject::createBlankPrototype<NumberObject>(cx, cx->global());
   if (!numberProto) {
     return nullptr;
   }
   numberProto->setPrimitiveValue(0);
+  return numberProto;
+}
 
-  RootedFunction ctor(cx);
-  ctor = GlobalObject::createConstructor(cx, Number, cx->names().Number, 1);
-  if (!ctor) {
-    return nullptr;
-  }
-
-  if (!LinkConstructorAndPrototype(cx, ctor, numberProto)) {
-    return nullptr;
-  }
-
+static bool NumberClassFinish(JSContext* cx, HandleObject ctor,
+                              HandleObject proto) {
   // Our NaN must be one particular canonical value, because we rely on NaN
   // encoding for our value representation.  See Value.h.
   static const JSConstDoubleSpec number_constants[] = {
       // clang-format off
         {"NaN",               GenericNaN()               },
         {"POSITIVE_INFINITY", mozilla::PositiveInfinity<double>() },
         {"NEGATIVE_INFINITY", mozilla::NegativeInfinity<double>() },
         {"MAX_VALUE",         1.7976931348623157E+308    },
@@ -1373,76 +1368,74 @@ JSObject* js::InitNumberClass(JSContext*
         /* ES6 (May 2013 draft) 15.7.3.7 */
         {"EPSILON", 2.2204460492503130808472633361816e-16},
         {0,0}
       // clang-format on
   };
 
   // Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor.
   if (!JS_DefineConstDoubles(cx, ctor, number_constants)) {
-    return nullptr;
-  }
-
-  if (!DefinePropertiesAndFunctions(cx, ctor, nullptr, number_static_methods)) {
-    return nullptr;
+    return false;
   }
 
-  if (!DefinePropertiesAndFunctions(cx, numberProto, nullptr, number_methods)) {
-    return nullptr;
-  }
+  Handle<GlobalObject*> global = cx->global();
 
   if (!JS_DefineFunctions(cx, global, number_functions)) {
-    return nullptr;
+    return false;
   }
 
   // Number.parseInt should be the same function object as global parseInt.
   RootedId parseIntId(cx, NameToId(cx->names().parseInt));
   JSFunction* parseInt =
       DefineFunction(cx, global, parseIntId, num_parseInt, 2, JSPROP_RESOLVING);
   if (!parseInt) {
-    return nullptr;
+    return false;
   }
   RootedValue parseIntValue(cx, ObjectValue(*parseInt));
   if (!DefineDataProperty(cx, ctor, parseIntId, parseIntValue, 0)) {
-    return nullptr;
+    return false;
   }
 
   // Number.parseFloat should be the same function object as global
   // parseFloat.
   RootedId parseFloatId(cx, NameToId(cx->names().parseFloat));
   JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId,
                                           num_parseFloat, 1, JSPROP_RESOLVING);
   if (!parseFloat) {
-    return nullptr;
+    return false;
   }
   RootedValue parseFloatValue(cx, ObjectValue(*parseFloat));
   if (!DefineDataProperty(cx, ctor, parseFloatId, parseFloatValue, 0)) {
-    return nullptr;
+    return false;
   }
 
   RootedValue valueNaN(cx, JS::NaNValue());
   RootedValue valueInfinity(cx, JS::InfinityValue());
 
   // ES5 15.1.1.1, 15.1.1.2
   if (!NativeDefineDataProperty(
           cx, global, cx->names().NaN, valueNaN,
           JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING) ||
       !NativeDefineDataProperty(
           cx, global, cx->names().Infinity, valueInfinity,
           JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
-    return nullptr;
+    return false;
   }
 
-  if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Number, ctor,
-                                            numberProto)) {
-    return nullptr;
-  }
+  return true;
+}
 
-  return numberProto;
-}
+const ClassSpec NumberObject::classSpec_ = {
+    GenericCreateConstructor<Number, 1, gc::AllocKind::FUNCTION>,
+    NumberObject::createPrototype,
+    number_static_methods,
+    nullptr,
+    number_methods,
+    nullptr,
+    NumberClassFinish};
 
 static char* FracNumberToCString(JSContext* cx, ToCStringBuf* cbuf, double d,
                                  int base = 10) {
 #ifdef DEBUG
   {
     int32_t _;
     MOZ_ASSERT(!NumberEqualsInt32(d, &_));
   }
--- a/js/src/vm/NumberObject.h
+++ b/js/src/vm/NumberObject.h
@@ -2,45 +2,43 @@
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_NumberObject_h
 #define vm_NumberObject_h
 
-#include "jsnum.h"
+#include "vm/NativeObject.h"
 
 namespace js {
 
-class GlobalObject;
-
 class NumberObject : public NativeObject {
   /* Stores this Number object's [[PrimitiveValue]]. */
   static const unsigned PRIMITIVE_VALUE_SLOT = 0;
 
+  static const ClassSpec classSpec_;
+
  public:
   static const unsigned RESERVED_SLOTS = 1;
 
   static const JSClass class_;
 
   /*
    * Creates a new Number object boxing the given number.
    * If proto is nullptr, then Number.prototype will be used instead.
    */
   static inline NumberObject* create(JSContext* cx, double d,
                                      HandleObject proto = nullptr);
 
   double unbox() const { return getFixedSlot(PRIMITIVE_VALUE_SLOT).toNumber(); }
 
  private:
+  static JSObject* createPrototype(JSContext* cx, JSProtoKey key);
+
   inline void setPrimitiveValue(double d) {
     setFixedSlot(PRIMITIVE_VALUE_SLOT, NumberValue(d));
   }
-
-  /* For access to init, as Number.prototype is special. */
-  friend JSObject* js::InitNumberClass(JSContext* cx,
-                                       Handle<GlobalObject*> global);
 };
 
 }  // namespace js
 
 #endif /* vm_NumberObject_h */