js/src/jsobj.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial