js/src/jsobj.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * JS object implementation.
     9  */
    11 #include "jsobjinlines.h"
    13 #include "mozilla/ArrayUtils.h"
    14 #include "mozilla/MathAlgorithms.h"
    15 #include "mozilla/MemoryReporting.h"
    16 #include "mozilla/TemplateLib.h"
    18 #include <string.h>
    20 #include "jsapi.h"
    21 #include "jsarray.h"
    22 #include "jsatom.h"
    23 #include "jscntxt.h"
    24 #include "jsfriendapi.h"
    25 #include "jsfun.h"
    26 #include "jsgc.h"
    27 #include "jsiter.h"
    28 #include "jsnum.h"
    29 #include "jsopcode.h"
    30 #include "jsprf.h"
    31 #include "jsproxy.h"
    32 #include "jsscript.h"
    33 #include "jsstr.h"
    34 #include "jstypes.h"
    35 #include "jsutil.h"
    36 #include "jswatchpoint.h"
    37 #include "jswrapper.h"
    39 #include "builtin/Object.h"
    40 #include "frontend/BytecodeCompiler.h"
    41 #include "gc/Marking.h"
    42 #include "jit/AsmJSModule.h"
    43 #include "jit/BaselineJIT.h"
    44 #include "js/MemoryMetrics.h"
    45 #include "js/OldDebugAPI.h"
    46 #include "vm/ArgumentsObject.h"
    47 #include "vm/Interpreter.h"
    48 #include "vm/ProxyObject.h"
    49 #include "vm/RegExpStaticsObject.h"
    50 #include "vm/Shape.h"
    52 #include "jsatominlines.h"
    53 #include "jsboolinlines.h"
    54 #include "jscntxtinlines.h"
    55 #include "jscompartmentinlines.h"
    57 #include "vm/ArrayObject-inl.h"
    58 #include "vm/BooleanObject-inl.h"
    59 #include "vm/NumberObject-inl.h"
    60 #include "vm/ObjectImpl-inl.h"
    61 #include "vm/Runtime-inl.h"
    62 #include "vm/Shape-inl.h"
    63 #include "vm/StringObject-inl.h"
    65 using namespace js;
    66 using namespace js::gc;
    67 using namespace js::types;
    69 using mozilla::Maybe;
    70 using mozilla::RoundUpPow2;
    72 JS_STATIC_ASSERT(int32_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) == int64_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
    74 const Class JSObject::class_ = {
    75     js_Object_str,
    76     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
    77     JS_PropertyStub,         /* addProperty */
    78     JS_DeletePropertyStub,   /* delProperty */
    79     JS_PropertyStub,         /* getProperty */
    80     JS_StrictPropertyStub,   /* setProperty */
    81     JS_EnumerateStub,
    82     JS_ResolveStub,
    83     JS_ConvertStub
    84 };
    86 const Class* const js::ObjectClassPtr = &JSObject::class_;
    88 JS_FRIEND_API(JSObject *)
    89 JS_ObjectToInnerObject(JSContext *cx, HandleObject obj)
    90 {
    91     if (!obj) {
    92         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INACTIVE);
    93         return nullptr;
    94     }
    95     return GetInnerObject(cx, obj);
    96 }
    98 JS_FRIEND_API(JSObject *)
    99 JS_ObjectToOuterObject(JSContext *cx, HandleObject obj)
   100 {
   101     assertSameCompartment(cx, obj);
   102     return GetOuterObject(cx, obj);
   103 }
   105 JSObject *
   106 js::NonNullObject(JSContext *cx, const Value &v)
   107 {
   108     if (v.isPrimitive()) {
   109         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
   110         return nullptr;
   111     }
   112     return &v.toObject();
   113 }
   115 const char *
   116 js::InformalValueTypeName(const Value &v)
   117 {
   118     if (v.isObject())
   119         return v.toObject().getClass()->name;
   120     if (v.isString())
   121         return "string";
   122     if (v.isNumber())
   123         return "number";
   124     if (v.isBoolean())
   125         return "boolean";
   126     if (v.isNull())
   127         return "null";
   128     if (v.isUndefined())
   129         return "undefined";
   130     return "value";
   131 }
   133 bool
   134 js::NewPropertyDescriptorObject(JSContext *cx, Handle<PropertyDescriptor> desc,
   135                                 MutableHandleValue vp)
   136 {
   137     if (!desc.object()) {
   138         vp.setUndefined();
   139         return true;
   140     }
   142     /* We have our own property, so start creating the descriptor. */
   143     AutoPropDescRooter d(cx);
   145     d.initFromPropertyDescriptor(desc);
   146     if (!d.makeObject(cx))
   147         return false;
   148     vp.set(d.pd());
   149     return true;
   150 }
   152 void
   153 PropDesc::initFromPropertyDescriptor(Handle<PropertyDescriptor> desc)
   154 {
   155     isUndefined_ = false;
   156     pd_.setUndefined();
   157     attrs = uint8_t(desc.attributes());
   158     JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
   159     if (desc.hasGetterOrSetterObject()) {
   160         hasGet_ = true;
   161         get_ = desc.hasGetterObject() && desc.getterObject()
   162                ? ObjectValue(*desc.getterObject())
   163                : UndefinedValue();
   164         hasSet_ = true;
   165         set_ = desc.hasSetterObject() && desc.setterObject()
   166                ? ObjectValue(*desc.setterObject())
   167                : UndefinedValue();
   168         hasValue_ = false;
   169         value_.setUndefined();
   170         hasWritable_ = false;
   171     } else {
   172         hasGet_ = false;
   173         get_.setUndefined();
   174         hasSet_ = false;
   175         set_.setUndefined();
   176         hasValue_ = true;
   177         value_ = desc.value();
   178         hasWritable_ = true;
   179     }
   180     hasEnumerable_ = true;
   181     hasConfigurable_ = true;
   182 }
   184 bool
   185 PropDesc::makeObject(JSContext *cx)
   186 {
   187     MOZ_ASSERT(!isUndefined());
   189     RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
   190     if (!obj)
   191         return false;
   193     const JSAtomState &names = cx->names();
   194     RootedValue configurableVal(cx, BooleanValue((attrs & JSPROP_PERMANENT) == 0));
   195     RootedValue enumerableVal(cx, BooleanValue((attrs & JSPROP_ENUMERATE) != 0));
   196     RootedValue writableVal(cx, BooleanValue((attrs & JSPROP_READONLY) == 0));
   197     if ((hasConfigurable() &&
   198          !JSObject::defineProperty(cx, obj, names.configurable, configurableVal)) ||
   199         (hasEnumerable() &&
   200          !JSObject::defineProperty(cx, obj, names.enumerable, enumerableVal)) ||
   201         (hasGet() &&
   202          !JSObject::defineProperty(cx, obj, names.get, getterValue())) ||
   203         (hasSet() &&
   204          !JSObject::defineProperty(cx, obj, names.set, setterValue())) ||
   205         (hasValue() &&
   206          !JSObject::defineProperty(cx, obj, names.value, value())) ||
   207         (hasWritable() &&
   208          !JSObject::defineProperty(cx, obj, names.writable, writableVal)))
   209     {
   210         return false;
   211     }
   213     pd_.setObject(*obj);
   214     return true;
   215 }
   217 bool
   218 js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
   219                              MutableHandle<PropertyDescriptor> desc)
   220 {
   221     // FIXME: Call TrapGetOwnProperty directly once ScriptedIndirectProxies is removed
   222     if (obj->is<ProxyObject>())
   223         return Proxy::getOwnPropertyDescriptor(cx, obj, id, desc);
   225     RootedObject pobj(cx);
   226     RootedShape shape(cx);
   227     if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
   228         return false;
   229     if (!shape) {
   230         desc.object().set(nullptr);
   231         return true;
   232     }
   234     bool doGet = true;
   235     if (pobj->isNative()) {
   236         desc.setAttributes(GetShapeAttributes(pobj, shape));
   237         if (desc.hasGetterOrSetterObject()) {
   238             MOZ_ASSERT(desc.isShared());
   239             doGet = false;
   240             if (desc.hasGetterObject())
   241                 desc.setGetterObject(shape->getterObject());
   242             if (desc.hasSetterObject())
   243                 desc.setSetterObject(shape->setterObject());
   244         } else {
   245             // This is either a straight-up data property or (rarely) a
   246             // property with a JSPropertyOp getter/setter. The latter must be
   247             // reported to the caller as a plain data property, so don't
   248             // populate desc.getter/setter, and mask away the SHARED bit.
   249             desc.attributesRef() &= ~JSPROP_SHARED;
   250         }
   251     } else {
   252         if (!JSObject::getGenericAttributes(cx, pobj, id, &desc.attributesRef()))
   253             return false;
   254     }
   256     RootedValue value(cx);
   257     if (doGet && !JSObject::getGeneric(cx, obj, obj, id, &value))
   258         return false;
   260     desc.value().set(value);
   261     desc.object().set(obj);
   262     return true;
   263 }
   265 bool
   266 js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
   267 {
   268     Rooted<PropertyDescriptor> desc(cx);
   269     return GetOwnPropertyDescriptor(cx, obj, id, &desc) &&
   270            NewPropertyDescriptorObject(cx, desc, vp);
   271 }
   273 bool
   274 js::GetFirstArgumentAsObject(JSContext *cx, const CallArgs &args, const char *method,
   275                              MutableHandleObject objp)
   276 {
   277     if (args.length() == 0) {
   278         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   279                              method, "0", "s");
   280         return false;
   281     }
   283     HandleValue v = args[0];
   284     if (!v.isObject()) {
   285         char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
   286         if (!bytes)
   287             return false;
   288         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   289                              bytes, "not an object");
   290         js_free(bytes);
   291         return false;
   292     }
   294     objp.set(&v.toObject());
   295     return true;
   296 }
   298 static bool
   299 HasProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, bool *foundp)
   300 {
   301     if (!JSObject::hasProperty(cx, obj, id, foundp))
   302         return false;
   303     if (!*foundp) {
   304         vp.setUndefined();
   305         return true;
   306     }
   308     /*
   309      * We must go through the method read barrier in case id is 'get' or 'set'.
   310      * There is no obvious way to defer cloning a joined function object whose
   311      * identity will be used by DefinePropertyOnObject, e.g., or reflected via
   312      * js::GetOwnPropertyDescriptor, as the getter or setter callable object.
   313      */
   314     return !!JSObject::getGeneric(cx, obj, obj, id, vp);
   315 }
   317 bool
   318 PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors)
   319 {
   320     RootedValue v(cx, origval);
   322     /* 8.10.5 step 1 */
   323     if (v.isPrimitive()) {
   324         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
   325         return false;
   326     }
   327     RootedObject desc(cx, &v.toObject());
   329     /* Make a copy of the descriptor. We might need it later. */
   330     pd_ = v;
   332     isUndefined_ = false;
   334     /*
   335      * Start with the proper defaults.  XXX shouldn't be necessary when we get
   336      * rid of PropDesc::attributes()
   337      */
   338     attrs = JSPROP_PERMANENT | JSPROP_READONLY;
   340     bool found = false;
   341     RootedId id(cx);
   343     /* 8.10.5 step 3 */
   344     id = NameToId(cx->names().enumerable);
   345     if (!HasProperty(cx, desc, id, &v, &found))
   346         return false;
   347     if (found) {
   348         hasEnumerable_ = true;
   349         if (ToBoolean(v))
   350             attrs |= JSPROP_ENUMERATE;
   351     }
   353     /* 8.10.5 step 4 */
   354     id = NameToId(cx->names().configurable);
   355     if (!HasProperty(cx, desc, id, &v, &found))
   356         return false;
   357     if (found) {
   358         hasConfigurable_ = true;
   359         if (ToBoolean(v))
   360             attrs &= ~JSPROP_PERMANENT;
   361     }
   363     /* 8.10.5 step 5 */
   364     id = NameToId(cx->names().value);
   365     if (!HasProperty(cx, desc, id, &v, &found))
   366         return false;
   367     if (found) {
   368         hasValue_ = true;
   369         value_ = v;
   370     }
   372     /* 8.10.6 step 6 */
   373     id = NameToId(cx->names().writable);
   374     if (!HasProperty(cx, desc, id, &v, &found))
   375         return false;
   376     if (found) {
   377         hasWritable_ = true;
   378         if (ToBoolean(v))
   379             attrs &= ~JSPROP_READONLY;
   380     }
   382     /* 8.10.7 step 7 */
   383     id = NameToId(cx->names().get);
   384     if (!HasProperty(cx, desc, id, &v, &found))
   385         return false;
   386     if (found) {
   387         hasGet_ = true;
   388         get_ = v;
   389         attrs |= JSPROP_GETTER | JSPROP_SHARED;
   390         attrs &= ~JSPROP_READONLY;
   391         if (checkAccessors && !checkGetter(cx))
   392             return false;
   393     }
   395     /* 8.10.7 step 8 */
   396     id = NameToId(cx->names().set);
   397     if (!HasProperty(cx, desc, id, &v, &found))
   398         return false;
   399     if (found) {
   400         hasSet_ = true;
   401         set_ = v;
   402         attrs |= JSPROP_SETTER | JSPROP_SHARED;
   403         attrs &= ~JSPROP_READONLY;
   404         if (checkAccessors && !checkSetter(cx))
   405             return false;
   406     }
   408     /* 8.10.7 step 9 */
   409     if ((hasGet() || hasSet()) && (hasValue() || hasWritable())) {
   410         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DESCRIPTOR);
   411         return false;
   412     }
   414     JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
   416     return true;
   417 }
   419 void
   420 PropDesc::complete()
   421 {
   422     if (isGenericDescriptor() || isDataDescriptor()) {
   423         if (!hasValue_) {
   424             hasValue_ = true;
   425             value_.setUndefined();
   426         }
   427         if (!hasWritable_) {
   428             hasWritable_ = true;
   429             attrs |= JSPROP_READONLY;
   430         }
   431     } else {
   432         if (!hasGet_) {
   433             hasGet_ = true;
   434             get_.setUndefined();
   435         }
   436         if (!hasSet_) {
   437             hasSet_ = true;
   438             set_.setUndefined();
   439         }
   440     }
   441     if (!hasEnumerable_) {
   442         hasEnumerable_ = true;
   443         attrs &= ~JSPROP_ENUMERATE;
   444     }
   445     if (!hasConfigurable_) {
   446         hasConfigurable_ = true;
   447         attrs |= JSPROP_PERMANENT;
   448     }
   449 }
   451 bool
   452 js::Throw(JSContext *cx, jsid id, unsigned errorNumber)
   453 {
   454     JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1);
   456     JSString *idstr = IdToString(cx, id);
   457     if (!idstr)
   458        return false;
   459     JSAutoByteString bytes(cx, idstr);
   460     if (!bytes)
   461         return false;
   462     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber, bytes.ptr());
   463     return false;
   464 }
   466 bool
   467 js::Throw(JSContext *cx, JSObject *obj, unsigned errorNumber)
   468 {
   469     if (js_ErrorFormatString[errorNumber].argCount == 1) {
   470         RootedValue val(cx, ObjectValue(*obj));
   471         js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
   472                                  JSDVG_IGNORE_STACK, val, NullPtr(),
   473                                  nullptr, nullptr);
   474     } else {
   475         JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
   476         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber);
   477     }
   478     return false;
   479 }
   481 static bool
   482 Reject(JSContext *cx, unsigned errorNumber, bool throwError, jsid id, bool *rval)
   483 {
   484     if (throwError)
   485         return Throw(cx, id, errorNumber);
   487     *rval = false;
   488     return true;
   489 }
   491 static bool
   492 Reject(JSContext *cx, JSObject *obj, unsigned errorNumber, bool throwError, bool *rval)
   493 {
   494     if (throwError)
   495         return Throw(cx, obj, errorNumber);
   497     *rval = false;
   498     return true;
   499 }
   501 static bool
   502 Reject(JSContext *cx, HandleId id, unsigned errorNumber, bool throwError, bool *rval)
   503 {
   504     if (throwError)
   505         return Throw(cx, id, errorNumber);
   507     *rval = false;
   508     return true;
   509 }
   511 // See comments on CheckDefineProperty in jsobj.h.
   512 //
   513 // DefinePropertyOnObject has its own implementation of these checks.
   514 //
   515 JS_FRIEND_API(bool)
   516 js::CheckDefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
   517                         PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
   518 {
   519     if (!obj->isNative())
   520         return true;
   522     // ES5 8.12.9 Step 1. Even though we know obj is native, we use generic
   523     // APIs for shorter, more readable code.
   524     Rooted<PropertyDescriptor> desc(cx);
   525     if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
   526         return false;
   528     // This does not have to check obj's extensibility when !desc.obj (steps
   529     // 2-3) because the low-level methods JSObject::{add,put}Property check
   530     // for that.
   531     if (desc.object() && desc.isPermanent()) {
   532         // Steps 6-11, skipping step 10.a.ii. Prohibit redefining a permanent
   533         // property with different metadata, except to make a writable property
   534         // non-writable.
   535         if (getter != desc.getter() ||
   536             setter != desc.setter() ||
   537             (attrs != desc.attributes() && attrs != (desc.attributes() | JSPROP_READONLY)))
   538         {
   539             return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
   540         }
   542         // Step 10.a.ii. Prohibit changing the value of a non-configurable,
   543         // non-writable data property.
   544         if ((desc.attributes() & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_READONLY)) == JSPROP_READONLY) {
   545             bool same;
   546             if (!SameValue(cx, value, desc.value(), &same))
   547                 return false;
   548             if (!same)
   549                 return JSObject::reportReadOnly(cx, id);
   550         }
   551     }
   552     return true;
   553 }
   555 static bool
   556 DefinePropertyOnObject(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
   557                        bool throwError, bool *rval)
   558 {
   559     /* 8.12.9 step 1. */
   560     RootedShape shape(cx);
   561     RootedObject obj2(cx);
   562     JS_ASSERT(!obj->getOps()->lookupGeneric);
   563     if (!HasOwnProperty<CanGC>(cx, nullptr, obj, id, &obj2, &shape))
   564         return false;
   566     JS_ASSERT(!obj->getOps()->defineProperty);
   568     /* 8.12.9 steps 2-4. */
   569     if (!shape) {
   570         bool extensible;
   571         if (!JSObject::isExtensible(cx, obj, &extensible))
   572             return false;
   573         if (!extensible)
   574             return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
   576         *rval = true;
   578         if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
   579             JS_ASSERT(!obj->getOps()->defineProperty);
   580             RootedValue v(cx, desc.hasValue() ? desc.value() : UndefinedValue());
   581             return baseops::DefineGeneric(cx, obj, id, v,
   582                                           JS_PropertyStub, JS_StrictPropertyStub,
   583                                           desc.attributes());
   584         }
   586         JS_ASSERT(desc.isAccessorDescriptor());
   588         return baseops::DefineGeneric(cx, obj, id, UndefinedHandleValue,
   589                                       desc.getter(), desc.setter(), desc.attributes());
   590     }
   592     /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
   593     RootedValue v(cx);
   595     JS_ASSERT(obj == obj2);
   597     bool shapeDataDescriptor = true,
   598          shapeAccessorDescriptor = false,
   599          shapeWritable = true,
   600          shapeConfigurable = true,
   601          shapeEnumerable = true,
   602          shapeHasDefaultGetter = true,
   603          shapeHasDefaultSetter = true,
   604          shapeHasGetterValue = false,
   605          shapeHasSetterValue = false;
   606     uint8_t shapeAttributes = GetShapeAttributes(obj, shape);
   607     if (!IsImplicitDenseOrTypedArrayElement(shape)) {
   608         shapeDataDescriptor = shape->isDataDescriptor();
   609         shapeAccessorDescriptor = shape->isAccessorDescriptor();
   610         shapeWritable = shape->writable();
   611         shapeConfigurable = shape->configurable();
   612         shapeEnumerable = shape->enumerable();
   613         shapeHasDefaultGetter = shape->hasDefaultGetter();
   614         shapeHasDefaultSetter = shape->hasDefaultSetter();
   615         shapeHasGetterValue = shape->hasGetterValue();
   616         shapeHasSetterValue = shape->hasSetterValue();
   617         shapeAttributes = shape->attributes();
   618     }
   620     do {
   621         if (desc.isAccessorDescriptor()) {
   622             if (!shapeAccessorDescriptor)
   623                 break;
   625             if (desc.hasGet()) {
   626                 bool same;
   627                 if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
   628                     return false;
   629                 if (!same)
   630                     break;
   631             }
   633             if (desc.hasSet()) {
   634                 bool same;
   635                 if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
   636                     return false;
   637                 if (!same)
   638                     break;
   639             }
   640         } else {
   641             /*
   642              * Determine the current value of the property once, if the current
   643              * value might actually need to be used or preserved later.  NB: we
   644              * guard on whether the current property is a data descriptor to
   645              * avoid calling a getter; we won't need the value if it's not a
   646              * data descriptor.
   647              */
   648             if (IsImplicitDenseOrTypedArrayElement(shape)) {
   649                 v = obj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
   650             } else if (shape->isDataDescriptor()) {
   651                 /*
   652                  * We must rule out a non-configurable js::PropertyOp-guarded
   653                  * property becoming a writable unguarded data property, since
   654                  * such a property can have its value changed to one the getter
   655                  * and setter preclude.
   656                  *
   657                  * A desc lacking writable but with value is a data descriptor
   658                  * and we must reject it as if it had writable: true if current
   659                  * is writable.
   660                  */
   661                 if (!shape->configurable() &&
   662                     (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) &&
   663                     desc.isDataDescriptor() &&
   664                     (desc.hasWritable() ? desc.writable() : shape->writable()))
   665                 {
   666                     return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
   667                 }
   669                 if (!NativeGet(cx, obj, obj2, shape, &v))
   670                     return false;
   671             }
   673             if (desc.isDataDescriptor()) {
   674                 if (!shapeDataDescriptor)
   675                     break;
   677                 bool same;
   678                 if (desc.hasValue()) {
   679                     if (!SameValue(cx, desc.value(), v, &same))
   680                         return false;
   681                     if (!same) {
   682                         /*
   683                          * Insist that a non-configurable js::PropertyOp data
   684                          * property is frozen at exactly the last-got value.
   685                          *
   686                          * Duplicate the first part of the big conjunction that
   687                          * we tested above, rather than add a local bool flag.
   688                          * Likewise, don't try to keep shape->writable() in a
   689                          * flag we veto from true to false for non-configurable
   690                          * PropertyOp-based data properties and test before the
   691                          * SameValue check later on in order to re-use that "if
   692                          * (!SameValue) Reject" logic.
   693                          *
   694                          * This function is large and complex enough that it
   695                          * seems best to repeat a small bit of code and return
   696                          * Reject(...) ASAP, instead of being clever.
   697                          */
   698                         if (!shapeConfigurable &&
   699                             (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()))
   700                         {
   701                             return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
   702                         }
   703                         break;
   704                     }
   705                 }
   706                 if (desc.hasWritable() && desc.writable() != shapeWritable)
   707                     break;
   708             } else {
   709                 /* The only fields in desc will be handled below. */
   710                 JS_ASSERT(desc.isGenericDescriptor());
   711             }
   712         }
   714         if (desc.hasConfigurable() && desc.configurable() != shapeConfigurable)
   715             break;
   716         if (desc.hasEnumerable() && desc.enumerable() != shapeEnumerable)
   717             break;
   719         /* The conditions imposed by step 5 or step 6 apply. */
   720         *rval = true;
   721         return true;
   722     } while (0);
   724     /* 8.12.9 step 7. */
   725     if (!shapeConfigurable) {
   726         if ((desc.hasConfigurable() && desc.configurable()) ||
   727             (desc.hasEnumerable() && desc.enumerable() != shape->enumerable())) {
   728             return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
   729         }
   730     }
   732     bool callDelProperty = false;
   734     if (desc.isGenericDescriptor()) {
   735         /* 8.12.9 step 8, no validation required */
   736     } else if (desc.isDataDescriptor() != shapeDataDescriptor) {
   737         /* 8.12.9 step 9. */
   738         if (!shapeConfigurable)
   739             return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
   740     } else if (desc.isDataDescriptor()) {
   741         /* 8.12.9 step 10. */
   742         JS_ASSERT(shapeDataDescriptor);
   743         if (!shapeConfigurable && !shape->writable()) {
   744             if (desc.hasWritable() && desc.writable())
   745                 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
   746             if (desc.hasValue()) {
   747                 bool same;
   748                 if (!SameValue(cx, desc.value(), v, &same))
   749                     return false;
   750                 if (!same)
   751                     return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
   752             }
   753         }
   755         callDelProperty = !shapeHasDefaultGetter || !shapeHasDefaultSetter;
   756     } else {
   757         /* 8.12.9 step 11. */
   758         JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
   759         if (!shape->configurable()) {
   760             if (desc.hasSet()) {
   761                 bool same;
   762                 if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
   763                     return false;
   764                 if (!same)
   765                     return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
   766             }
   768             if (desc.hasGet()) {
   769                 bool same;
   770                 if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
   771                     return false;
   772                 if (!same)
   773                     return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
   774             }
   775         }
   776     }
   778     /* 8.12.9 step 12. */
   779     unsigned attrs;
   780     PropertyOp getter;
   781     StrictPropertyOp setter;
   782     if (desc.isGenericDescriptor()) {
   783         unsigned changed = 0;
   784         if (desc.hasConfigurable())
   785             changed |= JSPROP_PERMANENT;
   786         if (desc.hasEnumerable())
   787             changed |= JSPROP_ENUMERATE;
   789         attrs = (shapeAttributes & ~changed) | (desc.attributes() & changed);
   790         getter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_PropertyStub : shape->getter();
   791         setter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_StrictPropertyStub : shape->setter();
   792     } else if (desc.isDataDescriptor()) {
   793         unsigned unchanged = 0;
   794         if (!desc.hasConfigurable())
   795             unchanged |= JSPROP_PERMANENT;
   796         if (!desc.hasEnumerable())
   797             unchanged |= JSPROP_ENUMERATE;
   798         /* Watch out for accessor -> data transformations here. */
   799         if (!desc.hasWritable() && shapeDataDescriptor)
   800             unchanged |= JSPROP_READONLY;
   802         if (desc.hasValue())
   803             v = desc.value();
   804         attrs = (desc.attributes() & ~unchanged) | (shapeAttributes & unchanged);
   805         getter = JS_PropertyStub;
   806         setter = JS_StrictPropertyStub;
   807     } else {
   808         JS_ASSERT(desc.isAccessorDescriptor());
   810         /* 8.12.9 step 12. */
   811         unsigned changed = 0;
   812         if (desc.hasConfigurable())
   813             changed |= JSPROP_PERMANENT;
   814         if (desc.hasEnumerable())
   815             changed |= JSPROP_ENUMERATE;
   816         if (desc.hasGet())
   817             changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY;
   818         if (desc.hasSet())
   819             changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY;
   821         attrs = (desc.attributes() & changed) | (shapeAttributes & ~changed);
   822         if (desc.hasGet()) {
   823             getter = desc.getter();
   824         } else {
   825             getter = (shapeHasDefaultGetter && !shapeHasGetterValue)
   826                      ? JS_PropertyStub
   827                      : shape->getter();
   828         }
   829         if (desc.hasSet()) {
   830             setter = desc.setter();
   831         } else {
   832             setter = (shapeHasDefaultSetter && !shapeHasSetterValue)
   833                      ? JS_StrictPropertyStub
   834                      : shape->setter();
   835         }
   836     }
   838     *rval = true;
   840     /*
   841      * Since "data" properties implemented using native C functions may rely on
   842      * side effects during setting, we must make them aware that they have been
   843      * "assigned"; deleting the property before redefining it does the trick.
   844      * See bug 539766, where we ran into problems when we redefined
   845      * arguments.length without making the property aware that its value had
   846      * been changed (which would have happened if we had deleted it before
   847      * redefining it or we had invoked its setter to change its value).
   848      */
   849     if (callDelProperty) {
   850         bool succeeded;
   851         if (!CallJSDeletePropertyOp(cx, obj2->getClass()->delProperty, obj2, id, &succeeded))
   852             return false;
   853     }
   855     return baseops::DefineGeneric(cx, obj, id, v, getter, setter, attrs);
   856 }
   858 /* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */
   859 static bool
   860 DefinePropertyOnArray(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, const PropDesc &desc,
   861                       bool throwError, bool *rval)
   862 {
   863     /* Step 2. */
   864     if (id == NameToId(cx->names().length)) {
   865         // Canonicalize value, if necessary, before proceeding any further.  It
   866         // would be better if this were always/only done by ArraySetLength.
   867         // But canonicalization may throw a RangeError (or other exception, if
   868         // the value is an object with user-defined conversion semantics)
   869         // before other attributes are checked.  So as long as our internal
   870         // defineProperty hook doesn't match the ECMA one, this duplicate
   871         // checking can't be helped.
   872         RootedValue v(cx);
   873         if (desc.hasValue()) {
   874             uint32_t newLen;
   875             if (!CanonicalizeArrayLengthValue<SequentialExecution>(cx, desc.value(), &newLen))
   876                 return false;
   877             v.setNumber(newLen);
   878         } else {
   879             v.setNumber(arr->length());
   880         }
   882         if (desc.hasConfigurable() && desc.configurable())
   883             return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
   884         if (desc.hasEnumerable() && desc.enumerable())
   885             return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
   887         if (desc.isAccessorDescriptor())
   888             return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
   890         unsigned attrs = arr->nativeLookup(cx, id)->attributes();
   891         if (!arr->lengthIsWritable()) {
   892             if (desc.hasWritable() && desc.writable())
   893                 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
   894         } else {
   895             if (desc.hasWritable() && !desc.writable())
   896                 attrs = attrs | JSPROP_READONLY;
   897         }
   899         return ArraySetLength<SequentialExecution>(cx, arr, id, attrs, v, throwError);
   900     }
   902     /* Step 3. */
   903     uint32_t index;
   904     if (js_IdIsIndex(id, &index)) {
   905         /* Step 3b. */
   906         uint32_t oldLen = arr->length();
   908         /* Steps 3a, 3e. */
   909         if (index >= oldLen && !arr->lengthIsWritable())
   910             return Reject(cx, arr, JSMSG_CANT_APPEND_TO_ARRAY, throwError, rval);
   912         /* Steps 3f-j. */
   913         return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval);
   914     }
   916     /* Step 4. */
   917     return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval);
   918 }
   920 bool
   921 js::DefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
   922                    bool throwError, bool *rval)
   923 {
   924     if (obj->is<ArrayObject>()) {
   925         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
   926         return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval);
   927     }
   929     if (obj->getOps()->lookupGeneric) {
   930         /*
   931          * FIXME: Once ScriptedIndirectProxies are removed, this code should call
   932          * TrapDefineOwnProperty directly
   933          */
   934         if (obj->is<ProxyObject>()) {
   935             RootedValue pd(cx, desc.pd());
   936             return Proxy::defineProperty(cx, obj, id, pd);
   937         }
   938         return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
   939     }
   941     return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
   942 }
   944 bool
   945 js::DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue descriptor,
   946                       bool *bp)
   947 {
   948     AutoPropDescArrayRooter descs(cx);
   949     PropDesc *desc = descs.append();
   950     if (!desc || !desc->initialize(cx, descriptor))
   951         return false;
   953     bool rval;
   954     if (!DefineProperty(cx, obj, id, *desc, true, &rval))
   955         return false;
   956     *bp = !!rval;
   957     return true;
   958 }
   960 bool
   961 js::DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id,
   962                       Handle<PropertyDescriptor> descriptor, bool *bp)
   963 {
   964     AutoPropDescArrayRooter descs(cx);
   965     PropDesc *desc = descs.append();
   966     if (!desc)
   967         return false;
   969     desc->initFromPropertyDescriptor(descriptor);
   971     bool rval;
   972     if (!DefineProperty(cx, obj, id, *desc, true, &rval))
   973         return false;
   974     *bp = !!rval;
   975     return true;
   976 }
   979 bool
   980 js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
   981                             AutoIdVector *ids, AutoPropDescArrayRooter *descs)
   982 {
   983     if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids))
   984         return false;
   986     RootedId id(cx);
   987     for (size_t i = 0, len = ids->length(); i < len; i++) {
   988         id = (*ids)[i];
   989         PropDesc* desc = descs->append();
   990         RootedValue v(cx);
   991         if (!desc ||
   992             !JSObject::getGeneric(cx, props, props, id, &v) ||
   993             !desc->initialize(cx, v, checkAccessors))
   994         {
   995             return false;
   996         }
   997     }
   998     return true;
   999 }
  1001 bool
  1002 js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
  1004     AutoIdVector ids(cx);
  1005     AutoPropDescArrayRooter descs(cx);
  1006     if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
  1007         return false;
  1009     if (obj->is<ArrayObject>()) {
  1010         bool dummy;
  1011         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
  1012         for (size_t i = 0, len = ids.length(); i < len; i++) {
  1013             if (!DefinePropertyOnArray(cx, arr, ids.handleAt(i), descs[i], true, &dummy))
  1014                 return false;
  1016         return true;
  1019     if (obj->getOps()->lookupGeneric) {
  1020         /*
  1021          * FIXME: Once ScriptedIndirectProxies are removed, this code should call
  1022          * TrapDefineOwnProperty directly
  1023          */
  1024         if (obj->is<ProxyObject>()) {
  1025             for (size_t i = 0, len = ids.length(); i < len; i++) {
  1026                 RootedValue pd(cx, descs[i].pd());
  1027                 if (!Proxy::defineProperty(cx, obj, ids.handleAt(i), pd))
  1028                     return false;
  1030             return true;
  1032         bool dummy;
  1033         return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, true, &dummy);
  1036     bool dummy;
  1037     for (size_t i = 0, len = ids.length(); i < len; i++) {
  1038         if (!DefinePropertyOnObject(cx, obj, ids.handleAt(i), descs[i], true, &dummy))
  1039             return false;
  1042     return true;
  1045 extern bool
  1046 js_PopulateObject(JSContext *cx, HandleObject newborn, HandleObject props)
  1048     return DefineProperties(cx, newborn, props);
  1051 js::types::TypeObject*
  1052 JSObject::uninlinedGetType(JSContext *cx)
  1054     return getType(cx);
  1057 void
  1058 JSObject::uninlinedSetType(js::types::TypeObject *newType)
  1060     setType(newType);
  1063 /* static */ inline unsigned
  1064 JSObject::getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it)
  1066     /* Make all attributes permanent; if freezing, make data attributes read-only. */
  1067     if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
  1068         return JSPROP_PERMANENT | JSPROP_READONLY;
  1069     return JSPROP_PERMANENT;
  1072 /* static */ bool
  1073 JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
  1075     assertSameCompartment(cx, obj);
  1076     JS_ASSERT(it == SEAL || it == FREEZE);
  1078     bool extensible;
  1079     if (!JSObject::isExtensible(cx, obj, &extensible))
  1080         return false;
  1081     if (extensible && !JSObject::preventExtensions(cx, obj))
  1082         return false;
  1084     AutoIdVector props(cx);
  1085     if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
  1086         return false;
  1088     /* preventExtensions must sparsify dense objects, so we can assign to holes without checks. */
  1089     JS_ASSERT_IF(obj->isNative(), obj->getDenseCapacity() == 0);
  1091     if (obj->isNative() && !obj->inDictionaryMode() && !obj->is<TypedArrayObject>()) {
  1092         /*
  1093          * Seal/freeze non-dictionary objects by constructing a new shape
  1094          * hierarchy mirroring the original one, which can be shared if many
  1095          * objects with the same structure are sealed/frozen. If we use the
  1096          * generic path below then any non-empty object will be converted to
  1097          * dictionary mode.
  1098          */
  1099         RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(),
  1100                                                          obj->getTaggedProto(),
  1101                                                          obj->getParent(),
  1102                                                          obj->getMetadata(),
  1103                                                          obj->numFixedSlots(),
  1104                                                          obj->lastProperty()->getObjectFlags()));
  1105         if (!last)
  1106             return false;
  1108         /* Get an in order list of the shapes in this object. */
  1109         AutoShapeVector shapes(cx);
  1110         for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
  1111             if (!shapes.append(&r.front()))
  1112                 return false;
  1114         Reverse(shapes.begin(), shapes.end());
  1116         for (size_t i = 0; i < shapes.length(); i++) {
  1117             StackShape unrootedChild(shapes[i]);
  1118             RootedGeneric<StackShape*> child(cx, &unrootedChild);
  1119             child->attrs |= getSealedOrFrozenAttributes(child->attrs, it);
  1121             if (!JSID_IS_EMPTY(child->propid) && it == FREEZE)
  1122                 MarkTypePropertyNonWritable(cx, obj, child->propid);
  1124             last = cx->compartment()->propertyTree.getChild(cx, last, *child);
  1125             if (!last)
  1126                 return false;
  1129         JS_ASSERT(obj->lastProperty()->slotSpan() == last->slotSpan());
  1130         JS_ALWAYS_TRUE(setLastProperty(cx, obj, last));
  1131     } else {
  1132         RootedId id(cx);
  1133         for (size_t i = 0; i < props.length(); i++) {
  1134             id = props[i];
  1136             unsigned attrs;
  1137             if (!getGenericAttributes(cx, obj, id, &attrs))
  1138                 return false;
  1140             unsigned new_attrs = getSealedOrFrozenAttributes(attrs, it);
  1142             /* If we already have the attributes we need, skip the setAttributes call. */
  1143             if ((attrs | new_attrs) == attrs)
  1144                 continue;
  1146             attrs |= new_attrs;
  1147             if (!setGenericAttributes(cx, obj, id, &attrs))
  1148                 return false;
  1152     // Ordinarily ArraySetLength handles this, but we're going behind its back
  1153     // right now, so we must do this manually.  Neither the custom property
  1154     // tree mutations nor the setGenericAttributes call in the above code will
  1155     // do this for us.
  1156     //
  1157     // ArraySetLength also implements the capacity <= length invariant for
  1158     // arrays with non-writable length.  We don't need to do anything special
  1159     // for that, because capacity was zeroed out by preventExtensions.  (See
  1160     // the assertion before the if-else above.)
  1161     if (it == FREEZE && obj->is<ArrayObject>())
  1162         obj->getElementsHeader()->setNonwritableArrayLength();
  1164     return true;
  1167 /* static */ bool
  1168 JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp)
  1170     bool extensible;
  1171     if (!JSObject::isExtensible(cx, obj, &extensible))
  1172         return false;
  1173     if (extensible) {
  1174         *resultp = false;
  1175         return true;
  1178     if (obj->is<TypedArrayObject>()) {
  1179         if (it == SEAL) {
  1180             // Typed arrays are always sealed.
  1181             *resultp = true;
  1182         } else {
  1183             // Typed arrays cannot be frozen, but an empty typed array is
  1184             // trivially frozen.
  1185             *resultp = (obj->as<TypedArrayObject>().length() == 0);
  1187         return true;
  1190     AutoIdVector props(cx);
  1191     if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
  1192         return false;
  1194     RootedId id(cx);
  1195     for (size_t i = 0, len = props.length(); i < len; i++) {
  1196         id = props[i];
  1198         unsigned attrs;
  1199         if (!getGenericAttributes(cx, obj, id, &attrs))
  1200             return false;
  1202         /*
  1203          * If the property is configurable, this object is neither sealed nor
  1204          * frozen. If the property is a writable data property, this object is
  1205          * not frozen.
  1206          */
  1207         if (!(attrs & JSPROP_PERMANENT) ||
  1208             (it == FREEZE && !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))))
  1210             *resultp = false;
  1211             return true;
  1215     /* All properties checked out. This object is sealed/frozen. */
  1216     *resultp = true;
  1217     return true;
  1220 /* static */
  1221 const char *
  1222 JSObject::className(JSContext *cx, HandleObject obj)
  1224     assertSameCompartment(cx, obj);
  1226     if (obj->is<ProxyObject>())
  1227         return Proxy::className(cx, obj);
  1229     return obj->getClass()->name;
  1232 /*
  1233  * Get the GC kind to use for scripted 'new' on the given class.
  1234  * FIXME bug 547327: estimate the size from the allocation site.
  1235  */
  1236 static inline gc::AllocKind
  1237 NewObjectGCKind(const js::Class *clasp)
  1239     if (clasp == &ArrayObject::class_)
  1240         return gc::FINALIZE_OBJECT8;
  1241     if (clasp == &JSFunction::class_)
  1242         return gc::FINALIZE_OBJECT2;
  1243     return gc::FINALIZE_OBJECT4;
  1246 static inline JSObject *
  1247 NewObject(ExclusiveContext *cx, types::TypeObject *type_, JSObject *parent, gc::AllocKind kind,
  1248           NewObjectKind newKind)
  1250     const Class *clasp = type_->clasp();
  1252     JS_ASSERT(clasp != &ArrayObject::class_);
  1253     JS_ASSERT_IF(clasp == &JSFunction::class_,
  1254                  kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
  1255     JS_ASSERT_IF(parent, &parent->global() == cx->global());
  1257     RootedTypeObject type(cx, type_);
  1259     JSObject *metadata = nullptr;
  1260     if (!NewObjectMetadata(cx, &metadata))
  1261         return nullptr;
  1263     // For objects which can have fixed data following the object, only use
  1264     // enough fixed slots to cover the number of reserved slots in the object,
  1265     // regardless of the allocation kind specified.
  1266     size_t nfixed = ClassCanHaveFixedData(clasp)
  1267                     ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
  1268                     : GetGCKindSlots(kind, clasp);
  1270     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, type->proto(),
  1271                                                       parent, metadata, nfixed));
  1272     if (!shape)
  1273         return nullptr;
  1275     gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
  1276     JSObject *obj = JSObject::create(cx, kind, heap, shape, type);
  1277     if (!obj)
  1278         return nullptr;
  1280     if (newKind == SingletonObject) {
  1281         RootedObject nobj(cx, obj);
  1282         if (!JSObject::setSingletonType(cx, nobj))
  1283             return nullptr;
  1284         obj = nobj;
  1287     /*
  1288      * This will cancel an already-running incremental GC from doing any more
  1289      * slices, and it will prevent any future incremental GCs.
  1290      */
  1291     bool globalWithoutCustomTrace = clasp->trace == JS_GlobalObjectTraceHook &&
  1292                                     !cx->compartment()->options().getTrace();
  1293     if (clasp->trace &&
  1294         !globalWithoutCustomTrace &&
  1295         !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS))
  1297         if (!cx->shouldBeJSContext())
  1298             return nullptr;
  1299         JSRuntime *rt = cx->asJSContext()->runtime();
  1300         rt->gcIncrementalEnabled = false;
  1302 #ifdef DEBUG
  1303         if (rt->gcMode() == JSGC_MODE_INCREMENTAL) {
  1304             fprintf(stderr,
  1305                     "The class %s has a trace hook but does not declare the\n"
  1306                     "JSCLASS_IMPLEMENTS_BARRIERS flag. Please ensure that it correctly\n"
  1307                     "implements write barriers and then set the flag.\n",
  1308                     clasp->name);
  1309             MOZ_CRASH();
  1311 #endif
  1314     probes::CreateObject(cx, obj);
  1315     return obj;
  1318 void
  1319 NewObjectCache::fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto proto,
  1320                           gc::AllocKind kind, JSObject *obj)
  1322     JS_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
  1323     JS_ASSERT(obj->getTaggedProto() == proto);
  1324     return fill(entry, clasp, proto.raw(), kind, obj);
  1327 JSObject *
  1328 js::NewObjectWithGivenProto(ExclusiveContext *cxArg, const js::Class *clasp,
  1329                             js::TaggedProto protoArg, JSObject *parentArg,
  1330                             gc::AllocKind allocKind, NewObjectKind newKind)
  1332     if (CanBeFinalizedInBackground(allocKind, clasp))
  1333         allocKind = GetBackgroundAllocKind(allocKind);
  1335     NewObjectCache::EntryIndex entry = -1;
  1336     if (JSContext *cx = cxArg->maybeJSContext()) {
  1337         NewObjectCache &cache = cx->runtime()->newObjectCache;
  1338         if (protoArg.isObject() &&
  1339             newKind == GenericObject &&
  1340             !cx->compartment()->hasObjectMetadataCallback() &&
  1341             (!parentArg || parentArg == protoArg.toObject()->getParent()) &&
  1342             !protoArg.toObject()->is<GlobalObject>())
  1344             if (cache.lookupProto(clasp, protoArg.toObject(), allocKind, &entry)) {
  1345                 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
  1346                 if (obj) {
  1347                     return obj;
  1348                 } else {
  1349                     Rooted<TaggedProto> proto(cxArg, protoArg);
  1350                     RootedObject parent(cxArg, parentArg);
  1351                     obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
  1352                     JS_ASSERT(!obj);
  1353                     parentArg = parent;
  1354                     protoArg = proto;
  1360     Rooted<TaggedProto> proto(cxArg, protoArg);
  1361     RootedObject parent(cxArg, parentArg);
  1363     types::TypeObject *type = cxArg->getNewType(clasp, proto, nullptr);
  1364     if (!type)
  1365         return nullptr;
  1367     /*
  1368      * Default parent to the parent of the prototype, which was set from
  1369      * the parent of the prototype's constructor.
  1370      */
  1371     if (!parent && proto.isObject())
  1372         parent = proto.toObject()->getParent();
  1374     RootedObject obj(cxArg, NewObject(cxArg, type, parent, allocKind, newKind));
  1375     if (!obj)
  1376         return nullptr;
  1378     if (entry != -1 && !obj->hasDynamicSlots()) {
  1379         cxArg->asJSContext()->runtime()->newObjectCache.fillProto(entry, clasp,
  1380                                                                   proto, allocKind, obj);
  1383     return obj;
  1386 JSObject *
  1387 js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg,
  1388                                   const js::Class *clasp, JSObject *protoArg, JSObject *parentArg,
  1389                                   gc::AllocKind allocKind, NewObjectKind newKind)
  1391     if (protoArg)
  1392         return NewObjectWithGivenProto(cxArg, clasp, protoArg, parentArg, allocKind, newKind);
  1394     if (CanBeFinalizedInBackground(allocKind, clasp))
  1395         allocKind = GetBackgroundAllocKind(allocKind);
  1397     if (!parentArg)
  1398         parentArg = cxArg->global();
  1400     /*
  1401      * Use the object cache, except for classes without a cached proto key.
  1402      * On these objects, FindProto will do a dynamic property lookup to get
  1403      * global[className].prototype, where changes to either the className or
  1404      * prototype property would render the cached lookup incorrect. For classes
  1405      * with a proto key, the prototype created during class initialization is
  1406      * stored in an immutable slot on the global (except for ClearScope, which
  1407      * will flush the new object cache).
  1408      */
  1409     JSProtoKey protoKey = GetClassProtoKey(clasp);
  1411     NewObjectCache::EntryIndex entry = -1;
  1412     if (JSContext *cx = cxArg->maybeJSContext()) {
  1413         NewObjectCache &cache = cx->runtime()->newObjectCache;
  1414         if (parentArg->is<GlobalObject>() &&
  1415             protoKey != JSProto_Null &&
  1416             newKind == GenericObject &&
  1417             !cx->compartment()->hasObjectMetadataCallback())
  1419             if (cache.lookupGlobal(clasp, &parentArg->as<GlobalObject>(), allocKind, &entry)) {
  1420                 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
  1421                 if (obj) {
  1422                     return obj;
  1423                 } else {
  1424                     RootedObject parent(cxArg, parentArg);
  1425                     RootedObject proto(cxArg, protoArg);
  1426                     obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
  1427                     JS_ASSERT(!obj);
  1428                     protoArg = proto;
  1429                     parentArg = parent;
  1435     RootedObject parent(cxArg, parentArg);
  1436     RootedObject proto(cxArg, protoArg);
  1438     if (!FindProto(cxArg, clasp, &proto))
  1439         return nullptr;
  1441     types::TypeObject *type = cxArg->getNewType(clasp, proto.get());
  1442     if (!type)
  1443         return nullptr;
  1445     JSObject *obj = NewObject(cxArg, type, parent, allocKind, newKind);
  1446     if (!obj)
  1447         return nullptr;
  1449     if (entry != -1 && !obj->hasDynamicSlots()) {
  1450         cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, clasp,
  1451                                                                    &parent->as<GlobalObject>(),
  1452                                                                    allocKind, obj);
  1455     return obj;
  1458 /*
  1459  * Create a plain object with the specified type. This bypasses getNewType to
  1460  * avoid losing creation site information for objects made by scripted 'new'.
  1461  */
  1462 JSObject *
  1463 js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind,
  1464                       NewObjectKind newKind)
  1466     JS_ASSERT(parent);
  1468     JS_ASSERT(allocKind <= gc::FINALIZE_OBJECT_LAST);
  1469     if (CanBeFinalizedInBackground(allocKind, type->clasp()))
  1470         allocKind = GetBackgroundAllocKind(allocKind);
  1472     NewObjectCache &cache = cx->runtime()->newObjectCache;
  1474     NewObjectCache::EntryIndex entry = -1;
  1475     if (parent == type->proto().toObject()->getParent() &&
  1476         newKind == GenericObject &&
  1477         !cx->compartment()->hasObjectMetadataCallback())
  1479         if (cache.lookupType(type, allocKind, &entry)) {
  1480             JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, type->clasp()));
  1481             if (obj) {
  1482                 return obj;
  1483             } else {
  1484                 obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, type->clasp()));
  1485                 parent = type->proto().toObject()->getParent();
  1490     JSObject *obj = NewObject(cx, type, parent, allocKind, newKind);
  1491     if (!obj)
  1492         return nullptr;
  1494     if (entry != -1 && !obj->hasDynamicSlots())
  1495         cache.fillType(entry, type, allocKind, obj);
  1497     return obj;
  1500 bool
  1501 js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj)
  1503     jsbytecode *pc;
  1504     RootedScript script(cx, cx->currentScript(&pc));
  1505     gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
  1506     NewObjectKind newKind = script
  1507                             ? UseNewTypeForInitializer(script, pc, &JSObject::class_)
  1508                             : GenericObject;
  1509     RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind));
  1510     if (!obj)
  1511         return false;
  1513     if (script) {
  1514         /* Try to specialize the type of the object to the scripted call site. */
  1515         if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind))
  1516             return false;
  1519     pobj.set(obj);
  1520     return true;
  1523 JSObject*
  1524 js::CreateThis(JSContext *cx, const Class *newclasp, HandleObject callee)
  1526     RootedValue protov(cx);
  1527     if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
  1528         return nullptr;
  1530     JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : nullptr;
  1531     JSObject *parent = callee->getParent();
  1532     gc::AllocKind kind = NewObjectGCKind(newclasp);
  1533     return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
  1536 static inline JSObject *
  1537 CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
  1538                               NewObjectKind newKind)
  1540     if (type->hasNewScript()) {
  1541         /*
  1542          * Make an object with the type's associated finalize kind and shape,
  1543          * which reflects any properties that will definitely be added to the
  1544          * object before it is read from.
  1545          */
  1546         RootedObject templateObject(cx, type->newScript()->templateObject);
  1547         JS_ASSERT(templateObject->type() == type);
  1549         RootedObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
  1550         if (!res)
  1551             return nullptr;
  1552         if (newKind == SingletonObject) {
  1553             Rooted<TaggedProto> proto(cx, templateObject->getProto());
  1554             if (!res->splicePrototype(cx, &JSObject::class_, proto))
  1555                 return nullptr;
  1556         } else {
  1557             res->setType(type);
  1559         return res;
  1562     gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
  1563     return NewObjectWithType(cx, type, parent, allocKind, newKind);
  1566 JSObject *
  1567 js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto,
  1568                                    NewObjectKind newKind /* = GenericObject */)
  1570     RootedObject res(cx);
  1572     if (proto) {
  1573         RootedTypeObject type(cx, cx->getNewType(&JSObject::class_, proto, &callee->as<JSFunction>()));
  1574         if (!type)
  1575             return nullptr;
  1576         res = CreateThisForFunctionWithType(cx, type, callee->getParent(), newKind);
  1577     } else {
  1578         gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
  1579         res = NewObjectWithClassProto(cx, &JSObject::class_, proto, callee->getParent(), allocKind, newKind);
  1582     if (res) {
  1583         JSScript *script = callee->as<JSFunction>().getOrCreateScript(cx);
  1584         if (!script)
  1585             return nullptr;
  1586         TypeScript::SetThis(cx, script, types::Type::ObjectType(res));
  1589     return res;
  1592 JSObject *
  1593 js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newKind)
  1595     RootedValue protov(cx);
  1596     if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
  1597         return nullptr;
  1598     JSObject *proto;
  1599     if (protov.isObject())
  1600         proto = &protov.toObject();
  1601     else
  1602         proto = nullptr;
  1603     JSObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
  1605     if (obj && newKind == SingletonObject) {
  1606         RootedObject nobj(cx, obj);
  1608         /* Reshape the singleton before passing it as the 'this' value. */
  1609         JSObject::clear(cx, nobj);
  1611         JSScript *calleeScript = callee->as<JSFunction>().nonLazyScript();
  1612         TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
  1614         return nobj;
  1617     return obj;
  1620 /*
  1621  * Given pc pointing after a property accessing bytecode, return true if the
  1622  * access is "object-detecting" in the sense used by web scripts, e.g., when
  1623  * checking whether document.all is defined.
  1624  */
  1625 static bool
  1626 Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
  1628     JS_ASSERT(script->containsPC(pc));
  1630     /* General case: a branch or equality op follows the access. */
  1631     JSOp op = JSOp(*pc);
  1632     if (js_CodeSpec[op].format & JOF_DETECTING)
  1633         return true;
  1635     jsbytecode *endpc = script->codeEnd();
  1637     if (op == JSOP_NULL) {
  1638         /*
  1639          * Special case #1: handle (document.all == null).  Don't sweat
  1640          * about JS1.2's revision of the equality operators here.
  1641          */
  1642         if (++pc < endpc) {
  1643             op = JSOp(*pc);
  1644             return op == JSOP_EQ || op == JSOP_NE;
  1646         return false;
  1649     if (op == JSOP_GETGNAME || op == JSOP_NAME) {
  1650         /*
  1651          * Special case #2: handle (document.all == undefined).  Don't worry
  1652          * about a local variable named |undefined| shadowing the immutable
  1653          * global binding...because, really?
  1654          */
  1655         JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
  1656         if (atom == cx->names().undefined &&
  1657             (pc += js_CodeSpec[op].length) < endpc) {
  1658             op = JSOp(*pc);
  1659             return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
  1663     return false;
  1666 /* static */ bool
  1667 JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj,
  1668                                HandleId id, MutableHandleValue vp, bool strict)
  1670     if (MOZ_UNLIKELY(obj->watched())) {
  1671         WatchpointMap *wpmap = cx->compartment()->watchpointMap;
  1672         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
  1673             return false;
  1675     return obj->getOps()->setGeneric(cx, obj, id, vp, strict);
  1678 /* static */ bool
  1679 JSObject::nonNativeSetElement(JSContext *cx, HandleObject obj,
  1680                               uint32_t index, MutableHandleValue vp, bool strict)
  1682     if (MOZ_UNLIKELY(obj->watched())) {
  1683         RootedId id(cx);
  1684         if (!IndexToId(cx, index, &id))
  1685             return false;
  1687         WatchpointMap *wpmap = cx->compartment()->watchpointMap;
  1688         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
  1689             return false;
  1691     return obj->getOps()->setElement(cx, obj, index, vp, strict);
  1694 /* static */ bool
  1695 JSObject::deleteByValue(JSContext *cx, HandleObject obj, const Value &property, bool *succeeded)
  1697     uint32_t index;
  1698     if (IsDefinitelyIndex(property, &index))
  1699         return deleteElement(cx, obj, index, succeeded);
  1701     RootedValue propval(cx, property);
  1703     JSAtom *name = ToAtom<CanGC>(cx, propval);
  1704     if (!name)
  1705         return false;
  1707     if (name->isIndex(&index))
  1708         return deleteElement(cx, obj, index, succeeded);
  1710     Rooted<PropertyName*> propname(cx, name->asPropertyName());
  1711     return deleteProperty(cx, obj, propname, succeeded);
  1714 JS_FRIEND_API(bool)
  1715 JS_CopyPropertyFrom(JSContext *cx, HandleId id, HandleObject target,
  1716                     HandleObject obj)
  1718     // |obj| and |cx| are generally not same-compartment with |target| here.
  1719     assertSameCompartment(cx, obj, id);
  1720     Rooted<JSPropertyDescriptor> desc(cx);
  1722     if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
  1723         return false;
  1724     MOZ_ASSERT(desc.object());
  1726     // Silently skip JSPropertyOp-implemented accessors.
  1727     if (desc.getter() && !desc.hasGetterObject())
  1728         return true;
  1729     if (desc.setter() && !desc.hasSetterObject())
  1730         return true;
  1732     JSAutoCompartment ac(cx, target);
  1733     RootedId wrappedId(cx, id);
  1734     if (!cx->compartment()->wrap(cx, &desc))
  1735         return false;
  1736     if (!cx->compartment()->wrapId(cx, wrappedId.address()))
  1737         return false;
  1739     bool ignored;
  1740     return DefineOwnProperty(cx, target, wrappedId, desc, &ignored);
  1743 JS_FRIEND_API(bool)
  1744 JS_CopyPropertiesFrom(JSContext *cx, HandleObject target, HandleObject obj)
  1746     JSAutoCompartment ac(cx, obj);
  1748     AutoIdVector props(cx);
  1749     if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &props))
  1750         return false;
  1752     for (size_t i = 0; i < props.length(); ++i) {
  1753         if (!JS_CopyPropertyFrom(cx, props.handleAt(i), target, obj))
  1754             return false;
  1757     return true;
  1760 static bool
  1761 CopySlots(JSContext *cx, HandleObject from, HandleObject to)
  1763     JS_ASSERT(!from->isNative() && !to->isNative());
  1764     JS_ASSERT(from->getClass() == to->getClass());
  1766     size_t n = 0;
  1767     if (from->is<WrapperObject>() &&
  1768         (Wrapper::wrapperHandler(from)->flags() &
  1769          Wrapper::CROSS_COMPARTMENT)) {
  1770         to->setSlot(0, from->getSlot(0));
  1771         to->setSlot(1, from->getSlot(1));
  1772         n = 2;
  1775     size_t span = JSCLASS_RESERVED_SLOTS(from->getClass());
  1776     RootedValue v(cx);
  1777     for (; n < span; ++n) {
  1778         v = from->getSlot(n);
  1779         if (!cx->compartment()->wrap(cx, &v))
  1780             return false;
  1781         to->setSlot(n, v);
  1783     return true;
  1786 JSObject *
  1787 js::CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent)
  1789     if (!obj->isNative() && !obj->is<ProxyObject>()) {
  1790         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1791                              JSMSG_CANT_CLONE_OBJECT);
  1792         return nullptr;
  1795     RootedObject clone(cx, NewObjectWithGivenProto(cx, obj->getClass(), proto, parent));
  1796     if (!clone)
  1797         return nullptr;
  1798     if (obj->isNative()) {
  1799         if (clone->is<JSFunction>() && (obj->compartment() != clone->compartment())) {
  1800             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1801                                  JSMSG_CANT_CLONE_OBJECT);
  1802             return nullptr;
  1805         if (obj->hasPrivate())
  1806             clone->setPrivate(obj->getPrivate());
  1807     } else {
  1808         JS_ASSERT(obj->is<ProxyObject>());
  1809         if (!CopySlots(cx, obj, clone))
  1810             return nullptr;
  1813     return clone;
  1816 JSObject *
  1817 js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind)
  1819     /* NB: Keep this in sync with XDRObjectLiteral. */
  1820     JS_ASSERT(JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
  1821     JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
  1823     // Result of the clone function.
  1824     RootedObject clone(cx);
  1826     // Temporary element/slot which would be stored in the cloned object.
  1827     RootedValue v(cx);
  1828     RootedObject deepObj(cx);
  1830     if (obj->getClass() == &ArrayObject::class_) {
  1831         clone = NewDenseUnallocatedArray(cx, obj->as<ArrayObject>().length(), nullptr, newKind);
  1832     } else {
  1833         // Object literals are tenured by default as holded by the JSScript.
  1834         JS_ASSERT(obj->isTenured());
  1835         AllocKind kind = obj->tenuredGetAllocKind();
  1836         Rooted<TypeObject*> typeObj(cx, obj->getType(cx));
  1837         if (!typeObj)
  1838             return nullptr;
  1839         RootedObject parent(cx, obj->getParent());
  1840         clone = NewObjectWithGivenProto(cx, &JSObject::class_, typeObj->proto().toObject(),
  1841                                         parent, kind, newKind);
  1844     // Allocate the same number of slots.
  1845     if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity()))
  1846         return nullptr;
  1848     // Copy the number of initialized elements.
  1849     uint32_t initialized = obj->getDenseInitializedLength();
  1850     if (initialized)
  1851         clone->setDenseInitializedLength(initialized);
  1853     // Recursive copy of dense element.
  1854     for (uint32_t i = 0; i < initialized; ++i) {
  1855         v = obj->getDenseElement(i);
  1856         if (v.isObject()) {
  1857             deepObj = &v.toObject();
  1858             deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
  1859             if (!deepObj) {
  1860                 JS_ReportOutOfMemory(cx);
  1861                 return nullptr;
  1863             v.setObject(*deepObj);
  1865         clone->initDenseElement(i, v);
  1868     JS_ASSERT(obj->compartment() == clone->compartment());
  1869     JS_ASSERT(!obj->hasPrivate());
  1870     RootedShape shape(cx, obj->lastProperty());
  1871     size_t span = shape->slotSpan();
  1872     clone->setLastProperty(cx, clone, shape);
  1873     for (size_t i = 0; i < span; i++) {
  1874         v = obj->getSlot(i);
  1875         if (v.isObject()) {
  1876             deepObj = &v.toObject();
  1877             deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
  1878             if (!deepObj)
  1879                 return nullptr;
  1880             v.setObject(*deepObj);
  1882         clone->setSlot(i, v);
  1885     if (obj->getClass() == &ArrayObject::class_)
  1886         FixArrayType(cx, clone);
  1887     else
  1888         FixObjectType(cx, clone);
  1890 #ifdef DEBUG
  1891     Rooted<TypeObject*> typeObj(cx, obj->getType(cx));
  1892     Rooted<TypeObject*> cloneTypeObj(cx, clone->getType(cx));
  1893     if (!typeObj || !cloneTypeObj)
  1894         return nullptr;
  1895     JS_ASSERT(typeObj == cloneTypeObj);
  1896 #endif
  1898     return clone;
  1901 template<XDRMode mode>
  1902 bool
  1903 js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
  1905     /* NB: Keep this in sync with DeepCloneObjectLiteral. */
  1907     JSContext *cx = xdr->cx();
  1908     JS_ASSERT_IF(mode == XDR_ENCODE, JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
  1910     // Distinguish between objects and array classes.
  1911     uint32_t isArray = 0;
  1913         if (mode == XDR_ENCODE) {
  1914             JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
  1915             isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0;
  1918         if (!xdr->codeUint32(&isArray))
  1919             return false;
  1922     if (isArray) {
  1923         uint32_t length;
  1925         if (mode == XDR_ENCODE)
  1926             length = obj->as<ArrayObject>().length();
  1928         if (!xdr->codeUint32(&length))
  1929             return false;
  1931         if (mode == XDR_DECODE)
  1932             obj.set(NewDenseUnallocatedArray(cx, length, NULL, js::MaybeSingletonObject));
  1934     } else {
  1935         // Code the alloc kind of the object.
  1936         AllocKind kind;
  1938             if (mode == XDR_ENCODE) {
  1939                 JS_ASSERT(obj->getClass() == &JSObject::class_);
  1940                 JS_ASSERT(obj->isTenured());
  1941                 kind = obj->tenuredGetAllocKind();
  1944             if (!xdr->codeEnum32(&kind))
  1945                 return false;
  1947             if (mode == XDR_DECODE)
  1948                 obj.set(NewBuiltinClassInstance(cx, &JSObject::class_, kind, js::MaybeSingletonObject));
  1953         uint32_t capacity;
  1954         if (mode == XDR_ENCODE)
  1955             capacity = obj->getDenseCapacity();
  1956         if (!xdr->codeUint32(&capacity))
  1957             return false;
  1958         if (mode == XDR_DECODE) {
  1959             if (!obj->ensureElements(cx, capacity)) {
  1960                 JS_ReportOutOfMemory(cx);
  1961                 return false;
  1966     uint32_t initialized;
  1968         if (mode == XDR_ENCODE)
  1969             initialized = obj->getDenseInitializedLength();
  1970         if (!xdr->codeUint32(&initialized))
  1971             return false;
  1972         if (mode == XDR_DECODE) {
  1973             if (initialized)
  1974                 obj->setDenseInitializedLength(initialized);
  1978     RootedValue tmpValue(cx);
  1980     // Recursively copy dense elements.
  1982         for (unsigned i = 0; i < initialized; i++) {
  1983             if (mode == XDR_ENCODE)
  1984                 tmpValue = obj->getDenseElement(i);
  1986             if (!xdr->codeConstValue(&tmpValue))
  1987                 return false;
  1989             if (mode == XDR_DECODE)
  1990                 obj->initDenseElement(i, tmpValue);
  1994     JS_ASSERT(!obj->hasPrivate());
  1995     RootedShape shape(cx, obj->lastProperty());
  1997     // Code the number of slots in the vector.
  1998     unsigned nslot = 0;
  2000     // Code ids of the object in order. As opposed to DeepCloneObjectLiteral we
  2001     // cannot just re-use the shape of the original bytecode value and we have
  2002     // to write down the shape as well as the corresponding values.  Ideally we
  2003     // would have a mechanism to serialize the shape too.
  2004     js::AutoIdVector ids(cx);
  2006         if (mode == XDR_ENCODE && !shape->isEmptyShape()) {
  2007             nslot = shape->slotSpan();
  2008             if (!ids.reserve(nslot))
  2009                 return false;
  2011             for (unsigned i = 0; i < nslot; i++)
  2012                 ids.infallibleAppend(JSID_VOID);
  2014             for (Shape::Range<NoGC> it(shape); !it.empty(); it.popFront()) {
  2015                 // If we have reached the native property of the array class, we
  2016                 // exit as the remaining would only be reserved slots.
  2017                 if (!it.front().hasSlot()) {
  2018                     JS_ASSERT(isArray);
  2019                     break;
  2022                 JS_ASSERT(it.front().hasDefaultGetter());
  2023                 ids[it.front().slot()] = it.front().propid();
  2027         if (!xdr->codeUint32(&nslot))
  2028             return false;
  2030         RootedAtom atom(cx);
  2031         RootedId id(cx);
  2032         uint32_t idType = 0;
  2033         for (unsigned i = 0; i < nslot; i++) {
  2034             if (mode == XDR_ENCODE) {
  2035                 id = ids[i];
  2036                 if (JSID_IS_INT(id))
  2037                     idType = JSID_TYPE_INT;
  2038                 else if (JSID_IS_ATOM(id))
  2039                     idType = JSID_TYPE_STRING;
  2040                 else
  2041                     MOZ_ASSUME_UNREACHABLE("Object property is not yet supported by XDR.");
  2043                 tmpValue = obj->getSlot(i);
  2046             if (!xdr->codeUint32(&idType))
  2047                 return false;
  2049             if (idType == JSID_TYPE_STRING) {
  2050                 if (mode == XDR_ENCODE)
  2051                     atom = JSID_TO_ATOM(id);
  2052                 if (!XDRAtom(xdr, &atom))
  2053                     return false;
  2054                 if (mode == XDR_DECODE)
  2055                     id = AtomToId(atom);
  2056             } else {
  2057                 JS_ASSERT(idType == JSID_TYPE_INT);
  2058                 uint32_t indexVal;
  2059                 if (mode == XDR_ENCODE)
  2060                     indexVal = uint32_t(JSID_TO_INT(id));
  2061                 if (!xdr->codeUint32(&indexVal))
  2062                     return false;
  2063                 if (mode == XDR_DECODE)
  2064                     id = INT_TO_JSID(int32_t(indexVal));
  2067             if (!xdr->codeConstValue(&tmpValue))
  2068                 return false;
  2070             if (mode == XDR_DECODE) {
  2071                 if (!DefineNativeProperty(cx, obj, id, tmpValue, NULL, NULL, JSPROP_ENUMERATE))
  2072                     return false;
  2076         JS_ASSERT_IF(mode == XDR_DECODE, !obj->inDictionaryMode());
  2079     if (mode == XDR_DECODE) {
  2080         if (isArray)
  2081             FixArrayType(cx, obj);
  2082         else
  2083             FixObjectType(cx, obj);
  2086     return true;
  2089 template bool
  2090 js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleObject obj);
  2092 template bool
  2093 js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj);
  2095 JSObject *
  2096 js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
  2098     Rooted<TypeObject*> typeObj(cx);
  2099     typeObj = cx->getNewType(&JSObject::class_, cx->global()->getOrCreateObjectPrototype(cx));
  2101     JS_ASSERT(srcObj->getClass() == &JSObject::class_);
  2102     AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->numFixedSlots()));
  2103     JS_ASSERT_IF(srcObj->isTenured(), kind == srcObj->tenuredGetAllocKind());
  2105     RootedShape shape(cx, srcObj->lastProperty());
  2106     return NewReshapedObject(cx, typeObj, parent, kind, shape);
  2109 struct JSObject::TradeGutsReserved {
  2110     Vector<Value> avals;
  2111     Vector<Value> bvals;
  2112     int newafixed;
  2113     int newbfixed;
  2114     RootedShape newashape;
  2115     RootedShape newbshape;
  2116     HeapSlot *newaslots;
  2117     HeapSlot *newbslots;
  2119     TradeGutsReserved(JSContext *cx)
  2120         : avals(cx), bvals(cx),
  2121           newafixed(0), newbfixed(0),
  2122           newashape(cx), newbshape(cx),
  2123           newaslots(nullptr), newbslots(nullptr)
  2124     {}
  2126     ~TradeGutsReserved()
  2128         js_free(newaslots);
  2129         js_free(newbslots);
  2131 };
  2133 bool
  2134 JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *aArg, JSObject *bArg,
  2135                               TradeGutsReserved &reserved)
  2137     /*
  2138      * Avoid GC in here to avoid confusing the tracing code with our
  2139      * intermediate state.
  2140      */
  2141     AutoSuppressGC suppress(cx);
  2143     RootedObject a(cx, aArg);
  2144     RootedObject b(cx, bArg);
  2145     JS_ASSERT(a->compartment() == b->compartment());
  2146     AutoCompartment ac(cx, a);
  2148     /*
  2149      * When performing multiple swaps between objects which may have different
  2150      * numbers of fixed slots, we reserve all space ahead of time so that the
  2151      * swaps can be performed infallibly.
  2152      */
  2154     /*
  2155      * Swap prototypes and classes on the two objects, so that TradeGuts can
  2156      * preserve the types of the two objects.
  2157      */
  2158     const Class *aClass = a->getClass();
  2159     const Class *bClass = b->getClass();
  2160     Rooted<TaggedProto> aProto(cx, a->getTaggedProto());
  2161     Rooted<TaggedProto> bProto(cx, b->getTaggedProto());
  2162     bool success;
  2163     if (!SetClassAndProto(cx, a, bClass, bProto, &success) || !success)
  2164         return false;
  2165     if (!SetClassAndProto(cx, b, aClass, aProto, &success) || !success)
  2166         return false;
  2168     if (a->tenuredSizeOfThis() == b->tenuredSizeOfThis())
  2169         return true;
  2171     /*
  2172      * If either object is native, it needs a new shape to preserve the
  2173      * invariant that objects with the same shape have the same number of
  2174      * inline slots. The fixed slots will be updated in place during TradeGuts.
  2175      * Non-native objects need to be reshaped according to the new count.
  2176      */
  2177     if (a->isNative()) {
  2178         if (!a->generateOwnShape(cx))
  2179             return false;
  2180     } else {
  2181         reserved.newbshape = EmptyShape::getInitialShape(cx, aClass, aProto, a->getParent(), a->getMetadata(),
  2182                                                          b->tenuredGetAllocKind());
  2183         if (!reserved.newbshape)
  2184             return false;
  2186     if (b->isNative()) {
  2187         if (!b->generateOwnShape(cx))
  2188             return false;
  2189     } else {
  2190         reserved.newashape = EmptyShape::getInitialShape(cx, bClass, bProto, b->getParent(), b->getMetadata(),
  2191                                                          a->tenuredGetAllocKind());
  2192         if (!reserved.newashape)
  2193             return false;
  2196     /* The avals/bvals vectors hold all original values from the objects. */
  2198     if (!reserved.avals.reserve(a->slotSpan()))
  2199         return false;
  2200     if (!reserved.bvals.reserve(b->slotSpan()))
  2201         return false;
  2203     /*
  2204      * The newafixed/newbfixed hold the number of fixed slots in the objects
  2205      * after the swap. Adjust these counts according to whether the objects
  2206      * use their last fixed slot for storing private data.
  2207      */
  2209     reserved.newafixed = a->numFixedSlots();
  2210     reserved.newbfixed = b->numFixedSlots();
  2212     if (aClass->hasPrivate()) {
  2213         reserved.newafixed++;
  2214         reserved.newbfixed--;
  2216     if (bClass->hasPrivate()) {
  2217         reserved.newbfixed++;
  2218         reserved.newafixed--;
  2221     JS_ASSERT(reserved.newafixed >= 0);
  2222     JS_ASSERT(reserved.newbfixed >= 0);
  2224     /*
  2225      * The newaslots/newbslots arrays hold any dynamic slots for the objects
  2226      * if they do not have enough fixed slots to accomodate the slots in the
  2227      * other object.
  2228      */
  2230     unsigned adynamic = dynamicSlotsCount(reserved.newafixed, b->slotSpan(), b->getClass());
  2231     unsigned bdynamic = dynamicSlotsCount(reserved.newbfixed, a->slotSpan(), a->getClass());
  2233     if (adynamic) {
  2234         reserved.newaslots = cx->pod_malloc<HeapSlot>(adynamic);
  2235         if (!reserved.newaslots)
  2236             return false;
  2237         Debug_SetSlotRangeToCrashOnTouch(reserved.newaslots, adynamic);
  2239     if (bdynamic) {
  2240         reserved.newbslots = cx->pod_malloc<HeapSlot>(bdynamic);
  2241         if (!reserved.newbslots)
  2242             return false;
  2243         Debug_SetSlotRangeToCrashOnTouch(reserved.newbslots, bdynamic);
  2246     return true;
  2249 void
  2250 JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved)
  2252     JS_ASSERT(a->compartment() == b->compartment());
  2253     JS_ASSERT(a->is<JSFunction>() == b->is<JSFunction>());
  2255     /*
  2256      * Swap the object's types, to restore their initial type information.
  2257      * The prototypes and classes of the objects were swapped in ReserveForTradeGuts.
  2258      */
  2259     TypeObject *tmp = a->type_;
  2260     a->type_ = b->type_;
  2261     b->type_ = tmp;
  2263     /* Don't try to swap a JSFunction for a plain function JSObject. */
  2264     JS_ASSERT_IF(a->is<JSFunction>(), a->tenuredSizeOfThis() == b->tenuredSizeOfThis());
  2266     /*
  2267      * Regexp guts are more complicated -- we would need to migrate the
  2268      * refcounted JIT code blob for them across compartments instead of just
  2269      * swapping guts.
  2270      */
  2271     JS_ASSERT(!a->is<RegExpObject>() && !b->is<RegExpObject>());
  2273     /* Arrays can use their fixed storage for elements. */
  2274     JS_ASSERT(!a->is<ArrayObject>() && !b->is<ArrayObject>());
  2276     /*
  2277      * Callers should not try to swap ArrayBuffer objects,
  2278      * these use a different slot representation from other objects.
  2279      */
  2280     JS_ASSERT(!a->is<ArrayBufferObject>() && !b->is<ArrayBufferObject>());
  2282     /* Trade the guts of the objects. */
  2283     const size_t size = a->tenuredSizeOfThis();
  2284     if (size == b->tenuredSizeOfThis()) {
  2285         /*
  2286          * If the objects are the same size, then we make no assumptions about
  2287          * whether they have dynamically allocated slots and instead just copy
  2288          * them over wholesale.
  2289          */
  2290         char tmp[mozilla::tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::value];
  2291         JS_ASSERT(size <= sizeof(tmp));
  2293         js_memcpy(tmp, a, size);
  2294         js_memcpy(a, b, size);
  2295         js_memcpy(b, tmp, size);
  2297 #ifdef JSGC_GENERATIONAL
  2298         /*
  2299          * Trigger post barriers for fixed slots. JSObject bits are barriered
  2300          * below, in common with the other case.
  2301          */
  2302         for (size_t i = 0; i < a->numFixedSlots(); ++i) {
  2303             HeapSlot::writeBarrierPost(cx->runtime(), a, HeapSlot::Slot, i, a->getSlot(i));
  2304             HeapSlot::writeBarrierPost(cx->runtime(), b, HeapSlot::Slot, i, b->getSlot(i));
  2306 #endif
  2307     } else {
  2308         /*
  2309          * If the objects are of differing sizes, use the space we reserved
  2310          * earlier to save the slots from each object and then copy them into
  2311          * the new layout for the other object.
  2312          */
  2314         uint32_t acap = a->slotSpan();
  2315         uint32_t bcap = b->slotSpan();
  2317         for (size_t i = 0; i < acap; i++)
  2318             reserved.avals.infallibleAppend(a->getSlot(i));
  2320         for (size_t i = 0; i < bcap; i++)
  2321             reserved.bvals.infallibleAppend(b->getSlot(i));
  2323         /* Done with the dynamic slots. */
  2324         if (a->hasDynamicSlots())
  2325             js_free(a->slots);
  2326         if (b->hasDynamicSlots())
  2327             js_free(b->slots);
  2329         void *apriv = a->hasPrivate() ? a->getPrivate() : nullptr;
  2330         void *bpriv = b->hasPrivate() ? b->getPrivate() : nullptr;
  2332         char tmp[sizeof(JSObject)];
  2333         js_memcpy(&tmp, a, sizeof tmp);
  2334         js_memcpy(a, b, sizeof tmp);
  2335         js_memcpy(b, &tmp, sizeof tmp);
  2337         if (a->isNative())
  2338             a->shape_->setNumFixedSlots(reserved.newafixed);
  2339         else
  2340             a->shape_ = reserved.newashape;
  2342         a->slots = reserved.newaslots;
  2343         a->initSlotRange(0, reserved.bvals.begin(), bcap);
  2344         if (a->hasPrivate())
  2345             a->initPrivate(bpriv);
  2347         if (b->isNative())
  2348             b->shape_->setNumFixedSlots(reserved.newbfixed);
  2349         else
  2350             b->shape_ = reserved.newbshape;
  2352         b->slots = reserved.newbslots;
  2353         b->initSlotRange(0, reserved.avals.begin(), acap);
  2354         if (b->hasPrivate())
  2355             b->initPrivate(apriv);
  2357         /* Make sure the destructor for reserved doesn't free the slots. */
  2358         reserved.newaslots = nullptr;
  2359         reserved.newbslots = nullptr;
  2362 #ifdef JSGC_GENERATIONAL
  2363     Shape::writeBarrierPost(a->shape_, &a->shape_);
  2364     Shape::writeBarrierPost(b->shape_, &b->shape_);
  2365     types::TypeObject::writeBarrierPost(a->type_, &a->type_);
  2366     types::TypeObject::writeBarrierPost(b->type_, &b->type_);
  2367 #endif
  2369     if (a->inDictionaryMode())
  2370         a->lastProperty()->listp = &a->shape_;
  2371     if (b->inDictionaryMode())
  2372         b->lastProperty()->listp = &b->shape_;
  2374 #ifdef JSGC_INCREMENTAL
  2375     /*
  2376      * We need a write barrier here. If |a| was marked and |b| was not, then
  2377      * after the swap, |b|'s guts would never be marked. The write barrier
  2378      * solves this.
  2380      * Normally write barriers happen before the write. However, that's not
  2381      * necessary here because nothing is being destroyed. We're just swapping.
  2382      * We don't do the barrier before TradeGuts because ReserveForTradeGuts
  2383      * makes changes to the objects that might confuse the tracing code.
  2384      */
  2385     JS::Zone *zone = a->zone();
  2386     if (zone->needsBarrier()) {
  2387         MarkChildren(zone->barrierTracer(), a);
  2388         MarkChildren(zone->barrierTracer(), b);
  2390 #endif
  2393 /* Use this method with extreme caution. It trades the guts of two objects. */
  2394 bool
  2395 JSObject::swap(JSContext *cx, HandleObject a, HandleObject b)
  2397     AutoMarkInDeadZone adc1(a->zone());
  2398     AutoMarkInDeadZone adc2(b->zone());
  2400     // Ensure swap doesn't cause a finalizer to not be run.
  2401     JS_ASSERT(IsBackgroundFinalized(a->tenuredGetAllocKind()) ==
  2402               IsBackgroundFinalized(b->tenuredGetAllocKind()));
  2403     JS_ASSERT(a->compartment() == b->compartment());
  2405     unsigned r = NotifyGCPreSwap(a, b);
  2407     TradeGutsReserved reserved(cx);
  2408     if (!ReserveForTradeGuts(cx, a, b, reserved)) {
  2409         NotifyGCPostSwap(b, a, r);
  2410         return false;
  2412     TradeGuts(cx, a, b, reserved);
  2414     NotifyGCPostSwap(a, b, r);
  2415     return true;
  2418 static bool
  2419 DefineStandardSlot(JSContext *cx, HandleObject obj, JSProtoKey key, JSAtom *atom,
  2420                    HandleValue v, uint32_t attrs, bool &named)
  2422     RootedId id(cx, AtomToId(atom));
  2424     if (key != JSProto_Null) {
  2425         /*
  2426          * Initializing an actual standard class on a global object. If the
  2427          * property is not yet present, force it into a new one bound to a
  2428          * reserved slot. Otherwise, go through the normal property path.
  2429          */
  2430         JS_ASSERT(obj->is<GlobalObject>());
  2431         JS_ASSERT(obj->isNative());
  2433         if (!obj->nativeLookup(cx, id)) {
  2434             obj->as<GlobalObject>().setConstructorPropertySlot(key, v);
  2436             uint32_t slot = GlobalObject::constructorPropertySlot(key);
  2437             if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0))
  2438                 return false;
  2440             named = true;
  2441             return true;
  2445     named = JSObject::defineGeneric(cx, obj, id,
  2446                                     v, JS_PropertyStub, JS_StrictPropertyStub, attrs);
  2447     return named;
  2450 static void
  2451 SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
  2453     JS_ASSERT(!obj->getParent());
  2454     if (!obj->is<GlobalObject>())
  2455         return;
  2457     obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
  2458     obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
  2461 static void
  2462 ClearClassObject(JSObject *obj, JSProtoKey key)
  2464     JS_ASSERT(!obj->getParent());
  2465     if (!obj->is<GlobalObject>())
  2466         return;
  2468     obj->as<GlobalObject>().setConstructor(key, UndefinedValue());
  2469     obj->as<GlobalObject>().setPrototype(key, UndefinedValue());
  2472 static JSObject *
  2473 DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
  2474                               JSObject *protoProto, const Class *clasp,
  2475                               Native constructor, unsigned nargs,
  2476                               const JSPropertySpec *ps, const JSFunctionSpec *fs,
  2477                               const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
  2478                               JSObject **ctorp, AllocKind ctorKind)
  2480     /*
  2481      * Create a prototype object for this class.
  2483      * FIXME: lazy standard (built-in) class initialization and even older
  2484      * eager boostrapping code rely on all of these properties:
  2486      * 1. NewObject attempting to compute a default prototype object when
  2487      *    passed null for proto; and
  2489      * 2. NewObject tolerating no default prototype (null proto slot value)
  2490      *    due to this js_InitClass call coming from js_InitFunctionClass on an
  2491      *    otherwise-uninitialized global.
  2493      * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
  2494      *    &JSFunction::class_, not a JSObject-sized (smaller) GC-thing.
  2496      * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
  2497      * be &JSFunction::class_ (we could break compatibility easily). But
  2498      * fixing (3) is not enough without addressing the bootstrapping dependency
  2499      * on (1) and (2).
  2500      */
  2502     /*
  2503      * Create the prototype object.  (GlobalObject::createBlankPrototype isn't
  2504      * used because it parents the prototype object to the global and because
  2505      * it uses WithProto::Given.  FIXME: Undo dependencies on this parentage
  2506      * [which already needs to happen for bug 638316], figure out nicer
  2507      * semantics for null-protoProto, and use createBlankPrototype.)
  2508      */
  2509     RootedObject proto(cx, NewObjectWithClassProto(cx, clasp, protoProto, obj, SingletonObject));
  2510     if (!proto)
  2511         return nullptr;
  2513     /* After this point, control must exit via label bad or out. */
  2514     RootedObject ctor(cx);
  2515     bool named = false;
  2516     bool cached = false;
  2517     if (!constructor) {
  2518         /*
  2519          * Lacking a constructor, name the prototype (e.g., Math) unless this
  2520          * class (a) is anonymous, i.e. for internal use only; (b) the class
  2521          * of obj (the global object) is has a reserved slot indexed by key;
  2522          * and (c) key is not the null key.
  2523          */
  2524         if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->is<GlobalObject>() ||
  2525             key == JSProto_Null)
  2527             uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
  2528                            ? JSPROP_READONLY | JSPROP_PERMANENT
  2529                            : 0;
  2530             RootedValue value(cx, ObjectValue(*proto));
  2531             if (!DefineStandardSlot(cx, obj, key, atom, value, attrs, named))
  2532                 goto bad;
  2535         ctor = proto;
  2536     } else {
  2537         /*
  2538          * Create the constructor, not using GlobalObject::createConstructor
  2539          * because the constructor currently must have |obj| as its parent.
  2540          * (FIXME: remove this dependency on the exact identity of the parent,
  2541          * perhaps as part of bug 638316.)
  2542          */
  2543         RootedFunction fun(cx, NewFunction(cx, js::NullPtr(), constructor, nargs,
  2544                                            JSFunction::NATIVE_CTOR, obj, atom, ctorKind));
  2545         if (!fun)
  2546             goto bad;
  2548         /*
  2549          * Set the class object early for standard class constructors. Type
  2550          * inference may need to access these, and js::GetBuiltinPrototype will
  2551          * fail if it tries to do a reentrant reconstruction of the class.
  2552          */
  2553         if (key != JSProto_Null) {
  2554             SetClassObject(obj, key, fun, proto);
  2555             cached = true;
  2558         RootedValue value(cx, ObjectValue(*fun));
  2559         if (!DefineStandardSlot(cx, obj, key, atom, value, 0, named))
  2560             goto bad;
  2562         /*
  2563          * Optionally construct the prototype object, before the class has
  2564          * been fully initialized.  Allow the ctor to replace proto with a
  2565          * different object, as is done for operator new.
  2566          */
  2567         ctor = fun;
  2568         if (!LinkConstructorAndPrototype(cx, ctor, proto))
  2569             goto bad;
  2571         /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
  2572         Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
  2573         if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, clasp, tagged))
  2574             goto bad;
  2577     if (!DefinePropertiesAndBrand(cx, proto, ps, fs) ||
  2578         (ctor != proto && !DefinePropertiesAndBrand(cx, ctor, static_ps, static_fs)))
  2580         goto bad;
  2583     /* If this is a standard class, cache its prototype. */
  2584     if (!cached && key != JSProto_Null)
  2585         SetClassObject(obj, key, ctor, proto);
  2587     if (ctorp)
  2588         *ctorp = ctor;
  2589     return proto;
  2591 bad:
  2592     if (named) {
  2593         bool succeeded;
  2594         JSObject::deleteByValue(cx, obj, StringValue(atom), &succeeded);
  2596     if (cached)
  2597         ClearClassObject(obj, key);
  2598     return nullptr;
  2601 JSObject *
  2602 js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto_,
  2603              const Class *clasp, Native constructor, unsigned nargs,
  2604              const JSPropertySpec *ps, const JSFunctionSpec *fs,
  2605              const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
  2606              JSObject **ctorp, AllocKind ctorKind)
  2608     RootedObject protoProto(cx, protoProto_);
  2610     /* Assert mandatory function pointer members. */
  2611     JS_ASSERT(clasp->addProperty);
  2612     JS_ASSERT(clasp->delProperty);
  2613     JS_ASSERT(clasp->getProperty);
  2614     JS_ASSERT(clasp->setProperty);
  2615     JS_ASSERT(clasp->enumerate);
  2616     JS_ASSERT(clasp->resolve);
  2617     JS_ASSERT(clasp->convert);
  2619     RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
  2620     if (!atom)
  2621         return nullptr;
  2623     /*
  2624      * All instances of the class will inherit properties from the prototype
  2625      * object we are about to create (in DefineConstructorAndPrototype), which
  2626      * in turn will inherit from protoProto.
  2628      * When initializing a standard class (other than Object), if protoProto is
  2629      * null, default to Object.prototype. The engine's internal uses of
  2630      * js_InitClass depend on this nicety.
  2631      */
  2632     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
  2633     if (key != JSProto_Null &&
  2634         !protoProto &&
  2635         !GetBuiltinPrototype(cx, JSProto_Object, &protoProto))
  2637         return nullptr;
  2640     return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
  2641                                          ps, fs, static_ps, static_fs, ctorp, ctorKind);
  2644 /* static */ inline bool
  2645 JSObject::updateSlotsForSpan(ThreadSafeContext *cx,
  2646                              HandleObject obj, size_t oldSpan, size_t newSpan)
  2648     JS_ASSERT(cx->isThreadLocal(obj));
  2649     JS_ASSERT(oldSpan != newSpan);
  2651     size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass());
  2652     size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass());
  2654     if (oldSpan < newSpan) {
  2655         if (oldCount < newCount && !JSObject::growSlots(cx, obj, oldCount, newCount))
  2656             return false;
  2658         if (newSpan == oldSpan + 1)
  2659             obj->initSlotUnchecked(oldSpan, UndefinedValue());
  2660         else
  2661             obj->initializeSlotRange(oldSpan, newSpan - oldSpan);
  2662     } else {
  2663         /* Trigger write barriers on the old slots before reallocating. */
  2664         obj->prepareSlotRangeForOverwrite(newSpan, oldSpan);
  2665         obj->invalidateSlotRange(newSpan, oldSpan - newSpan);
  2667         if (oldCount > newCount)
  2668             JSObject::shrinkSlots(cx, obj, oldCount, newCount);
  2671     return true;
  2674 /* static */ bool
  2675 JSObject::setLastProperty(ThreadSafeContext *cx, HandleObject obj, HandleShape shape)
  2677     JS_ASSERT(cx->isThreadLocal(obj));
  2678     JS_ASSERT(!obj->inDictionaryMode());
  2679     JS_ASSERT(!shape->inDictionary());
  2680     JS_ASSERT(shape->compartment() == obj->compartment());
  2681     JS_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
  2683     size_t oldSpan = obj->lastProperty()->slotSpan();
  2684     size_t newSpan = shape->slotSpan();
  2686     if (oldSpan == newSpan) {
  2687         obj->shape_ = shape;
  2688         return true;
  2691     if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
  2692         return false;
  2694     obj->shape_ = shape;
  2695     return true;
  2698 /* static */ bool
  2699 JSObject::setSlotSpan(ThreadSafeContext *cx, HandleObject obj, uint32_t span)
  2701     JS_ASSERT(cx->isThreadLocal(obj));
  2702     JS_ASSERT(obj->inDictionaryMode());
  2704     size_t oldSpan = obj->lastProperty()->base()->slotSpan();
  2705     if (oldSpan == span)
  2706         return true;
  2708     if (!JSObject::updateSlotsForSpan(cx, obj, oldSpan, span))
  2709         return false;
  2711     obj->lastProperty()->base()->setSlotSpan(span);
  2712     return true;
  2715 static HeapSlot *
  2716 AllocateSlots(ThreadSafeContext *cx, JSObject *obj, uint32_t nslots)
  2718 #ifdef JSGC_GENERATIONAL
  2719     if (cx->isJSContext())
  2720         return cx->asJSContext()->runtime()->gcNursery.allocateSlots(cx->asJSContext(), obj, nslots);
  2721 #endif
  2722     return cx->pod_malloc<HeapSlot>(nslots);
  2725 static HeapSlot *
  2726 ReallocateSlots(ThreadSafeContext *cx, JSObject *obj, HeapSlot *oldSlots,
  2727                 uint32_t oldCount, uint32_t newCount)
  2729 #ifdef JSGC_GENERATIONAL
  2730     if (cx->isJSContext()) {
  2731         return cx->asJSContext()->runtime()->gcNursery.reallocateSlots(cx->asJSContext(),
  2732                                                                        obj, oldSlots,
  2733                                                                        oldCount, newCount);
  2735 #endif
  2736     return (HeapSlot *)cx->realloc_(oldSlots, oldCount * sizeof(HeapSlot),
  2737                                     newCount * sizeof(HeapSlot));
  2740 /* static */ bool
  2741 JSObject::growSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
  2743     JS_ASSERT(cx->isThreadLocal(obj));
  2744     JS_ASSERT(newCount > oldCount);
  2745     JS_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
  2747     /*
  2748      * Slot capacities are determined by the span of allocated objects. Due to
  2749      * the limited number of bits to store shape slots, object growth is
  2750      * throttled well before the slot capacity can overflow.
  2751      */
  2752     JS_ASSERT(newCount < NELEMENTS_LIMIT);
  2754     /*
  2755      * If we are allocating slots for an object whose type is always created
  2756      * by calling 'new' on a particular script, bump the GC kind for that
  2757      * type to give these objects a larger number of fixed slots when future
  2758      * objects are constructed.
  2759      */
  2760     if (!obj->hasLazyType() && !oldCount && obj->type()->hasNewScript()) {
  2761         JSObject *oldTemplate = obj->type()->newScript()->templateObject;
  2762         gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(oldTemplate->numFixedSlots());
  2763         uint32_t newScriptSlots = gc::GetGCKindSlots(kind);
  2764         if (newScriptSlots == obj->numFixedSlots() &&
  2765             gc::TryIncrementAllocKind(&kind) &&
  2766             cx->isJSContext())
  2768             JSContext *ncx = cx->asJSContext();
  2769             AutoEnterAnalysis enter(ncx);
  2771             Rooted<TypeObject*> typeObj(cx, obj->type());
  2772             RootedShape shape(cx, oldTemplate->lastProperty());
  2773             JSObject *reshapedObj = NewReshapedObject(ncx, typeObj, obj->getParent(), kind, shape,
  2774                                                       MaybeSingletonObject);
  2775             if (!reshapedObj)
  2776                 return false;
  2778             typeObj->newScript()->templateObject = reshapedObj;
  2779             typeObj->markStateChange(ncx);
  2783     if (!oldCount) {
  2784         obj->slots = AllocateSlots(cx, obj, newCount);
  2785         if (!obj->slots)
  2786             return false;
  2787         Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount);
  2788         return true;
  2791     HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
  2792     if (!newslots)
  2793         return false;  /* Leave slots at its old size. */
  2795     obj->slots = newslots;
  2797     Debug_SetSlotRangeToCrashOnTouch(obj->slots + oldCount, newCount - oldCount);
  2799     return true;
  2802 static void
  2803 FreeSlots(ThreadSafeContext *cx, HeapSlot *slots)
  2805     // Note: threads without a JSContext do not have access to nursery allocated things.
  2806 #ifdef JSGC_GENERATIONAL
  2807     if (cx->isJSContext())
  2808         return cx->asJSContext()->runtime()->gcNursery.freeSlots(cx->asJSContext(), slots);
  2809 #endif
  2810     js_free(slots);
  2813 /* static */ void
  2814 JSObject::shrinkSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
  2816     JS_ASSERT(cx->isThreadLocal(obj));
  2817     JS_ASSERT(newCount < oldCount);
  2819     if (newCount == 0) {
  2820         FreeSlots(cx, obj->slots);
  2821         obj->slots = nullptr;
  2822         return;
  2825     JS_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
  2827     HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
  2828     if (!newslots)
  2829         return;  /* Leave slots at its old size. */
  2831     obj->slots = newslots;
  2834 /* static */ bool
  2835 JSObject::sparsifyDenseElement(ExclusiveContext *cx, HandleObject obj, uint32_t index)
  2837     RootedValue value(cx, obj->getDenseElement(index));
  2838     JS_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
  2840     JSObject::removeDenseElementForSparseIndex(cx, obj, index);
  2842     uint32_t slot = obj->slotSpan();
  2843     if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) {
  2844         obj->setDenseElement(index, value);
  2845         return false;
  2848     JS_ASSERT(slot == obj->slotSpan() - 1);
  2849     obj->initSlot(slot, value);
  2851     return true;
  2854 /* static */ bool
  2855 JSObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleObject obj)
  2857     uint32_t initialized = obj->getDenseInitializedLength();
  2859     /* Create new properties with the value of non-hole dense elements. */
  2860     for (uint32_t i = 0; i < initialized; i++) {
  2861         if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
  2862             continue;
  2864         if (!sparsifyDenseElement(cx, obj, i))
  2865             return false;
  2868     if (initialized)
  2869         obj->setDenseInitializedLength(0);
  2871     /*
  2872      * Reduce storage for dense elements which are now holes. Explicitly mark
  2873      * the elements capacity as zero, so that any attempts to add dense
  2874      * elements will be caught in ensureDenseElements.
  2875      */
  2876     if (obj->getDenseCapacity()) {
  2877         obj->shrinkElements(cx, 0);
  2878         obj->getElementsHeader()->capacity = 0;
  2881     return true;
  2884 bool
  2885 JSObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
  2887     JS_ASSERT(isNative());
  2888     JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
  2890     uint32_t cap = getDenseCapacity();
  2891     JS_ASSERT(requiredCapacity >= cap);
  2893     if (requiredCapacity >= NELEMENTS_LIMIT)
  2894         return true;
  2896     uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
  2897     if (newElementsHint >= minimalDenseCount)
  2898         return false;
  2899     minimalDenseCount -= newElementsHint;
  2901     if (minimalDenseCount > cap)
  2902         return true;
  2904     uint32_t len = getDenseInitializedLength();
  2905     const Value *elems = getDenseElements();
  2906     for (uint32_t i = 0; i < len; i++) {
  2907         if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
  2908             return false;
  2910     return true;
  2913 /* static */ JSObject::EnsureDenseResult
  2914 JSObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleObject obj)
  2916     /*
  2917      * Wait until after the object goes into dictionary mode, which must happen
  2918      * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
  2919      * (see PropertyTree::MAX_HEIGHT).
  2920      */
  2921     if (!obj->inDictionaryMode())
  2922         return ED_SPARSE;
  2924     /*
  2925      * Only measure the number of indexed properties every log(n) times when
  2926      * populating the object.
  2927      */
  2928     uint32_t slotSpan = obj->slotSpan();
  2929     if (slotSpan != RoundUpPow2(slotSpan))
  2930         return ED_SPARSE;
  2932     /* Watch for conditions under which an object's elements cannot be dense. */
  2933     if (!obj->nonProxyIsExtensible() || obj->watched())
  2934         return ED_SPARSE;
  2936     /*
  2937      * The indexes in the object need to be sufficiently dense before they can
  2938      * be converted to dense mode.
  2939      */
  2940     uint32_t numDenseElements = 0;
  2941     uint32_t newInitializedLength = 0;
  2943     RootedShape shape(cx, obj->lastProperty());
  2944     while (!shape->isEmptyShape()) {
  2945         uint32_t index;
  2946         if (js_IdIsIndex(shape->propid(), &index)) {
  2947             if (shape->attributes() == JSPROP_ENUMERATE &&
  2948                 shape->hasDefaultGetter() &&
  2949                 shape->hasDefaultSetter())
  2951                 numDenseElements++;
  2952                 newInitializedLength = Max(newInitializedLength, index + 1);
  2953             } else {
  2954                 /*
  2955                  * For simplicity, only densify the object if all indexed
  2956                  * properties can be converted to dense elements.
  2957                  */
  2958                 return ED_SPARSE;
  2961         shape = shape->previous();
  2964     if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
  2965         return ED_SPARSE;
  2967     if (newInitializedLength >= NELEMENTS_LIMIT)
  2968         return ED_SPARSE;
  2970     /*
  2971      * This object meets all necessary restrictions, convert all indexed
  2972      * properties into dense elements.
  2973      */
  2975     if (newInitializedLength > obj->getDenseCapacity()) {
  2976         if (!obj->growElements(cx, newInitializedLength))
  2977             return ED_FAILED;
  2980     obj->ensureDenseInitializedLength(cx, newInitializedLength, 0);
  2982     RootedValue value(cx);
  2984     shape = obj->lastProperty();
  2985     while (!shape->isEmptyShape()) {
  2986         jsid id = shape->propid();
  2987         uint32_t index;
  2988         if (js_IdIsIndex(id, &index)) {
  2989             value = obj->getSlot(shape->slot());
  2991             /*
  2992              * When removing a property from a dictionary, the specified
  2993              * property will be removed from the dictionary list and the
  2994              * last property will then be changed due to reshaping the object.
  2995              * Compute the next shape in the traverse, watching for such
  2996              * removals from the list.
  2997              */
  2998             if (shape != obj->lastProperty()) {
  2999                 shape = shape->previous();
  3000                 if (!obj->removeProperty(cx, id))
  3001                     return ED_FAILED;
  3002             } else {
  3003                 if (!obj->removeProperty(cx, id))
  3004                     return ED_FAILED;
  3005                 shape = obj->lastProperty();
  3008             obj->setDenseElement(index, value);
  3009         } else {
  3010             shape = shape->previous();
  3014     /*
  3015      * All indexed properties on the object are now dense, clear the indexed
  3016      * flag so that we will not start using sparse indexes again if we need
  3017      * to grow the object.
  3018      */
  3019     if (!obj->clearFlag(cx, BaseShape::INDEXED))
  3020         return ED_FAILED;
  3022     return ED_OK;
  3025 static ObjectElements *
  3026 AllocateElements(ThreadSafeContext *cx, JSObject *obj, uint32_t nelems)
  3028 #ifdef JSGC_GENERATIONAL
  3029     if (cx->isJSContext())
  3030         return cx->asJSContext()->runtime()->gcNursery.allocateElements(cx->asJSContext(), obj, nelems);
  3031 #endif
  3033     return static_cast<js::ObjectElements *>(cx->malloc_(nelems * sizeof(HeapValue)));
  3036 static ObjectElements *
  3037 ReallocateElements(ThreadSafeContext *cx, JSObject *obj, ObjectElements *oldHeader,
  3038                    uint32_t oldCount, uint32_t newCount)
  3040 #ifdef JSGC_GENERATIONAL
  3041     if (cx->isJSContext()) {
  3042         return cx->asJSContext()->runtime()->gcNursery.reallocateElements(cx->asJSContext(), obj,
  3043                                                                           oldHeader, oldCount,
  3044                                                                           newCount);
  3046 #endif
  3048     return static_cast<js::ObjectElements *>(cx->realloc_(oldHeader, oldCount * sizeof(HeapSlot),
  3049                                                           newCount * sizeof(HeapSlot)));
  3052 bool
  3053 JSObject::growElements(ThreadSafeContext *cx, uint32_t newcap)
  3055     JS_ASSERT(nonProxyIsExtensible());
  3056     JS_ASSERT(canHaveNonEmptyElements());
  3058     /*
  3059      * When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to
  3060      * grow, double its capacity, to add N elements in amortized O(N) time.
  3062      * Above this limit, grow by 12.5% each time. Speed is still amortized
  3063      * O(N), with a higher constant factor, and we waste less space.
  3064      */
  3065     static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
  3066     static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
  3068     uint32_t oldcap = getDenseCapacity();
  3069     JS_ASSERT(oldcap <= newcap);
  3071     uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
  3072                       ? oldcap * 2
  3073                       : oldcap + (oldcap >> 3);
  3075     uint32_t actualCapacity;
  3076     if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
  3077         JS_ASSERT(newcap <= as<ArrayObject>().length());
  3078         // Preserve the |capacity <= length| invariant for arrays with
  3079         // non-writable length.  See also js::ArraySetLength which initially
  3080         // enforces this requirement.
  3081         actualCapacity = newcap;
  3082     } else {
  3083         actualCapacity = Max(newcap, nextsize);
  3084         if (actualCapacity >= CAPACITY_CHUNK)
  3085             actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
  3086         else if (actualCapacity < SLOT_CAPACITY_MIN)
  3087             actualCapacity = SLOT_CAPACITY_MIN;
  3089         /* Don't let nelements get close to wrapping around uint32_t. */
  3090         if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap)
  3091             return false;
  3094     uint32_t initlen = getDenseInitializedLength();
  3095     uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER;
  3096     uint32_t newAllocated = actualCapacity + ObjectElements::VALUES_PER_HEADER;
  3098     ObjectElements *newheader;
  3099     if (hasDynamicElements()) {
  3100         newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated);
  3101         if (!newheader)
  3102             return false; /* Leave elements as its old size. */
  3103     } else {
  3104         newheader = AllocateElements(cx, this, newAllocated);
  3105         if (!newheader)
  3106             return false; /* Leave elements as its old size. */
  3107         js_memcpy(newheader, getElementsHeader(),
  3108                   (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
  3111     newheader->capacity = actualCapacity;
  3112     elements = newheader->elements();
  3114     Debug_SetSlotRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
  3116     return true;
  3119 void
  3120 JSObject::shrinkElements(ThreadSafeContext *cx, uint32_t newcap)
  3122     JS_ASSERT(cx->isThreadLocal(this));
  3123     JS_ASSERT(canHaveNonEmptyElements());
  3125     uint32_t oldcap = getDenseCapacity();
  3126     JS_ASSERT(newcap <= oldcap);
  3128     // Don't shrink elements below the minimum capacity.
  3129     if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements())
  3130         return;
  3132     newcap = Max(newcap, SLOT_CAPACITY_MIN);
  3134     uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER;
  3135     uint32_t newAllocated = newcap + ObjectElements::VALUES_PER_HEADER;
  3137     ObjectElements *newheader = ReallocateElements(cx, this, getElementsHeader(),
  3138                                                    oldAllocated, newAllocated);
  3139     if (!newheader) {
  3140         cx->recoverFromOutOfMemory();
  3141         return;  // Leave elements at its old size.
  3144     newheader->capacity = newcap;
  3145     elements = newheader->elements();
  3148 bool
  3149 js::SetClassAndProto(JSContext *cx, HandleObject obj,
  3150                      const Class *clasp, Handle<js::TaggedProto> proto,
  3151                      bool *succeeded)
  3153     /*
  3154      * Regenerate shapes for all of the scopes along the old prototype chain,
  3155      * in case any entries were filled by looking up through obj. Stop when a
  3156      * non-native object is found, prototype lookups will not be cached across
  3157      * these.
  3159      * How this shape change is done is very delicate; the change can be made
  3160      * either by marking the object's prototype as uncacheable (such that the
  3161      * property cache and JIT'ed ICs cannot assume the shape determines the
  3162      * prototype) or by just generating a new shape for the object. Choosing
  3163      * the former is bad if the object is on the prototype chain of other
  3164      * objects, as the uncacheable prototype can inhibit iterator caches on
  3165      * those objects and slow down prototype accesses. Choosing the latter is
  3166      * bad if there are many similar objects to this one which will have their
  3167      * prototype mutated, as the generateOwnShape forces the object into
  3168      * dictionary mode and similar property lineages will be repeatedly cloned.
  3170      * :XXX: bug 707717 make this code less brittle.
  3171      */
  3172     *succeeded = false;
  3173     RootedObject oldproto(cx, obj);
  3174     while (oldproto && oldproto->isNative()) {
  3175         if (oldproto->hasSingletonType()) {
  3176             if (!oldproto->generateOwnShape(cx))
  3177                 return false;
  3178         } else {
  3179             if (!oldproto->setUncacheableProto(cx))
  3180                 return false;
  3182         oldproto = oldproto->getProto();
  3185     if (obj->hasSingletonType()) {
  3186         /*
  3187          * Just splice the prototype, but mark the properties as unknown for
  3188          * consistent behavior.
  3189          */
  3190         if (!obj->splicePrototype(cx, clasp, proto))
  3191             return false;
  3192         MarkTypeObjectUnknownProperties(cx, obj->type());
  3193         *succeeded = true;
  3194         return true;
  3197     if (proto.isObject()) {
  3198         RootedObject protoObj(cx, proto.toObject());
  3199         if (!JSObject::setNewTypeUnknown(cx, clasp, protoObj))
  3200             return false;
  3203     TypeObject *type = cx->getNewType(clasp, proto);
  3204     if (!type)
  3205         return false;
  3207     /*
  3208      * Setting __proto__ on an object that has escaped and may be referenced by
  3209      * other heap objects can only be done if the properties of both objects
  3210      * are unknown. Type sets containing this object will contain the original
  3211      * type but not the new type of the object, so we need to go and scan the
  3212      * entire compartment for type sets which have these objects and mark them
  3213      * as containing generic objects.
  3214      */
  3215     MarkTypeObjectUnknownProperties(cx, obj->type(), true);
  3216     MarkTypeObjectUnknownProperties(cx, type, true);
  3218     obj->setType(type);
  3220     *succeeded = true;
  3221     return true;
  3224 static bool
  3225 MaybeResolveConstructor(ExclusiveContext *cxArg, Handle<GlobalObject*> global, JSProtoKey key)
  3227     if (global->isStandardClassResolved(key))
  3228         return true;
  3229     if (!cxArg->shouldBeJSContext())
  3230         return false;
  3232     JSContext *cx = cxArg->asJSContext();
  3233     return GlobalObject::resolveConstructor(cx, global, key);
  3236 bool
  3237 js::GetBuiltinConstructor(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp)
  3239     MOZ_ASSERT(key != JSProto_Null);
  3240     Rooted<GlobalObject*> global(cx, cx->global());
  3241     if (!MaybeResolveConstructor(cx, global, key))
  3242         return false;
  3244     objp.set(&global->getConstructor(key).toObject());
  3245     return true;
  3248 bool
  3249 js::GetBuiltinPrototype(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject protop)
  3251     MOZ_ASSERT(key != JSProto_Null);
  3252     Rooted<GlobalObject*> global(cx, cx->global());
  3253     if (!MaybeResolveConstructor(cx, global, key))
  3254         return false;
  3256     protop.set(&global->getPrototype(key).toObject());
  3257     return true;
  3260 static bool
  3261 IsStandardPrototype(JSObject *obj, JSProtoKey key)
  3263     GlobalObject &global = obj->global();
  3264     Value v = global.getPrototype(key);
  3265     return v.isObject() && obj == &v.toObject();
  3268 JSProtoKey
  3269 JS::IdentifyStandardInstance(JSObject *obj)
  3271     // Note: The prototype shares its JSClass with instances.
  3272     JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
  3273     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
  3274     if (key != JSProto_Null && !IsStandardPrototype(obj, key))
  3275         return key;
  3276     return JSProto_Null;
  3279 JSProtoKey
  3280 JS::IdentifyStandardPrototype(JSObject *obj)
  3282     // Note: The prototype shares its JSClass with instances.
  3283     JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
  3284     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
  3285     if (key != JSProto_Null && IsStandardPrototype(obj, key))
  3286         return key;
  3287     return JSProto_Null;
  3290 JSProtoKey
  3291 JS::IdentifyStandardInstanceOrPrototype(JSObject *obj)
  3293     return JSCLASS_CACHED_PROTO_KEY(obj->getClass());
  3296 bool
  3297 js::FindClassObject(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp)
  3299     JSProtoKey protoKey = GetClassProtoKey(clasp);
  3300     if (protoKey != JSProto_Null) {
  3301         JS_ASSERT(JSProto_Null < protoKey);
  3302         JS_ASSERT(protoKey < JSProto_LIMIT);
  3303         return GetBuiltinConstructor(cx, protoKey, protop);
  3306     JSAtom *atom = Atomize(cx, clasp->name, strlen(clasp->name));
  3307     if (!atom)
  3308         return false;
  3309     RootedId id(cx, AtomToId(atom));
  3311     RootedObject pobj(cx);
  3312     RootedShape shape(cx);
  3313     if (!LookupNativeProperty(cx, cx->global(), id, &pobj, &shape))
  3314         return false;
  3315     RootedValue v(cx);
  3316     if (shape && pobj->isNative()) {
  3317         if (shape->hasSlot())
  3318             v = pobj->nativeGetSlot(shape->slot());
  3320     if (v.isObject())
  3321         protop.set(&v.toObject());
  3322     return true;
  3325 /* static */ bool
  3326 JSObject::allocSlot(ThreadSafeContext *cx, HandleObject obj, uint32_t *slotp)
  3328     JS_ASSERT(cx->isThreadLocal(obj));
  3330     uint32_t slot = obj->slotSpan();
  3331     JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
  3333     /*
  3334      * If this object is in dictionary mode, try to pull a free slot from the
  3335      * shape table's slot-number freelist.
  3336      */
  3337     if (obj->inDictionaryMode()) {
  3338         ShapeTable &table = obj->lastProperty()->table();
  3339         uint32_t last = table.freelist;
  3340         if (last != SHAPE_INVALID_SLOT) {
  3341 #ifdef DEBUG
  3342             JS_ASSERT(last < slot);
  3343             uint32_t next = obj->getSlot(last).toPrivateUint32();
  3344             JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
  3345 #endif
  3347             *slotp = last;
  3349             const Value &vref = obj->getSlot(last);
  3350             table.freelist = vref.toPrivateUint32();
  3351             obj->setSlot(last, UndefinedValue());
  3352             return true;
  3356     if (slot >= SHAPE_MAXIMUM_SLOT) {
  3357         js_ReportOutOfMemory(cx);
  3358         return false;
  3361     *slotp = slot;
  3363     if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1))
  3364         return false;
  3366     return true;
  3369 void
  3370 JSObject::freeSlot(uint32_t slot)
  3372     JS_ASSERT(slot < slotSpan());
  3374     if (inDictionaryMode()) {
  3375         uint32_t &last = lastProperty()->table().freelist;
  3377         /* Can't afford to check the whole freelist, but let's check the head. */
  3378         JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
  3380         /*
  3381          * Place all freed slots other than reserved slots (bug 595230) on the
  3382          * dictionary's free list.
  3383          */
  3384         if (JSSLOT_FREE(getClass()) <= slot) {
  3385             JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
  3386             setSlot(slot, PrivateUint32Value(last));
  3387             last = slot;
  3388             return;
  3391     setSlot(slot, UndefinedValue());
  3394 static bool
  3395 PurgeProtoChain(ExclusiveContext *cx, JSObject *objArg, HandleId id)
  3397     /* Root locally so we can re-assign. */
  3398     RootedObject obj(cx, objArg);
  3400     RootedShape shape(cx);
  3401     while (obj) {
  3402         /* Lookups will not be cached through non-native protos. */
  3403         if (!obj->isNative())
  3404             break;
  3406         shape = obj->nativeLookup(cx, id);
  3407         if (shape) {
  3408             if (!obj->shadowingShapeChange(cx, *shape))
  3409                 return false;
  3411             obj->shadowingShapeChange(cx, *shape);
  3412             return true;
  3414         obj = obj->getProto();
  3417     return true;
  3420 static bool
  3421 PurgeScopeChainHelper(ExclusiveContext *cx, HandleObject objArg, HandleId id)
  3423     /* Re-root locally so we can re-assign. */
  3424     RootedObject obj(cx, objArg);
  3426     JS_ASSERT(obj->isNative());
  3427     JS_ASSERT(obj->isDelegate());
  3429     /* Lookups on integer ids cannot be cached through prototypes. */
  3430     if (JSID_IS_INT(id))
  3431         return true;
  3433     PurgeProtoChain(cx, obj->getProto(), id);
  3435     /*
  3436      * We must purge the scope chain only for Call objects as they are the only
  3437      * kind of cacheable non-global object that can gain properties after outer
  3438      * properties with the same names have been cached or traced. Call objects
  3439      * may gain such properties via eval introducing new vars; see bug 490364.
  3440      */
  3441     if (obj->is<CallObject>()) {
  3442         while ((obj = obj->enclosingScope()) != nullptr) {
  3443             if (!PurgeProtoChain(cx, obj, id))
  3444                 return false;
  3448     return true;
  3451 /*
  3452  * PurgeScopeChain does nothing if obj is not itself a prototype or parent
  3453  * scope, else it reshapes the scope and prototype chains it links. It calls
  3454  * PurgeScopeChainHelper, which asserts that obj is flagged as a delegate
  3455  * (i.e., obj has ever been on a prototype or parent chain).
  3456  */
  3457 static inline bool
  3458 PurgeScopeChain(ExclusiveContext *cx, JS::HandleObject obj, JS::HandleId id)
  3460     if (obj->isDelegate())
  3461         return PurgeScopeChainHelper(cx, obj, id);
  3462     return true;
  3465 bool
  3466 baseops::DefineGeneric(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
  3467                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  3469     return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
  3472 /* static */ bool
  3473 JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj,
  3474                         HandleId id, HandleValue value,
  3475                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
  3477     JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
  3478     js::DefineGenericOp op = obj->getOps()->defineGeneric;
  3479     if (op) {
  3480         if (!cx->shouldBeJSContext())
  3481             return false;
  3482         return op(cx->asJSContext(), obj, id, value, getter, setter, attrs);
  3484     return baseops::DefineGeneric(cx, obj, id, value, getter, setter, attrs);
  3487 /* static */ bool
  3488 JSObject::defineProperty(ExclusiveContext *cx, HandleObject obj,
  3489                          PropertyName *name, HandleValue value,
  3490                          JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
  3492     RootedId id(cx, NameToId(name));
  3493     return defineGeneric(cx, obj, id, value, getter, setter, attrs);
  3496 bool
  3497 baseops::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value,
  3498                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  3500     RootedId id(cx);
  3501     if (index <= JSID_INT_MAX) {
  3502         id = INT_TO_JSID(index);
  3503         return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
  3506     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
  3508     if (!IndexToId(cx, index, &id))
  3509         return false;
  3511     return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
  3514 /* static */ bool
  3515 JSObject::defineElement(ExclusiveContext *cx, HandleObject obj,
  3516                         uint32_t index, HandleValue value,
  3517                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
  3519     js::DefineElementOp op = obj->getOps()->defineElement;
  3520     if (op) {
  3521         if (!cx->shouldBeJSContext())
  3522             return false;
  3523         return op(cx->asJSContext(), obj, index, value, getter, setter, attrs);
  3525     return baseops::DefineElement(cx, obj, index, value, getter, setter, attrs);
  3528 Shape *
  3529 JSObject::addDataProperty(ExclusiveContext *cx, jsid idArg, uint32_t slot, unsigned attrs)
  3531     JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
  3532     RootedObject self(cx, this);
  3533     RootedId id(cx, idArg);
  3534     return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
  3537 Shape *
  3538 JSObject::addDataProperty(ExclusiveContext *cx, HandlePropertyName name,
  3539                           uint32_t slot, unsigned attrs)
  3541     JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
  3542     RootedObject self(cx, this);
  3543     RootedId id(cx, NameToId(name));
  3544     return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
  3547 /*
  3548  * Backward compatibility requires allowing addProperty hooks to mutate the
  3549  * nominal initial value of a slotful property, while GC safety wants that
  3550  * value to be stored before the call-out through the hook.  Optimize to do
  3551  * both while saving cycles for classes that stub their addProperty hook.
  3552  */
  3553 template <ExecutionMode mode>
  3554 static inline bool
  3555 CallAddPropertyHook(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
  3556                     const Class *clasp, HandleObject obj, HandleShape shape,
  3557                     HandleValue nominal)
  3559     if (clasp->addProperty != JS_PropertyStub) {
  3560         if (mode == ParallelExecution)
  3561             return false;
  3563         ExclusiveContext *cx = cxArg->asExclusiveContext();
  3564         if (!cx->shouldBeJSContext())
  3565             return false;
  3567         /* Make a local copy of value so addProperty can mutate its inout parameter. */
  3568         RootedValue value(cx, nominal);
  3570         Rooted<jsid> id(cx, shape->propid());
  3571         if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
  3572             obj->removeProperty(cx, shape->propid());
  3573             return false;
  3575         if (value.get() != nominal) {
  3576             if (shape->hasSlot())
  3577                 obj->nativeSetSlotWithType(cx, shape, value);
  3580     return true;
  3583 template <ExecutionMode mode>
  3584 static inline bool
  3585 CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
  3586                          const Class *clasp, HandleObject obj, uint32_t index,
  3587                          HandleValue nominal)
  3589     /* Inline addProperty for array objects. */
  3590     if (obj->is<ArrayObject>()) {
  3591         ArrayObject *arr = &obj->as<ArrayObject>();
  3592         uint32_t length = arr->length();
  3593         if (index >= length) {
  3594             if (mode == ParallelExecution) {
  3595                 /* We cannot deal with overflows in parallel. */
  3596                 if (length > INT32_MAX)
  3597                     return false;
  3598                 arr->setLengthInt32(index + 1);
  3599             } else {
  3600                 arr->setLength(cxArg->asExclusiveContext(), index + 1);
  3603         return true;
  3606     if (clasp->addProperty != JS_PropertyStub) {
  3607         if (mode == ParallelExecution)
  3608             return false;
  3610         ExclusiveContext *cx = cxArg->asExclusiveContext();
  3611         if (!cx->shouldBeJSContext())
  3612             return false;
  3614         /* Make a local copy of value so addProperty can mutate its inout parameter. */
  3615         RootedValue value(cx, nominal);
  3617         Rooted<jsid> id(cx, INT_TO_JSID(index));
  3618         if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
  3619             obj->setDenseElementHole(cx, index);
  3620             return false;
  3622         if (value.get() != nominal)
  3623             obj->setDenseElementWithType(cx, index, value);
  3626     return true;
  3629 template <ExecutionMode mode>
  3630 static bool
  3631 UpdateShapeTypeAndValue(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
  3632                         JSObject *obj, Shape *shape, const Value &value)
  3634     jsid id = shape->propid();
  3635     if (shape->hasSlot()) {
  3636         if (mode == ParallelExecution) {
  3637             if (!obj->nativeSetSlotIfHasType(shape, value))
  3638                 return false;
  3639         } else {
  3640             obj->nativeSetSlotWithType(cx->asExclusiveContext(), shape, value);
  3643     if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter()) {
  3644         if (mode == ParallelExecution) {
  3645             if (!IsTypePropertyIdMarkedNonData(obj, id))
  3646                 return false;
  3647         } else {
  3648             MarkTypePropertyNonData(cx->asExclusiveContext(), obj, id);
  3651     if (!shape->writable()) {
  3652         if (mode == ParallelExecution) {
  3653             if (!IsTypePropertyIdMarkedNonWritable(obj, id))
  3654                 return false;
  3655         } else {
  3656             MarkTypePropertyNonWritable(cx->asExclusiveContext(), obj, id);
  3659     return true;
  3662 template <ExecutionMode mode>
  3663 static inline bool
  3664 DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
  3665                         HandleObject obj, HandleId id,
  3666                         PropertyOp getter, StrictPropertyOp setter,
  3667                         unsigned attrs, HandleValue value,
  3668                         bool callSetterAfterwards, bool setterIsStrict)
  3670     /* Use dense storage for new indexed properties where possible. */
  3671     if (JSID_IS_INT(id) &&
  3672         getter == JS_PropertyStub &&
  3673         setter == JS_StrictPropertyStub &&
  3674         attrs == JSPROP_ENUMERATE &&
  3675         (!obj->isIndexed() || !obj->nativeContainsPure(id)) &&
  3676         !obj->is<TypedArrayObject>())
  3678         uint32_t index = JSID_TO_INT(id);
  3679         bool definesPast;
  3680         if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast))
  3681             return false;
  3682         if (definesPast)
  3683             return true;
  3685         JSObject::EnsureDenseResult result;
  3686         if (mode == ParallelExecution) {
  3687             if (obj->writeToIndexWouldMarkNotPacked(index))
  3688                 return false;
  3689             result = obj->ensureDenseElementsPreservePackedFlag(cx, index, 1);
  3690         } else {
  3691             result = obj->ensureDenseElements(cx->asExclusiveContext(), index, 1);
  3694         if (result == JSObject::ED_FAILED)
  3695             return false;
  3696         if (result == JSObject::ED_OK) {
  3697             if (mode == ParallelExecution) {
  3698                 if (!obj->setDenseElementIfHasType(index, value))
  3699                     return false;
  3700             } else {
  3701                 obj->setDenseElementWithType(cx->asExclusiveContext(), index, value);
  3703             return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
  3707     if (obj->is<ArrayObject>()) {
  3708         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
  3709         if (id == NameToId(cx->names().length)) {
  3710             if (mode == SequentialExecution && !cx->shouldBeJSContext())
  3711                 return false;
  3712             return ArraySetLength<mode>(ExecutionModeTraits<mode>::toContextType(cx), arr, id,
  3713                                         attrs, value, setterIsStrict);
  3716         uint32_t index;
  3717         if (js_IdIsIndex(id, &index)) {
  3718             bool definesPast;
  3719             if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
  3720                 return false;
  3721             if (definesPast)
  3722                 return true;
  3726     // Don't define new indexed properties on typed arrays.
  3727     if (obj->is<TypedArrayObject>()) {
  3728         uint64_t index;
  3729         if (IsTypedArrayIndex(id, &index))
  3730             return true;
  3733     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
  3735     RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter,
  3736                                                       SHAPE_INVALID_SLOT, attrs, 0));
  3737     if (!shape)
  3738         return false;
  3740     if (!UpdateShapeTypeAndValue<mode>(cx, obj, shape, value))
  3741         return false;
  3743     /*
  3744      * Clear any existing dense index after adding a sparse indexed property,
  3745      * and investigate converting the object to dense indexes.
  3746      */
  3747     if (JSID_IS_INT(id)) {
  3748         if (mode == ParallelExecution)
  3749             return false;
  3751         ExclusiveContext *ncx = cx->asExclusiveContext();
  3752         uint32_t index = JSID_TO_INT(id);
  3753         JSObject::removeDenseElementForSparseIndex(ncx, obj, index);
  3754         JSObject::EnsureDenseResult result = JSObject::maybeDensifySparseElements(ncx, obj);
  3755         if (result == JSObject::ED_FAILED)
  3756             return false;
  3757         if (result == JSObject::ED_OK) {
  3758             JS_ASSERT(setter == JS_StrictPropertyStub);
  3759             return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
  3763     if (!CallAddPropertyHook<mode>(cx, obj->getClass(), obj, shape, value))
  3764         return false;
  3766     if (callSetterAfterwards && setter != JS_StrictPropertyStub) {
  3767         if (!cx->shouldBeJSContext())
  3768             return false;
  3769         RootedValue nvalue(cx, value);
  3770         return NativeSet<mode>(ExecutionModeTraits<mode>::toContextType(cx),
  3771                                obj, obj, shape, setterIsStrict, &nvalue);
  3773     return true;
  3776 static bool
  3777 NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
  3778                         MutableHandle<Shape*> shapep);
  3780 bool
  3781 js::DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
  3782                          PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  3784     JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
  3786     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
  3788     /*
  3789      * If defining a getter or setter, we must check for its counterpart and
  3790      * update the attributes and property ops.  A getter or setter is really
  3791      * only half of a property.
  3792      */
  3793     RootedShape shape(cx);
  3794     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
  3795         /*
  3796          * If we are defining a getter whose setter was already defined, or
  3797          * vice versa, finish the job via obj->changeProperty.
  3798          */
  3799         if (!NativeLookupOwnProperty(cx, obj, id, &shape))
  3800             return false;
  3801         if (shape) {
  3802             if (IsImplicitDenseOrTypedArrayElement(shape)) {
  3803                 if (obj->is<TypedArrayObject>()) {
  3804                     /* Ignore getter/setter properties added to typed arrays. */
  3805                     return true;
  3807                 if (!JSObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
  3808                     return false;
  3809                 shape = obj->nativeLookup(cx, id);
  3811             if (shape->isAccessorDescriptor()) {
  3812                 shape = JSObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs,
  3813                                                                       JSPROP_GETTER | JSPROP_SETTER,
  3814                                                                       (attrs & JSPROP_GETTER)
  3815                                                                       ? getter
  3816                                                                       : shape->getter(),
  3817                                                                       (attrs & JSPROP_SETTER)
  3818                                                                       ? setter
  3819                                                                       : shape->setter());
  3820                 if (!shape)
  3821                     return false;
  3822             } else {
  3823                 shape = nullptr;
  3828     /*
  3829      * Purge the property cache of any properties named by id that are about
  3830      * to be shadowed in obj's scope chain.
  3831      */
  3832     if (!PurgeScopeChain(cx, obj, id))
  3833         return false;
  3835     /* Use the object's class getter and setter by default. */
  3836     const Class *clasp = obj->getClass();
  3837     if (!getter && !(attrs & JSPROP_GETTER))
  3838         getter = clasp->getProperty;
  3839     if (!setter && !(attrs & JSPROP_SETTER))
  3840         setter = clasp->setProperty;
  3842     if (!shape) {
  3843         return DefinePropertyOrElement<SequentialExecution>(cx, obj, id, getter, setter,
  3844                                                             attrs, value, false, false);
  3847     JS_ALWAYS_TRUE(UpdateShapeTypeAndValue<SequentialExecution>(cx, obj, shape, value));
  3849     return CallAddPropertyHook<SequentialExecution>(cx, clasp, obj, shape, value);
  3852 /*
  3853  * Call obj's resolve hook.
  3855  * cx, id, and flags are the parameters initially passed to the ongoing lookup;
  3856  * objp and propp are its out parameters. obj is an object along the prototype
  3857  * chain from where the lookup started.
  3859  * There are four possible outcomes:
  3861  *   - On failure, report an error or exception and return false.
  3863  *   - If we are already resolving a property of *curobjp, set *recursedp = true,
  3864  *     and return true.
  3866  *   - If the resolve hook finds or defines the sought property, set *objp and
  3867  *     *propp appropriately, set *recursedp = false, and return true.
  3869  *   - Otherwise no property was resolved. Set *propp = nullptr and
  3870  *     *recursedp = false and return true.
  3871  */
  3872 static MOZ_ALWAYS_INLINE bool
  3873 CallResolveOp(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp,
  3874               MutableHandleShape propp, bool *recursedp)
  3876     const Class *clasp = obj->getClass();
  3877     JSResolveOp resolve = clasp->resolve;
  3879     /*
  3880      * Avoid recursion on (obj, id) already being resolved on cx.
  3882      * Once we have successfully added an entry for (obj, key) to
  3883      * cx->resolvingTable, control must go through cleanup: before
  3884      * returning.  But note that JS_DHASH_ADD may find an existing
  3885      * entry, in which case we bail to suppress runaway recursion.
  3886      */
  3887     AutoResolving resolving(cx, obj, id);
  3888     if (resolving.alreadyStarted()) {
  3889         /* Already resolving id in obj -- suppress recursion. */
  3890         *recursedp = true;
  3891         return true;
  3893     *recursedp = false;
  3895     propp.set(nullptr);
  3897     if (clasp->flags & JSCLASS_NEW_RESOLVE) {
  3898         JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
  3899         RootedObject obj2(cx, nullptr);
  3900         if (!newresolve(cx, obj, id, &obj2))
  3901             return false;
  3903         /*
  3904          * We trust the new style resolve hook to set obj2 to nullptr when
  3905          * the id cannot be resolved. But, when obj2 is not null, we do
  3906          * not assume that id must exist and do full nativeLookup for
  3907          * compatibility.
  3908          */
  3909         if (!obj2)
  3910             return true;
  3912         if (!obj2->isNative()) {
  3913             /* Whoops, newresolve handed back a foreign obj2. */
  3914             JS_ASSERT(obj2 != obj);
  3915             return JSObject::lookupGeneric(cx, obj2, id, objp, propp);
  3918         objp.set(obj2);
  3919     } else {
  3920         if (!resolve(cx, obj, id))
  3921             return false;
  3923         objp.set(obj);
  3926     if (JSID_IS_INT(id) && objp->containsDenseElement(JSID_TO_INT(id))) {
  3927         MarkDenseOrTypedArrayElementFound<CanGC>(propp);
  3928         return true;
  3931     Shape *shape;
  3932     if (!objp->nativeEmpty() && (shape = objp->nativeLookup(cx, id)))
  3933         propp.set(shape);
  3934     else
  3935         objp.set(nullptr);
  3937     return true;
  3940 template <AllowGC allowGC>
  3941 static MOZ_ALWAYS_INLINE bool
  3942 LookupOwnPropertyInline(ExclusiveContext *cx,
  3943                         typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
  3944                         typename MaybeRooted<jsid, allowGC>::HandleType id,
  3945                         typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
  3946                         typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
  3947                         bool *donep)
  3949     // Check for a native dense element.
  3950     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
  3951         objp.set(obj);
  3952         MarkDenseOrTypedArrayElementFound<allowGC>(propp);
  3953         *donep = true;
  3954         return true;
  3957     // Check for a typed array element. Integer lookups always finish here
  3958     // so that integer properties on the prototype are ignored even for out
  3959     // of bounds accesses.
  3960     if (obj->template is<TypedArrayObject>()) {
  3961         uint64_t index;
  3962         if (IsTypedArrayIndex(id, &index)) {
  3963             if (index < obj->template as<TypedArrayObject>().length()) {
  3964                 objp.set(obj);
  3965                 MarkDenseOrTypedArrayElementFound<allowGC>(propp);
  3966             } else {
  3967                 objp.set(nullptr);
  3968                 propp.set(nullptr);
  3970             *donep = true;
  3971             return true;
  3975     // Check for a native property.
  3976     if (Shape *shape = obj->nativeLookup(cx, id)) {
  3977         objp.set(obj);
  3978         propp.set(shape);
  3979         *donep = true;
  3980         return true;
  3983     // id was not found in obj. Try obj's resolve hook, if any.
  3984     if (obj->getClass()->resolve != JS_ResolveStub) {
  3985         if (!cx->shouldBeJSContext() || !allowGC)
  3986             return false;
  3988         bool recursed;
  3989         if (!CallResolveOp(cx->asJSContext(),
  3990                            MaybeRooted<JSObject*, allowGC>::toHandle(obj),
  3991                            MaybeRooted<jsid, allowGC>::toHandle(id),
  3992                            MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
  3993                            MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
  3994                            &recursed))
  3996             return false;
  3999         if (recursed) {
  4000             objp.set(nullptr);
  4001             propp.set(nullptr);
  4002             *donep = true;
  4003             return true;
  4006         if (propp) {
  4007             *donep = true;
  4008             return true;
  4012     *donep = false;
  4013     return true;
  4016 static bool
  4017 NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
  4018                         MutableHandle<Shape*> shapep)
  4020     RootedObject pobj(cx);
  4021     bool done;
  4023     if (!LookupOwnPropertyInline<CanGC>(cx, obj, id, &pobj, shapep, &done))
  4024         return false;
  4025     if (!done || pobj != obj)
  4026         shapep.set(nullptr);
  4027     return true;
  4030 template <AllowGC allowGC>
  4031 static MOZ_ALWAYS_INLINE bool
  4032 LookupPropertyInline(ExclusiveContext *cx,
  4033                      typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
  4034                      typename MaybeRooted<jsid, allowGC>::HandleType id,
  4035                      typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
  4036                      typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
  4038     /* NB: The logic of this procedure is implicitly reflected in IonBuilder.cpp's
  4039      *     |CanEffectlesslyCallLookupGenericOnObject| logic.
  4040      *     If this changes, please remember to update the logic there as well.
  4041      */
  4043     /* Search scopes starting with obj and following the prototype link. */
  4044     typename MaybeRooted<JSObject*, allowGC>::RootType current(cx, obj);
  4046     while (true) {
  4047         bool done;
  4048         if (!LookupOwnPropertyInline<allowGC>(cx, current, id, objp, propp, &done))
  4049             return false;
  4050         if (done)
  4051             return true;
  4053         typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
  4055         if (!proto)
  4056             break;
  4057         if (!proto->isNative()) {
  4058             if (!cx->shouldBeJSContext() || !allowGC)
  4059                 return false;
  4060             return JSObject::lookupGeneric(cx->asJSContext(),
  4061                                            MaybeRooted<JSObject*, allowGC>::toHandle(proto),
  4062                                            MaybeRooted<jsid, allowGC>::toHandle(id),
  4063                                            MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
  4064                                            MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
  4067         current = proto;
  4070     objp.set(nullptr);
  4071     propp.set(nullptr);
  4072     return true;
  4075 template <AllowGC allowGC>
  4076 bool
  4077 baseops::LookupProperty(ExclusiveContext *cx,
  4078                         typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
  4079                         typename MaybeRooted<jsid, allowGC>::HandleType id,
  4080                         typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
  4081                         typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
  4083     return LookupPropertyInline<allowGC>(cx, obj, id, objp, propp);
  4086 template bool
  4087 baseops::LookupProperty<CanGC>(ExclusiveContext *cx, HandleObject obj, HandleId id,
  4088                                MutableHandleObject objp, MutableHandleShape propp);
  4090 template bool
  4091 baseops::LookupProperty<NoGC>(ExclusiveContext *cx, JSObject *obj, jsid id,
  4092                               FakeMutableHandle<JSObject*> objp,
  4093                               FakeMutableHandle<Shape*> propp);
  4095 /* static */ bool
  4096 JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id,
  4097                         MutableHandleObject objp, MutableHandleShape propp)
  4099     /*
  4100      * NB: The logic of lookupGeneric is implicitly reflected in IonBuilder.cpp's
  4101      *     |CanEffectlesslyCallLookupGenericOnObject| logic.
  4102      *     If this changes, please remember to update the logic there as well.
  4103      */
  4104     LookupGenericOp op = obj->getOps()->lookupGeneric;
  4105     if (op)
  4106         return op(cx, obj, id, objp, propp);
  4107     return baseops::LookupProperty<js::CanGC>(cx, obj, id, objp, propp);
  4110 bool
  4111 baseops::LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
  4112                        MutableHandleObject objp, MutableHandleShape propp)
  4114     RootedId id(cx);
  4115     if (!IndexToId(cx, index, &id))
  4116         return false;
  4118     return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
  4121 bool
  4122 js::LookupNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
  4123                          MutableHandleObject objp, MutableHandleShape propp)
  4125     return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
  4128 bool
  4129 js::LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
  4130                MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp)
  4132     RootedId id(cx, NameToId(name));
  4134     for (RootedObject scope(cx, scopeChain); scope; scope = scope->enclosingScope()) {
  4135         if (!JSObject::lookupGeneric(cx, scope, id, pobjp, propp))
  4136             return false;
  4137         if (propp) {
  4138             objp.set(scope);
  4139             return true;
  4143     objp.set(nullptr);
  4144     pobjp.set(nullptr);
  4145     propp.set(nullptr);
  4146     return true;
  4149 bool
  4150 js::LookupNameNoGC(JSContext *cx, PropertyName *name, JSObject *scopeChain,
  4151                    JSObject **objp, JSObject **pobjp, Shape **propp)
  4153     AutoAssertNoException nogc(cx);
  4155     JS_ASSERT(!*objp && !*pobjp && !*propp);
  4157     for (JSObject *scope = scopeChain; scope; scope = scope->enclosingScope()) {
  4158         if (scope->getOps()->lookupGeneric)
  4159             return false;
  4160         if (!LookupPropertyInline<NoGC>(cx, scope, NameToId(name), pobjp, propp))
  4161             return false;
  4162         if (*propp) {
  4163             *objp = scope;
  4164             return true;
  4168     return true;
  4171 bool
  4172 js::LookupNameWithGlobalDefault(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
  4173                                 MutableHandleObject objp)
  4175     RootedId id(cx, NameToId(name));
  4177     RootedObject pobj(cx);
  4178     RootedShape prop(cx);
  4180     RootedObject scope(cx, scopeChain);
  4181     for (; !scope->is<GlobalObject>(); scope = scope->enclosingScope()) {
  4182         if (!JSObject::lookupGeneric(cx, scope, id, &pobj, &prop))
  4183             return false;
  4184         if (prop)
  4185             break;
  4188     objp.set(scope);
  4189     return true;
  4192 template <AllowGC allowGC>
  4193 bool
  4194 js::HasOwnProperty(JSContext *cx, LookupGenericOp lookup,
  4195                    typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
  4196                    typename MaybeRooted<jsid, allowGC>::HandleType id,
  4197                    typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
  4198                    typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
  4200     if (lookup) {
  4201         if (!allowGC)
  4202             return false;
  4203         if (!lookup(cx,
  4204                     MaybeRooted<JSObject*, allowGC>::toHandle(obj),
  4205                     MaybeRooted<jsid, allowGC>::toHandle(id),
  4206                     MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
  4207                     MaybeRooted<Shape*, allowGC>::toMutableHandle(propp)))
  4209             return false;
  4211     } else {
  4212         bool done;
  4213         if (!LookupOwnPropertyInline<allowGC>(cx, obj, id, objp, propp, &done))
  4214             return false;
  4215         if (!done) {
  4216             objp.set(nullptr);
  4217             propp.set(nullptr);
  4218             return true;
  4222     if (!propp)
  4223         return true;
  4225     if (objp == obj)
  4226         return true;
  4228     JSObject *outer = nullptr;
  4229     if (JSObjectOp op = objp->getClass()->ext.outerObject) {
  4230         if (!allowGC)
  4231             return false;
  4232         RootedObject inner(cx, objp);
  4233         outer = op(cx, inner);
  4234         if (!outer)
  4235             return false;
  4238     if (outer != objp)
  4239         propp.set(nullptr);
  4240     return true;
  4243 template bool
  4244 js::HasOwnProperty<CanGC>(JSContext *cx, LookupGenericOp lookup,
  4245                           HandleObject obj, HandleId id,
  4246                           MutableHandleObject objp, MutableHandleShape propp);
  4248 template bool
  4249 js::HasOwnProperty<NoGC>(JSContext *cx, LookupGenericOp lookup,
  4250                          JSObject *obj, jsid id,
  4251                          FakeMutableHandle<JSObject*> objp, FakeMutableHandle<Shape*> propp);
  4253 bool
  4254 js::HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *resultp)
  4256     RootedObject pobj(cx);
  4257     RootedShape shape(cx);
  4258     if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
  4259         return false;
  4260     *resultp = (shape != nullptr);
  4261     return true;
  4264 template <AllowGC allowGC>
  4265 static MOZ_ALWAYS_INLINE bool
  4266 NativeGetInline(JSContext *cx,
  4267                 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
  4268                 typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
  4269                 typename MaybeRooted<JSObject*, allowGC>::HandleType pobj,
  4270                 typename MaybeRooted<Shape*, allowGC>::HandleType shape,
  4271                 typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
  4273     JS_ASSERT(pobj->isNative());
  4275     if (shape->hasSlot()) {
  4276         vp.set(pobj->nativeGetSlot(shape->slot()));
  4277         JS_ASSERT(!vp.isMagic());
  4278         JS_ASSERT_IF(!pobj->hasSingletonType() &&
  4279                      !pobj->template is<ScopeObject>() &&
  4280                      shape->hasDefaultGetter(),
  4281                      js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp));
  4282     } else {
  4283         vp.setUndefined();
  4285     if (shape->hasDefaultGetter())
  4286         return true;
  4289         jsbytecode *pc;
  4290         JSScript *script = cx->currentScript(&pc);
  4291 #ifdef JS_ION
  4292         if (script && script->hasBaselineScript()) {
  4293             switch (JSOp(*pc)) {
  4294               case JSOP_GETPROP:
  4295               case JSOP_CALLPROP:
  4296               case JSOP_LENGTH:
  4297                 script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc));
  4298                 break;
  4299               default:
  4300                 break;
  4303 #endif
  4306     if (!allowGC)
  4307         return false;
  4309     if (!shape->get(cx,
  4310                     MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
  4311                     MaybeRooted<JSObject*, allowGC>::toHandle(obj),
  4312                     MaybeRooted<JSObject*, allowGC>::toHandle(pobj),
  4313                     MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
  4315         return false;
  4318     /* Update slotful shapes according to the value produced by the getter. */
  4319     if (shape->hasSlot() && pobj->nativeContains(cx, shape))
  4320         pobj->nativeSetSlot(shape->slot(), vp);
  4322     return true;
  4325 bool
  4326 js::NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Handle<Shape*> shape,
  4327               MutableHandle<Value> vp)
  4329     return NativeGetInline<CanGC>(cx, obj, obj, pobj, shape, vp);
  4332 template <ExecutionMode mode>
  4333 bool
  4334 js::NativeSet(typename ExecutionModeTraits<mode>::ContextType cxArg,
  4335               Handle<JSObject*> obj, Handle<JSObject*> receiver,
  4336               HandleShape shape, bool strict, MutableHandleValue vp)
  4338     JS_ASSERT(cxArg->isThreadLocal(obj));
  4339     JS_ASSERT(obj->isNative());
  4341     if (shape->hasSlot()) {
  4342         /* If shape has a stub setter, just store vp. */
  4343         if (shape->hasDefaultSetter()) {
  4344             if (mode == ParallelExecution) {
  4345                 if (!obj->nativeSetSlotIfHasType(shape, vp))
  4346                     return false;
  4347             } else {
  4348                 obj->nativeSetSlotWithType(cxArg->asExclusiveContext(), shape, vp);
  4351             return true;
  4355     if (mode == ParallelExecution)
  4356         return false;
  4357     JSContext *cx = cxArg->asJSContext();
  4359     if (!shape->hasSlot()) {
  4360         /*
  4361          * Allow API consumers to create shared properties with stub setters.
  4362          * Such properties effectively function as data descriptors which are
  4363          * not writable, so attempting to set such a property should do nothing
  4364          * or throw if we're in strict mode.
  4365          */
  4366         if (!shape->hasGetterValue() && shape->hasDefaultSetter())
  4367             return js_ReportGetterOnlyAssignment(cx, strict);
  4370     RootedValue ovp(cx, vp);
  4372     uint32_t sample = cx->runtime()->propertyRemovals;
  4373     if (!shape->set(cx, obj, receiver, strict, vp))
  4374         return false;
  4376     /*
  4377      * Update any slot for the shape with the value produced by the setter,
  4378      * unless the setter deleted the shape.
  4379      */
  4380     if (shape->hasSlot() &&
  4381         (MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
  4382          obj->nativeContains(cx, shape)))
  4384         obj->setSlot(shape->slot(), vp);
  4387     return true;
  4390 template bool
  4391 js::NativeSet<SequentialExecution>(JSContext *cx,
  4392                                    Handle<JSObject*> obj, Handle<JSObject*> receiver,
  4393                                    HandleShape shape, bool strict, MutableHandleValue vp);
  4394 template bool
  4395 js::NativeSet<ParallelExecution>(ForkJoinContext *cx,
  4396                                  Handle<JSObject*> obj, Handle<JSObject*> receiver,
  4397                                  HandleShape shape, bool strict, MutableHandleValue vp);
  4399 template <AllowGC allowGC>
  4400 static MOZ_ALWAYS_INLINE bool
  4401 GetPropertyHelperInline(JSContext *cx,
  4402                         typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
  4403                         typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
  4404                         typename MaybeRooted<jsid, allowGC>::HandleType id,
  4405                         typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
  4407     /* This call site is hot -- use the always-inlined variant of LookupNativeProperty(). */
  4408     typename MaybeRooted<JSObject*, allowGC>::RootType obj2(cx);
  4409     typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
  4410     if (!LookupPropertyInline<allowGC>(cx, obj, id, &obj2, &shape))
  4411         return false;
  4413     if (!shape) {
  4414         if (!allowGC)
  4415             return false;
  4417         vp.setUndefined();
  4419         if (!CallJSPropertyOp(cx, obj->getClass()->getProperty,
  4420                               MaybeRooted<JSObject*, allowGC>::toHandle(obj),
  4421                               MaybeRooted<jsid, allowGC>::toHandle(id),
  4422                               MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
  4424             return false;
  4427         /*
  4428          * Give a strict warning if foo.bar is evaluated by a script for an
  4429          * object foo with no property named 'bar'.
  4430          */
  4431         if (vp.isUndefined()) {
  4432             jsbytecode *pc = nullptr;
  4433             RootedScript script(cx, cx->currentScript(&pc));
  4434             if (!pc)
  4435                 return true;
  4436             JSOp op = (JSOp) *pc;
  4438             if (op == JSOP_GETXPROP) {
  4439                 /* Undefined property during a name lookup, report an error. */
  4440                 JSAutoByteString printable;
  4441                 if (js_ValueToPrintable(cx, IdToValue(id), &printable))
  4442                     js_ReportIsNotDefined(cx, printable.ptr());
  4443                 return false;
  4446             /* Don't warn if extra warnings not enabled or for random getprop operations. */
  4447             if (!cx->options().extraWarnings() || (op != JSOP_GETPROP && op != JSOP_GETELEM))
  4448                 return true;
  4450             /* Don't warn repeatedly for the same script. */
  4451             if (!script || script->warnedAboutUndefinedProp())
  4452                 return true;
  4454             /*
  4455              * Don't warn in self-hosted code (where the further presence of
  4456              * JS::ContextOptions::werror() would result in impossible-to-avoid
  4457              * errors to entirely-innocent client code).
  4458              */
  4459             if (script->selfHosted())
  4460                 return true;
  4462             /* We may just be checking if that object has an iterator. */
  4463             if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
  4464                 return true;
  4466             /* Do not warn about tests like (obj[prop] == undefined). */
  4467             pc += js_CodeSpec[op].length;
  4468             if (Detecting(cx, script, pc))
  4469                 return true;
  4471             unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
  4472             script->setWarnedAboutUndefinedProp();
  4474             /* Ok, bad undefined property reference: whine about it. */
  4475             RootedValue val(cx, IdToValue(id));
  4476             if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
  4477                                           JSDVG_IGNORE_STACK, val, js::NullPtr(),
  4478                                           nullptr, nullptr))
  4480                 return false;
  4483         return true;
  4486     if (!obj2->isNative()) {
  4487         if (!allowGC)
  4488             return false;
  4489         HandleObject obj2Handle = MaybeRooted<JSObject*, allowGC>::toHandle(obj2);
  4490         HandleObject receiverHandle = MaybeRooted<JSObject*, allowGC>::toHandle(receiver);
  4491         HandleId idHandle = MaybeRooted<jsid, allowGC>::toHandle(id);
  4492         MutableHandleValue vpHandle = MaybeRooted<Value, allowGC>::toMutableHandle(vp);
  4493         return obj2->template is<ProxyObject>()
  4494                ? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle)
  4495                : JSObject::getGeneric(cx, obj2Handle, obj2Handle, idHandle, vpHandle);
  4498     if (IsImplicitDenseOrTypedArrayElement(shape)) {
  4499         vp.set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
  4500         return true;
  4503     /* This call site is hot -- use the always-inlined variant of NativeGet(). */
  4504     if (!NativeGetInline<allowGC>(cx, obj, receiver, obj2, shape, vp))
  4505         return false;
  4507     return true;
  4510 bool
  4511 baseops::GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp)
  4513     /* This call site is hot -- use the always-inlined variant of GetPropertyHelper(). */
  4514     return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
  4517 bool
  4518 baseops::GetPropertyNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
  4520     AutoAssertNoException nogc(cx);
  4521     return GetPropertyHelperInline<NoGC>(cx, obj, receiver, id, vp);
  4524 static MOZ_ALWAYS_INLINE bool
  4525 LookupPropertyPureInline(JSObject *obj, jsid id, JSObject **objp, Shape **propp)
  4527     if (!obj->isNative())
  4528         return false;
  4530     JSObject *current = obj;
  4531     while (true) {
  4532         /* Search for a native dense element, typed array element, or property. */
  4534         if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) {
  4535             *objp = current;
  4536             MarkDenseOrTypedArrayElementFound<NoGC>(propp);
  4537             return true;
  4540         if (current->is<TypedArrayObject>()) {
  4541             uint64_t index;
  4542             if (IsTypedArrayIndex(id, &index)) {
  4543                 if (index < obj->as<TypedArrayObject>().length()) {
  4544                     *objp = current;
  4545                     MarkDenseOrTypedArrayElementFound<NoGC>(propp);
  4546                 } else {
  4547                     *objp = nullptr;
  4548                     *propp = nullptr;
  4550                 return true;
  4554         if (Shape *shape = current->nativeLookupPure(id)) {
  4555             *objp = current;
  4556             *propp = shape;
  4557             return true;
  4560         /* Fail if there's a resolve hook. */
  4561         if (current->getClass()->resolve != JS_ResolveStub)
  4562             return false;
  4564         JSObject *proto = current->getProto();
  4566         if (!proto)
  4567             break;
  4568         if (!proto->isNative())
  4569             return false;
  4571         current = proto;
  4574     *objp = nullptr;
  4575     *propp = nullptr;
  4576     return true;
  4579 static MOZ_ALWAYS_INLINE bool
  4580 NativeGetPureInline(JSObject *pobj, Shape *shape, Value *vp)
  4582     JS_ASSERT(pobj->isNative());
  4584     if (shape->hasSlot()) {
  4585         *vp = pobj->nativeGetSlot(shape->slot());
  4586         JS_ASSERT(!vp->isMagic());
  4587     } else {
  4588         vp->setUndefined();
  4591     /* Fail if we have a custom getter. */
  4592     return shape->hasDefaultGetter();
  4595 bool
  4596 js::LookupPropertyPure(JSObject *obj, jsid id, JSObject **objp, Shape **propp)
  4598     return LookupPropertyPureInline(obj, id, objp, propp);
  4601 static inline bool
  4602 IdIsLength(ThreadSafeContext *cx, jsid id)
  4604     return JSID_IS_ATOM(id) && cx->names().length == JSID_TO_ATOM(id);
  4607 /*
  4608  * A pure version of GetPropertyHelper that can be called from parallel code
  4609  * without locking. This code path cannot GC. This variant returns false
  4610  * whenever a side-effect might have occured in the effectful version. This
  4611  * includes, but is not limited to:
  4613  *  - Any object in the lookup chain has a non-stub resolve hook.
  4614  *  - Any object in the lookup chain is non-native.
  4615  *  - The property has a getter.
  4616  */
  4617 bool
  4618 js::GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp)
  4620     /* Deal with native objects. */
  4621     JSObject *obj2;
  4622     Shape *shape;
  4623     if (!LookupPropertyPureInline(obj, id, &obj2, &shape))
  4624         return false;
  4626     if (!shape) {
  4627         /* Fail if we have a non-stub class op hooks. */
  4628         if (obj->getClass()->getProperty && obj->getClass()->getProperty != JS_PropertyStub)
  4629             return false;
  4631         if (obj->getOps()->getElement)
  4632             return false;
  4634         /* Vanilla native object, return undefined. */
  4635         vp->setUndefined();
  4636         return true;
  4639     if (IsImplicitDenseOrTypedArrayElement(shape)) {
  4640         *vp = obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id));
  4641         return true;
  4644     /* Special case 'length' on Array and TypedArray. */
  4645     if (IdIsLength(cx, id)) {
  4646         if (obj->is<ArrayObject>()) {
  4647             vp->setNumber(obj->as<ArrayObject>().length());
  4648             return true;
  4651         if (obj->is<TypedArrayObject>()) {
  4652             vp->setNumber(obj->as<TypedArrayObject>().length());
  4653             return true;
  4657     return NativeGetPureInline(obj2, shape, vp);
  4660 static bool
  4661 MOZ_ALWAYS_INLINE
  4662 GetElementPure(ThreadSafeContext *cx, JSObject *obj, uint32_t index, Value *vp)
  4664     if (index <= JSID_INT_MAX)
  4665         return GetPropertyPure(cx, obj, INT_TO_JSID(index), vp);
  4666     return false;
  4669 /*
  4670  * A pure version of GetObjectElementOperation that can be called from
  4671  * parallel code without locking. This variant returns false whenever a
  4672  * side-effect might have occurred.
  4673  */
  4674 bool
  4675 js::GetObjectElementOperationPure(ThreadSafeContext *cx, JSObject *obj, const Value &prop,
  4676                                   Value *vp)
  4678     uint32_t index;
  4679     if (IsDefinitelyIndex(prop, &index))
  4680         return GetElementPure(cx, obj, index, vp);
  4682     /* Atomizing the property value is effectful and not threadsafe. */
  4683     if (!prop.isString() || !prop.toString()->isAtom())
  4684         return false;
  4686     JSAtom *name = &prop.toString()->asAtom();
  4687     if (name->isIndex(&index))
  4688         return GetElementPure(cx, obj, index, vp);
  4690     return GetPropertyPure(cx, obj, NameToId(name->asPropertyName()), vp);
  4693 bool
  4694 baseops::GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
  4695                     MutableHandleValue vp)
  4697     RootedId id(cx);
  4698     if (!IndexToId(cx, index, &id))
  4699         return false;
  4701     /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
  4702     return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
  4705 static bool
  4706 MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
  4709         JSScript *script = cx->currentScript(nullptr, JSContext::ALLOW_CROSS_COMPARTMENT);
  4710         if (!script)
  4711             return true;
  4713         // If the code is not strict and extra warnings aren't enabled, then no
  4714         // check is needed.
  4715         if (!script->strict() && !cx->options().extraWarnings())
  4716             return true;
  4719     JSAutoByteString bytes(cx, propname);
  4720     return !!bytes &&
  4721            JS_ReportErrorFlagsAndNumber(cx,
  4722                                         (JSREPORT_WARNING | JSREPORT_STRICT
  4723                                          | JSREPORT_STRICT_MODE_ERROR),
  4724                                         js_GetErrorMessage, nullptr,
  4725                                         JSMSG_UNDECLARED_VAR, bytes.ptr());
  4728 bool
  4729 js::ReportIfUndeclaredVarAssignment(JSContext *cx, HandleString propname)
  4732         jsbytecode *pc;
  4733         JSScript *script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT);
  4734         if (!script)
  4735             return true;
  4737         // If the code is not strict and extra warnings aren't enabled, then no
  4738         // check is needed.
  4739         if (!script->strict() && !cx->options().extraWarnings())
  4740             return true;
  4742         /*
  4743          * We only need to check for bare name mutations: we shouldn't be
  4744          * warning, or throwing, or whatever, if we're not doing a variable
  4745          * access.
  4747          * TryConvertToGname in frontend/BytecodeEmitter.cpp checks for rather
  4748          * more opcodes when it does, in the normal course of events, what this
  4749          * method does in the abnormal course of events.  Because we're called
  4750          * in narrower circumstances, we only need check two.  We don't need to
  4751          * check for the increment/decrement opcodes because they're no-ops:
  4752          * the actual semantics are implemented by desugaring.  And we don't
  4753          * need to check name-access because this method is only supposed to be
  4754          * called in assignment contexts.
  4755          */
  4756         MOZ_ASSERT(*pc != JSOP_NAME);
  4757         MOZ_ASSERT(*pc != JSOP_GETGNAME);
  4758         if (*pc != JSOP_SETNAME && *pc != JSOP_SETGNAME)
  4759             return true;
  4762     JSAutoByteString bytes(cx, propname);
  4763     return !!bytes &&
  4764            JS_ReportErrorFlagsAndNumber(cx,
  4765                                         JSREPORT_WARNING | JSREPORT_STRICT |
  4766                                         JSREPORT_STRICT_MODE_ERROR,
  4767                                         js_GetErrorMessage, nullptr,
  4768                                         JSMSG_UNDECLARED_VAR, bytes.ptr());
  4771 bool
  4772 JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report)
  4774     if (cxArg->isForkJoinContext())
  4775         return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
  4777     if (!cxArg->isJSContext())
  4778         return true;
  4780     JSContext *cx = cxArg->asJSContext();
  4781     RootedValue val(cx, IdToValue(id));
  4782     return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
  4783                                     JSDVG_IGNORE_STACK, val, js::NullPtr(),
  4784                                     nullptr, nullptr);
  4787 bool
  4788 JSObject::reportNotConfigurable(ThreadSafeContext *cxArg, jsid id, unsigned report)
  4790     if (cxArg->isForkJoinContext())
  4791         return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
  4793     if (!cxArg->isJSContext())
  4794         return true;
  4796     JSContext *cx = cxArg->asJSContext();
  4797     RootedValue val(cx, IdToValue(id));
  4798     return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
  4799                                     JSDVG_IGNORE_STACK, val, js::NullPtr(),
  4800                                     nullptr, nullptr);
  4803 bool
  4804 JSObject::reportNotExtensible(ThreadSafeContext *cxArg, unsigned report)
  4806     if (cxArg->isForkJoinContext())
  4807         return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
  4809     if (!cxArg->isJSContext())
  4810         return true;
  4812     JSContext *cx = cxArg->asJSContext();
  4813     RootedValue val(cx, ObjectValue(*this));
  4814     return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
  4815                                     JSDVG_IGNORE_STACK, val, js::NullPtr(),
  4816                                     nullptr, nullptr);
  4819 bool
  4820 JSObject::callMethod(JSContext *cx, HandleId id, unsigned argc, Value *argv, MutableHandleValue vp)
  4822     RootedValue fval(cx);
  4823     RootedObject obj(cx, this);
  4824     if (!JSObject::getGeneric(cx, obj, obj, id, &fval))
  4825         return false;
  4826     return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
  4829 template <ExecutionMode mode>
  4830 bool
  4831 baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg,
  4832                            HandleObject obj, HandleObject receiver, HandleId id,
  4833                            QualifiedBool qualified, MutableHandleValue vp, bool strict)
  4835     JS_ASSERT(cxArg->isThreadLocal(obj));
  4837     if (MOZ_UNLIKELY(obj->watched())) {
  4838         if (mode == ParallelExecution)
  4839             return false;
  4841         /* Fire watchpoints, if any. */
  4842         JSContext *cx = cxArg->asJSContext();
  4843         WatchpointMap *wpmap = cx->compartment()->watchpointMap;
  4844         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
  4845             return false;
  4848     RootedObject pobj(cxArg);
  4849     RootedShape shape(cxArg);
  4850     if (mode == ParallelExecution) {
  4851         if (!LookupPropertyPure(obj, id, pobj.address(), shape.address()))
  4852             return false;
  4853     } else {
  4854         JSContext *cx = cxArg->asJSContext();
  4855         if (!LookupNativeProperty(cx, obj, id, &pobj, &shape))
  4856             return false;
  4858     if (shape) {
  4859         if (!pobj->isNative()) {
  4860             if (pobj->is<ProxyObject>()) {
  4861                 if (mode == ParallelExecution)
  4862                     return false;
  4864                 JSContext *cx = cxArg->asJSContext();
  4865                 Rooted<PropertyDescriptor> pd(cx);
  4866                 if (!Proxy::getPropertyDescriptor(cx, pobj, id, &pd))
  4867                     return false;
  4869                 if ((pd.attributes() & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
  4870                     return !pd.setter() ||
  4871                            CallSetter(cx, receiver, id, pd.setter(), pd.attributes(), strict, vp);
  4874                 if (pd.isReadonly()) {
  4875                     if (strict)
  4876                         return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
  4877                     if (cx->options().extraWarnings())
  4878                         return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
  4879                     return true;
  4883             shape = nullptr;
  4885     } else {
  4886         /* We should never add properties to lexical blocks. */
  4887         JS_ASSERT(!obj->is<BlockObject>());
  4889         if (obj->is<GlobalObject>() && !qualified) {
  4890             if (mode == ParallelExecution)
  4891                 return false;
  4893             if (!MaybeReportUndeclaredVarAssignment(cxArg->asJSContext(), JSID_TO_STRING(id)))
  4894                 return false;
  4898     /*
  4899      * Now either shape is null, meaning id was not found in obj or one of its
  4900      * prototypes; or shape is non-null, meaning id was found directly in pobj.
  4901      */
  4902     unsigned attrs = JSPROP_ENUMERATE;
  4903     const Class *clasp = obj->getClass();
  4904     PropertyOp getter = clasp->getProperty;
  4905     StrictPropertyOp setter = clasp->setProperty;
  4907     if (IsImplicitDenseOrTypedArrayElement(shape)) {
  4908         /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */
  4909         if (pobj != obj)
  4910             shape = nullptr;
  4911     } else if (shape) {
  4912         /* ES5 8.12.4 [[Put]] step 2. */
  4913         if (shape->isAccessorDescriptor()) {
  4914             if (shape->hasDefaultSetter()) {
  4915                 /* Bail out of parallel execution if we are strict to throw. */
  4916                 if (mode == ParallelExecution)
  4917                     return !strict;
  4919                 return js_ReportGetterOnlyAssignment(cxArg->asJSContext(), strict);
  4921         } else {
  4922             JS_ASSERT(shape->isDataDescriptor());
  4924             if (!shape->writable()) {
  4925                 /*
  4926                  * Error in strict mode code, warn with extra warnings
  4927                  * options, otherwise do nothing.
  4929                  * Bail out of parallel execution if we are strict to throw.
  4930                  */
  4931                 if (mode == ParallelExecution)
  4932                     return !strict;
  4934                 JSContext *cx = cxArg->asJSContext();
  4935                 if (strict)
  4936                     return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
  4937                 if (cx->options().extraWarnings())
  4938                     return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
  4939                 return true;
  4943         attrs = shape->attributes();
  4944         if (pobj != obj) {
  4945             /*
  4946              * We found id in a prototype object: prepare to share or shadow.
  4947              */
  4948             if (!shape->shadowable()) {
  4949                 if (shape->hasDefaultSetter() && !shape->hasGetterValue())
  4950                     return true;
  4952                 if (mode == ParallelExecution)
  4953                     return false;
  4955                 return shape->set(cxArg->asJSContext(), obj, receiver, strict, vp);
  4958             /*
  4959              * Preserve attrs except JSPROP_SHARED, getter, and setter when
  4960              * shadowing any property that has no slot (is shared). We must
  4961              * clear the shared attribute for the shadowing shape so that the
  4962              * property in obj that it defines has a slot to retain the value
  4963              * being set, in case the setter simply cannot operate on instances
  4964              * of obj's class by storing the value in some class-specific
  4965              * location.
  4966              */
  4967             if (!shape->hasSlot()) {
  4968                 attrs &= ~JSPROP_SHARED;
  4969                 getter = shape->getter();
  4970                 setter = shape->setter();
  4971             } else {
  4972                 /* Restore attrs to the ECMA default for new properties. */
  4973                 attrs = JSPROP_ENUMERATE;
  4976             /*
  4977              * Forget we found the proto-property now that we've copied any
  4978              * needed member values.
  4979              */
  4980             shape = nullptr;
  4984     if (IsImplicitDenseOrTypedArrayElement(shape)) {
  4985         uint32_t index = JSID_TO_INT(id);
  4987         if (obj->is<TypedArrayObject>()) {
  4988             double d;
  4989             if (mode == ParallelExecution) {
  4990                 // Bail if converting the value might invoke user-defined
  4991                 // conversions.
  4992                 if (vp.isObject())
  4993                     return false;
  4994                 if (!NonObjectToNumber(cxArg, vp, &d))
  4995                     return false;
  4996             } else {
  4997                 if (!ToNumber(cxArg->asJSContext(), vp, &d))
  4998                     return false;
  5001             // Silently do nothing for out-of-bounds sets, for consistency with
  5002             // current behavior.  (ES6 currently says to throw for this in
  5003             // strict mode code, so we may eventually need to change.)
  5004             TypedArrayObject &tarray = obj->as<TypedArrayObject>();
  5005             if (index < tarray.length())
  5006                 TypedArrayObject::setElement(tarray, index, d);
  5007             return true;
  5010         bool definesPast;
  5011         if (!WouldDefinePastNonwritableLength(cxArg, obj, index, strict, &definesPast))
  5012             return false;
  5013         if (definesPast) {
  5014             /* Bail out of parallel execution if we are strict to throw. */
  5015             if (mode == ParallelExecution)
  5016                 return !strict;
  5017             return true;
  5020         if (mode == ParallelExecution)
  5021             return obj->setDenseElementIfHasType(index, vp);
  5023         obj->setDenseElementWithType(cxArg->asJSContext(), index, vp);
  5024         return true;
  5027     if (obj->is<ArrayObject>() && id == NameToId(cxArg->names().length)) {
  5028         Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>());
  5029         return ArraySetLength<mode>(cxArg, arr, id, attrs, vp, strict);
  5032     if (!shape) {
  5033         bool extensible;
  5034         if (mode == ParallelExecution) {
  5035             if (obj->is<ProxyObject>())
  5036                 return false;
  5037             extensible = obj->nonProxyIsExtensible();
  5038         } else {
  5039             if (!JSObject::isExtensible(cxArg->asJSContext(), obj, &extensible))
  5040                 return false;
  5043         if (!extensible) {
  5044             /* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */
  5045             if (strict)
  5046                 return obj->reportNotExtensible(cxArg);
  5047             if (mode == SequentialExecution && cxArg->asJSContext()->options().extraWarnings())
  5048                 return obj->reportNotExtensible(cxArg, JSREPORT_STRICT | JSREPORT_WARNING);
  5049             return true;
  5052         if (mode == ParallelExecution) {
  5053             if (obj->isDelegate())
  5054                 return false;
  5056             if (getter != JS_PropertyStub || !HasTypePropertyId(obj, id, vp))
  5057                 return false;
  5058         } else {
  5059             JSContext *cx = cxArg->asJSContext();
  5061             /* Purge the property cache of now-shadowed id in obj's scope chain. */
  5062             if (!PurgeScopeChain(cx, obj, id))
  5063                 return false;
  5066         return DefinePropertyOrElement<mode>(cxArg, obj, id, getter, setter,
  5067                                              attrs, vp, true, strict);
  5070     return NativeSet<mode>(cxArg, obj, receiver, shape, strict, vp);
  5073 template bool
  5074 baseops::SetPropertyHelper<SequentialExecution>(JSContext *cx, HandleObject obj,
  5075                                                 HandleObject receiver, HandleId id,
  5076                                                 QualifiedBool qualified,
  5077                                                 MutableHandleValue vp, bool strict);
  5078 template bool
  5079 baseops::SetPropertyHelper<ParallelExecution>(ForkJoinContext *cx, HandleObject obj,
  5080                                               HandleObject receiver, HandleId id,
  5081                                               QualifiedBool qualified,
  5082                                               MutableHandleValue vp, bool strict);
  5084 bool
  5085 baseops::SetElementHelper(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
  5086                           MutableHandleValue vp, bool strict)
  5088     RootedId id(cx);
  5089     if (!IndexToId(cx, index, &id))
  5090         return false;
  5091     return baseops::SetPropertyHelper<SequentialExecution>(cx, obj, receiver, id, Qualified, vp,
  5092                                                            strict);
  5095 bool
  5096 baseops::GetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
  5098     RootedObject nobj(cx);
  5099     RootedShape shape(cx);
  5100     if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
  5101         return false;
  5102     if (!shape) {
  5103         *attrsp = 0;
  5104         return true;
  5106     if (!nobj->isNative())
  5107         return JSObject::getGenericAttributes(cx, nobj, id, attrsp);
  5109     *attrsp = GetShapeAttributes(nobj, shape);
  5110     return true;
  5113 bool
  5114 baseops::SetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
  5116     RootedObject nobj(cx);
  5117     RootedShape shape(cx);
  5118     if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
  5119         return false;
  5120     if (!shape)
  5121         return true;
  5122     if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) {
  5123         if (nobj->is<TypedArrayObject>()) {
  5124             if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT))
  5125                 return true;
  5126             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
  5127             return false;
  5129         if (!JSObject::sparsifyDenseElement(cx, nobj, JSID_TO_INT(id)))
  5130             return false;
  5131         shape = obj->nativeLookup(cx, id);
  5133     if (nobj->isNative()) {
  5134         if (!JSObject::changePropertyAttributes(cx, nobj, shape, *attrsp))
  5135             return false;
  5136         if (*attrsp & JSPROP_READONLY)
  5137             MarkTypePropertyNonWritable(cx, obj, id);
  5138         return true;
  5139     } else {
  5140         return JSObject::setGenericAttributes(cx, nobj, id, attrsp);
  5144 bool
  5145 baseops::DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
  5147     RootedObject proto(cx);
  5148     RootedShape shape(cx);
  5149     if (!baseops::LookupProperty<CanGC>(cx, obj, id, &proto, &shape))
  5150         return false;
  5151     if (!shape || proto != obj) {
  5152         /*
  5153          * If no property, or the property comes from a prototype, call the
  5154          * class's delProperty hook, passing succeeded as the result parameter.
  5155          */
  5156         return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
  5159     GCPoke(cx->runtime());
  5161     if (IsImplicitDenseOrTypedArrayElement(shape)) {
  5162         if (obj->is<TypedArrayObject>()) {
  5163             // Don't delete elements from typed arrays.
  5164             *succeeded = false;
  5165             return true;
  5168         if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
  5169             return false;
  5170         if (!succeeded)
  5171             return true;
  5173         obj->setDenseElementHole(cx, JSID_TO_INT(id));
  5174         return js_SuppressDeletedProperty(cx, obj, id);
  5177     if (!shape->configurable()) {
  5178         *succeeded = false;
  5179         return true;
  5182     RootedId propid(cx, shape->propid());
  5183     if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, propid, succeeded))
  5184         return false;
  5185     if (!succeeded)
  5186         return true;
  5188     return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
  5191 bool
  5192 baseops::DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
  5193                         bool *succeeded)
  5195     Rooted<jsid> id(cx, NameToId(name));
  5196     return baseops::DeleteGeneric(cx, obj, id, succeeded);
  5199 bool
  5200 baseops::DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded)
  5202     RootedId id(cx);
  5203     if (!IndexToId(cx, index, &id))
  5204         return false;
  5205     return baseops::DeleteGeneric(cx, obj, id, succeeded);
  5208 bool
  5209 js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
  5211     RootedObject obj(cx, GetInnerObject(cx, origObj));
  5212     if (obj->isNative()) {
  5213         // Use sparse indexes for watched objects, as dense elements can be
  5214         // written to without checking the watchpoint map.
  5215         if (!JSObject::sparsifyDenseElements(cx, obj))
  5216             return false;
  5218         types::MarkTypePropertyNonData(cx, obj, id);
  5221     WatchpointMap *wpmap = cx->compartment()->watchpointMap;
  5222     if (!wpmap) {
  5223         wpmap = cx->runtime()->new_<WatchpointMap>();
  5224         if (!wpmap || !wpmap->init()) {
  5225             js_ReportOutOfMemory(cx);
  5226             return false;
  5228         cx->compartment()->watchpointMap = wpmap;
  5231     return wpmap->watch(cx, obj, id, js::WatchHandler, callable);
  5234 bool
  5235 baseops::Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable)
  5237     if (!obj->isNative() || obj->is<TypedArrayObject>()) {
  5238         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
  5239                              obj->getClass()->name);
  5240         return false;
  5243     return WatchGuts(cx, obj, id, callable);
  5246 bool
  5247 js::UnwatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id)
  5249     // Looking in the map for an unsupported object will never hit, so we don't
  5250     // need to check for nativeness or watchable-ness here.
  5251     RootedObject obj(cx, GetInnerObject(cx, origObj));
  5252     if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
  5253         wpmap->unwatch(obj, id, nullptr, nullptr);
  5254     return true;
  5257 bool
  5258 baseops::Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
  5260     return UnwatchGuts(cx, obj, id);
  5263 bool
  5264 js::HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
  5266     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
  5267         *vp = obj->getDenseElement(JSID_TO_INT(id));
  5268         return true;
  5271     if (Shape *shape = obj->nativeLookup(cx, id)) {
  5272         if (shape->hasDefaultGetter() && shape->hasSlot()) {
  5273             *vp = obj->nativeGetSlot(shape->slot());
  5274             return true;
  5278     return false;
  5281 /*
  5282  * Gets |obj[id]|.  If that value's not callable, returns true and stores a
  5283  * non-primitive value in *vp.  If it's callable, calls it with no arguments
  5284  * and |obj| as |this|, returning the result in *vp.
  5286  * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
  5287  * or steps 3-4.
  5288  */
  5289 static bool
  5290 MaybeCallMethod(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
  5292     if (!JSObject::getGeneric(cx, obj, obj, id, vp))
  5293         return false;
  5294     if (!js_IsCallable(vp)) {
  5295         vp.setObject(*obj);
  5296         return true;
  5298     return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp);
  5301 JS_FRIEND_API(bool)
  5302 js::DefaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
  5304     JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
  5306     Rooted<jsid> id(cx);
  5308     const Class *clasp = obj->getClass();
  5309     if (hint == JSTYPE_STRING) {
  5310         id = NameToId(cx->names().toString);
  5312         /* Optimize (new String(...)).toString(). */
  5313         if (clasp == &StringObject::class_) {
  5314             if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) {
  5315                 vp.setString(obj->as<StringObject>().unbox());
  5316                 return true;
  5320         if (!MaybeCallMethod(cx, obj, id, vp))
  5321             return false;
  5322         if (vp.isPrimitive())
  5323             return true;
  5325         id = NameToId(cx->names().valueOf);
  5326         if (!MaybeCallMethod(cx, obj, id, vp))
  5327             return false;
  5328         if (vp.isPrimitive())
  5329             return true;
  5330     } else {
  5332         /* Optimize new String(...).valueOf(). */
  5333         if (clasp == &StringObject::class_) {
  5334             id = NameToId(cx->names().valueOf);
  5335             if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) {
  5336                 vp.setString(obj->as<StringObject>().unbox());
  5337                 return true;
  5341         /* Optimize new Number(...).valueOf(). */
  5342         if (clasp == &NumberObject::class_) {
  5343             id = NameToId(cx->names().valueOf);
  5344             if (ClassMethodIsNative(cx, obj, &NumberObject::class_, id, js_num_valueOf)) {
  5345                 vp.setNumber(obj->as<NumberObject>().unbox());
  5346                 return true;
  5350         id = NameToId(cx->names().valueOf);
  5351         if (!MaybeCallMethod(cx, obj, id, vp))
  5352             return false;
  5353         if (vp.isPrimitive())
  5354             return true;
  5356         id = NameToId(cx->names().toString);
  5357         if (!MaybeCallMethod(cx, obj, id, vp))
  5358             return false;
  5359         if (vp.isPrimitive())
  5360             return true;
  5363     /* Avoid recursive death when decompiling in js_ReportValueError. */
  5364     RootedString str(cx);
  5365     if (hint == JSTYPE_STRING) {
  5366         str = JS_InternString(cx, clasp->name);
  5367         if (!str)
  5368             return false;
  5369     } else {
  5370         str = nullptr;
  5373     RootedValue val(cx, ObjectValue(*obj));
  5374     js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, JSDVG_SEARCH_STACK, val, str,
  5375                          (hint == JSTYPE_VOID) ? "primitive type" : TypeStrings[hint]);
  5376     return false;
  5379 JS_FRIEND_API(bool)
  5380 JS_EnumerateState(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
  5381                   MutableHandleValue statep, JS::MutableHandleId idp)
  5383     /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
  5384     const Class *clasp = obj->getClass();
  5385     JSEnumerateOp enumerate = clasp->enumerate;
  5386     if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
  5387         JS_ASSERT(enumerate != JS_EnumerateStub);
  5388         return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
  5391     if (!enumerate(cx, obj))
  5392         return false;
  5394     /* Tell InitNativeIterator to treat us like a native object. */
  5395     JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
  5396     statep.setMagic(JS_NATIVE_ENUMERATE);
  5397     return true;
  5400 bool
  5401 js::IsDelegate(JSContext *cx, HandleObject obj, const js::Value &v, bool *result)
  5403     if (v.isPrimitive()) {
  5404         *result = false;
  5405         return true;
  5407     return IsDelegateOfObject(cx, obj, &v.toObject(), result);
  5410 bool
  5411 js::IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result)
  5413     RootedObject obj2(cx, obj);
  5414     for (;;) {
  5415         if (!JSObject::getProto(cx, obj2, &obj2))
  5416             return false;
  5417         if (!obj2) {
  5418             *result = false;
  5419             return true;
  5421         if (obj2 == protoObj) {
  5422             *result = true;
  5423             return true;
  5428 JSObject *
  5429 js::GetBuiltinPrototypePure(GlobalObject *global, JSProtoKey protoKey)
  5431     JS_ASSERT(JSProto_Null <= protoKey);
  5432     JS_ASSERT(protoKey < JSProto_LIMIT);
  5434     if (protoKey != JSProto_Null) {
  5435         const Value &v = global->getPrototype(protoKey);
  5436         if (v.isObject())
  5437             return &v.toObject();
  5440     return nullptr;
  5443 /*
  5444  * The first part of this function has been hand-expanded and optimized into
  5445  * NewBuiltinClassInstance in jsobjinlines.h.
  5446  */
  5447 bool
  5448 js::FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp)
  5450     protop.set(nullptr);
  5451     JSProtoKey protoKey = GetClassProtoKey(clasp);
  5452     if (protoKey != JSProto_Null)
  5453         return GetBuiltinPrototype(cx, protoKey, protop);
  5455     RootedObject ctor(cx);
  5456     if (!FindClassObject(cx, &ctor, clasp))
  5457         return false;
  5459     if (ctor && ctor->is<JSFunction>()) {
  5460         RootedValue v(cx);
  5461         if (cx->isJSContext()) {
  5462             if (!JSObject::getProperty(cx->asJSContext(),
  5463                                        ctor, ctor, cx->names().prototype, &v))
  5465                 return false;
  5467         } else {
  5468             Shape *shape = ctor->nativeLookup(cx, cx->names().prototype);
  5469             if (!shape || !NativeGetPureInline(ctor, shape, v.address()))
  5470                 return false;
  5472         if (v.isObject())
  5473             protop.set(&v.toObject());
  5475     return true;
  5478 JSObject *
  5479 js::PrimitiveToObject(JSContext *cx, const Value &v)
  5481     if (v.isString()) {
  5482         Rooted<JSString*> str(cx, v.toString());
  5483         return StringObject::create(cx, str);
  5485     if (v.isNumber())
  5486         return NumberObject::create(cx, v.toNumber());
  5488     JS_ASSERT(v.isBoolean());
  5489     return BooleanObject::create(cx, v.toBoolean());
  5492 /* Callers must handle the already-object case . */
  5493 JSObject *
  5494 js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
  5496     JS_ASSERT(!val.isMagic());
  5497     JS_ASSERT(!val.isObject());
  5499     if (val.isNullOrUndefined()) {
  5500         if (reportScanStack) {
  5501             js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val, NullPtr());
  5502         } else {
  5503             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
  5504                                  val.isNull() ? "null" : "undefined", "object");
  5506         return nullptr;
  5509     return PrimitiveToObject(cx, val);
  5512 void
  5513 js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
  5515     JS_ASSERT(trc->debugPrinter() == js_GetObjectSlotName);
  5517     JSObject *obj = (JSObject *)trc->debugPrintArg();
  5518     uint32_t slot = uint32_t(trc->debugPrintIndex());
  5520     Shape *shape;
  5521     if (obj->isNative()) {
  5522         shape = obj->lastProperty();
  5523         while (shape && (!shape->hasSlot() || shape->slot() != slot))
  5524             shape = shape->previous();
  5525     } else {
  5526         shape = nullptr;
  5529     if (!shape) {
  5530         const char *slotname = nullptr;
  5531         if (obj->is<GlobalObject>()) {
  5532 #define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init,clasp)                     \
  5533             if ((code) == slot) { slotname = js_##name##_str; goto found; }
  5534             JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
  5535 #undef TEST_SLOT_MATCHES_PROTOTYPE
  5537       found:
  5538         if (slotname)
  5539             JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
  5540         else
  5541             JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
  5542     } else {
  5543         jsid propid = shape->propid();
  5544         if (JSID_IS_INT(propid)) {
  5545             JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid));
  5546         } else if (JSID_IS_ATOM(propid)) {
  5547             PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
  5548         } else {
  5549             JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
  5554 bool
  5555 js_ReportGetterOnlyAssignment(JSContext *cx, bool strict)
  5557     return JS_ReportErrorFlagsAndNumber(cx,
  5558                                         strict
  5559                                         ? JSREPORT_ERROR
  5560                                         : JSREPORT_WARNING | JSREPORT_STRICT,
  5561                                         js_GetErrorMessage, nullptr,
  5562                                         JSMSG_GETTER_ONLY);
  5565 JS_FRIEND_API(bool)
  5566 js_GetterOnlyPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
  5567                           MutableHandleValue vp)
  5569     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
  5570     return false;
  5573 #ifdef DEBUG
  5575 /*
  5576  * Routines to print out values during debugging.  These are FRIEND_API to help
  5577  * the debugger find them and to support temporarily hacking js_Dump* calls
  5578  * into other code.
  5579  */
  5581 static void
  5582 dumpValue(const Value &v)
  5584     if (v.isNull())
  5585         fprintf(stderr, "null");
  5586     else if (v.isUndefined())
  5587         fprintf(stderr, "undefined");
  5588     else if (v.isInt32())
  5589         fprintf(stderr, "%d", v.toInt32());
  5590     else if (v.isDouble())
  5591         fprintf(stderr, "%g", v.toDouble());
  5592     else if (v.isString())
  5593         v.toString()->dump();
  5594     else if (v.isObject() && v.toObject().is<JSFunction>()) {
  5595         JSFunction *fun = &v.toObject().as<JSFunction>();
  5596         if (fun->displayAtom()) {
  5597             fputs("<function ", stderr);
  5598             FileEscapedString(stderr, fun->displayAtom(), 0);
  5599         } else {
  5600             fputs("<unnamed function", stderr);
  5602         if (fun->hasScript()) {
  5603             JSScript *script = fun->nonLazyScript();
  5604             fprintf(stderr, " (%s:%d)",
  5605                     script->filename() ? script->filename() : "", (int) script->lineno());
  5607         fprintf(stderr, " at %p>", (void *) fun);
  5608     } else if (v.isObject()) {
  5609         JSObject *obj = &v.toObject();
  5610         const Class *clasp = obj->getClass();
  5611         fprintf(stderr, "<%s%s at %p>",
  5612                 clasp->name,
  5613                 (clasp == &JSObject::class_) ? "" : " object",
  5614                 (void *) obj);
  5615     } else if (v.isBoolean()) {
  5616         if (v.toBoolean())
  5617             fprintf(stderr, "true");
  5618         else
  5619             fprintf(stderr, "false");
  5620     } else if (v.isMagic()) {
  5621         fprintf(stderr, "<invalid");
  5622 #ifdef DEBUG
  5623         switch (v.whyMagic()) {
  5624           case JS_ELEMENTS_HOLE:     fprintf(stderr, " elements hole");      break;
  5625           case JS_NATIVE_ENUMERATE:  fprintf(stderr, " native enumeration"); break;
  5626           case JS_NO_ITER_VALUE:     fprintf(stderr, " no iter value");      break;
  5627           case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing");  break;
  5628           case JS_OPTIMIZED_OUT:     fprintf(stderr, " optimized out");      break;
  5629           default:                   fprintf(stderr, " ?!");                 break;
  5631 #endif
  5632         fprintf(stderr, ">");
  5633     } else {
  5634         fprintf(stderr, "unexpected value");
  5638 JS_FRIEND_API(void)
  5639 js_DumpValue(const Value &val)
  5641     dumpValue(val);
  5642     fputc('\n', stderr);
  5645 JS_FRIEND_API(void)
  5646 js_DumpId(jsid id)
  5648     fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
  5649     dumpValue(IdToValue(id));
  5650     fputc('\n', stderr);
  5653 static void
  5654 DumpProperty(JSObject *obj, Shape &shape)
  5656     jsid id = shape.propid();
  5657     uint8_t attrs = shape.attributes();
  5659     fprintf(stderr, "    ((JSShape *) %p) ", (void *) &shape);
  5660     if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
  5661     if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
  5662     if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
  5663     if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
  5665     if (shape.hasGetterValue())
  5666         fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject());
  5667     else if (!shape.hasDefaultGetter())
  5668         fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.getterOp()));
  5670     if (shape.hasSetterValue())
  5671         fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject());
  5672     else if (!shape.hasDefaultSetter())
  5673         fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp()));
  5675     if (JSID_IS_ATOM(id))
  5676         JSID_TO_STRING(id)->dump();
  5677     else if (JSID_IS_INT(id))
  5678         fprintf(stderr, "%d", (int) JSID_TO_INT(id));
  5679     else
  5680         fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
  5682     uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT;
  5683     fprintf(stderr, ": slot %d", slot);
  5684     if (shape.hasSlot()) {
  5685         fprintf(stderr, " = ");
  5686         dumpValue(obj->getSlot(slot));
  5687     } else if (slot != SHAPE_INVALID_SLOT) {
  5688         fprintf(stderr, " (INVALID!)");
  5690     fprintf(stderr, "\n");
  5693 bool
  5694 JSObject::uninlinedIsProxy() const
  5696     return is<ProxyObject>();
  5699 void
  5700 JSObject::dump()
  5702     JSObject *obj = this;
  5703     fprintf(stderr, "object %p\n", (void *) obj);
  5704     const Class *clasp = obj->getClass();
  5705     fprintf(stderr, "class %p %s\n", (const void *)clasp, clasp->name);
  5707     fprintf(stderr, "flags:");
  5708     if (obj->isDelegate()) fprintf(stderr, " delegate");
  5709     if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) fprintf(stderr, " not_extensible");
  5710     if (obj->isIndexed()) fprintf(stderr, " indexed");
  5711     if (obj->isBoundFunction()) fprintf(stderr, " bound_function");
  5712     if (obj->isVarObj()) fprintf(stderr, " varobj");
  5713     if (obj->watched()) fprintf(stderr, " watched");
  5714     if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton");
  5715     if (obj->isNewTypeUnknown()) fprintf(stderr, " new_type_unknown");
  5716     if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto");
  5717     if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access");
  5719     if (obj->isNative()) {
  5720         if (obj->inDictionaryMode())
  5721             fprintf(stderr, " inDictionaryMode");
  5722         if (obj->hasShapeTable())
  5723             fprintf(stderr, " hasShapeTable");
  5725     fprintf(stderr, "\n");
  5727     if (obj->isNative()) {
  5728         uint32_t slots = obj->getDenseInitializedLength();
  5729         if (slots) {
  5730             fprintf(stderr, "elements\n");
  5731             for (uint32_t i = 0; i < slots; i++) {
  5732                 fprintf(stderr, " %3d: ", i);
  5733                 dumpValue(obj->getDenseElement(i));
  5734                 fprintf(stderr, "\n");
  5735                 fflush(stderr);
  5740     fprintf(stderr, "proto ");
  5741     TaggedProto proto = obj->getTaggedProto();
  5742     if (proto.isLazy())
  5743         fprintf(stderr, "<lazy>");
  5744     else
  5745         dumpValue(ObjectOrNullValue(proto.toObjectOrNull()));
  5746     fputc('\n', stderr);
  5748     fprintf(stderr, "parent ");
  5749     dumpValue(ObjectOrNullValue(obj->getParent()));
  5750     fputc('\n', stderr);
  5752     if (clasp->flags & JSCLASS_HAS_PRIVATE)
  5753         fprintf(stderr, "private %p\n", obj->getPrivate());
  5755     if (!obj->isNative())
  5756         fprintf(stderr, "not native\n");
  5758     uint32_t reservedEnd = JSCLASS_RESERVED_SLOTS(clasp);
  5759     uint32_t slots = obj->slotSpan();
  5760     uint32_t stop = obj->isNative() ? reservedEnd : slots;
  5761     if (stop > 0)
  5762         fprintf(stderr, obj->isNative() ? "reserved slots:\n" : "slots:\n");
  5763     for (uint32_t i = 0; i < stop; i++) {
  5764         fprintf(stderr, " %3d ", i);
  5765         if (i < reservedEnd)
  5766             fprintf(stderr, "(reserved) ");
  5767         fprintf(stderr, "= ");
  5768         dumpValue(obj->getSlot(i));
  5769         fputc('\n', stderr);
  5772     if (obj->isNative()) {
  5773         fprintf(stderr, "properties:\n");
  5774         Vector<Shape *, 8, SystemAllocPolicy> props;
  5775         for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
  5776             props.append(&r.front());
  5777         for (size_t i = props.length(); i-- != 0;)
  5778             DumpProperty(obj, *props[i]);
  5780     fputc('\n', stderr);
  5783 static void
  5784 MaybeDumpObject(const char *name, JSObject *obj)
  5786     if (obj) {
  5787         fprintf(stderr, "  %s: ", name);
  5788         dumpValue(ObjectValue(*obj));
  5789         fputc('\n', stderr);
  5793 static void
  5794 MaybeDumpValue(const char *name, const Value &v)
  5796     if (!v.isNull()) {
  5797         fprintf(stderr, "  %s: ", name);
  5798         dumpValue(v);
  5799         fputc('\n', stderr);
  5803 JS_FRIEND_API(void)
  5804 js_DumpInterpreterFrame(JSContext *cx, InterpreterFrame *start)
  5806     /* This should only called during live debugging. */
  5807     ScriptFrameIter i(cx, ScriptFrameIter::GO_THROUGH_SAVED);
  5808     if (!start) {
  5809         if (i.done()) {
  5810             fprintf(stderr, "no stack for cx = %p\n", (void*) cx);
  5811             return;
  5813     } else {
  5814         while (!i.done() && !i.isJit() && i.interpFrame() != start)
  5815             ++i;
  5817         if (i.done()) {
  5818             fprintf(stderr, "fp = %p not found in cx = %p\n",
  5819                     (void *)start, (void *)cx);
  5820             return;
  5824     for (; !i.done(); ++i) {
  5825         if (i.isJit())
  5826             fprintf(stderr, "JIT frame\n");
  5827         else
  5828             fprintf(stderr, "InterpreterFrame at %p\n", (void *) i.interpFrame());
  5830         if (i.isFunctionFrame()) {
  5831             fprintf(stderr, "callee fun: ");
  5832             dumpValue(i.calleev());
  5833         } else {
  5834             fprintf(stderr, "global frame, no callee");
  5836         fputc('\n', stderr);
  5838         fprintf(stderr, "file %s line %u\n",
  5839                 i.script()->filename(), (unsigned) i.script()->lineno());
  5841         if (jsbytecode *pc = i.pc()) {
  5842             fprintf(stderr, "  pc = %p\n", pc);
  5843             fprintf(stderr, "  current op: %s\n", js_CodeName[*pc]);
  5844             MaybeDumpObject("staticScope", i.script()->getStaticScope(pc));
  5846         MaybeDumpValue("this", i.thisv());
  5847         if (!i.isJit()) {
  5848             fprintf(stderr, "  rval: ");
  5849             dumpValue(i.interpFrame()->returnValue());
  5850             fputc('\n', stderr);
  5853         fprintf(stderr, "  flags:");
  5854         if (i.isConstructing())
  5855             fprintf(stderr, " constructing");
  5856         if (!i.isJit() && i.interpFrame()->isDebuggerFrame())
  5857             fprintf(stderr, " debugger");
  5858         if (i.isEvalFrame())
  5859             fprintf(stderr, " eval");
  5860         if (!i.isJit() && i.interpFrame()->isYielding())
  5861             fprintf(stderr, " yielding");
  5862         if (!i.isJit() && i.interpFrame()->isGeneratorFrame())
  5863             fprintf(stderr, " generator");
  5864         fputc('\n', stderr);
  5866         fprintf(stderr, "  scopeChain: (JSObject *) %p\n", (void *) i.scopeChain());
  5868         fputc('\n', stderr);
  5872 #endif /* DEBUG */
  5874 JS_FRIEND_API(void)
  5875 js_DumpBacktrace(JSContext *cx)
  5877     Sprinter sprinter(cx);
  5878     sprinter.init();
  5879     size_t depth = 0;
  5880     for (ScriptFrameIter i(cx); !i.done(); ++i, ++depth) {
  5881         const char *filename = JS_GetScriptFilename(i.script());
  5882         unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
  5883         JSScript *script = i.script();
  5884         sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
  5885                         depth, (i.isJit() ? 0 : i.interpFrame()), filename, line,
  5886                         script, script->pcToOffset(i.pc()));
  5888     fprintf(stdout, "%s", sprinter.string());
  5890 void
  5891 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
  5893     if (hasDynamicSlots())
  5894         sizes->mallocHeapSlots += mallocSizeOf(slots);
  5896     if (hasDynamicElements()) {
  5897         js::ObjectElements *elements = getElementsHeader();
  5898         sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
  5901     // Other things may be measured in the future if DMD indicates it is worthwhile.
  5902     if (is<JSFunction>() ||
  5903         is<JSObject>() ||
  5904         is<ArrayObject>() ||
  5905         is<CallObject>() ||
  5906         is<RegExpObject>() ||
  5907         is<ProxyObject>())
  5909         // Do nothing.  But this function is hot, and we win by getting the
  5910         // common cases out of the way early.  Some stats on the most common
  5911         // classes, as measured during a vanilla browser session:
  5912         // - (53.7%, 53.7%): Function
  5913         // - (18.0%, 71.7%): Object
  5914         // - (16.9%, 88.6%): Array
  5915         // - ( 3.9%, 92.5%): Call
  5916         // - ( 2.8%, 95.3%): RegExp
  5917         // - ( 1.0%, 96.4%): Proxy
  5919     } else if (is<ArgumentsObject>()) {
  5920         sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
  5921     } else if (is<RegExpStaticsObject>()) {
  5922         sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
  5923     } else if (is<PropertyIteratorObject>()) {
  5924         sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
  5925     } else if (is<ArrayBufferObject>() || is<SharedArrayBufferObject>()) {
  5926         ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, sizes);
  5927 #ifdef JS_ION
  5928     } else if (is<AsmJSModuleObject>()) {
  5929         as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS,
  5930                                               &sizes->mallocHeapAsmJSModuleData);
  5931 #endif
  5932 #ifdef JS_HAS_CTYPES
  5933     } else {
  5934         // This must be the last case.
  5935         sizes->mallocHeapCtypesData +=
  5936             js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
  5937 #endif

mercurial