1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsobj.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,5939 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * JS object implementation. 1.12 + */ 1.13 + 1.14 +#include "jsobjinlines.h" 1.15 + 1.16 +#include "mozilla/ArrayUtils.h" 1.17 +#include "mozilla/MathAlgorithms.h" 1.18 +#include "mozilla/MemoryReporting.h" 1.19 +#include "mozilla/TemplateLib.h" 1.20 + 1.21 +#include <string.h> 1.22 + 1.23 +#include "jsapi.h" 1.24 +#include "jsarray.h" 1.25 +#include "jsatom.h" 1.26 +#include "jscntxt.h" 1.27 +#include "jsfriendapi.h" 1.28 +#include "jsfun.h" 1.29 +#include "jsgc.h" 1.30 +#include "jsiter.h" 1.31 +#include "jsnum.h" 1.32 +#include "jsopcode.h" 1.33 +#include "jsprf.h" 1.34 +#include "jsproxy.h" 1.35 +#include "jsscript.h" 1.36 +#include "jsstr.h" 1.37 +#include "jstypes.h" 1.38 +#include "jsutil.h" 1.39 +#include "jswatchpoint.h" 1.40 +#include "jswrapper.h" 1.41 + 1.42 +#include "builtin/Object.h" 1.43 +#include "frontend/BytecodeCompiler.h" 1.44 +#include "gc/Marking.h" 1.45 +#include "jit/AsmJSModule.h" 1.46 +#include "jit/BaselineJIT.h" 1.47 +#include "js/MemoryMetrics.h" 1.48 +#include "js/OldDebugAPI.h" 1.49 +#include "vm/ArgumentsObject.h" 1.50 +#include "vm/Interpreter.h" 1.51 +#include "vm/ProxyObject.h" 1.52 +#include "vm/RegExpStaticsObject.h" 1.53 +#include "vm/Shape.h" 1.54 + 1.55 +#include "jsatominlines.h" 1.56 +#include "jsboolinlines.h" 1.57 +#include "jscntxtinlines.h" 1.58 +#include "jscompartmentinlines.h" 1.59 + 1.60 +#include "vm/ArrayObject-inl.h" 1.61 +#include "vm/BooleanObject-inl.h" 1.62 +#include "vm/NumberObject-inl.h" 1.63 +#include "vm/ObjectImpl-inl.h" 1.64 +#include "vm/Runtime-inl.h" 1.65 +#include "vm/Shape-inl.h" 1.66 +#include "vm/StringObject-inl.h" 1.67 + 1.68 +using namespace js; 1.69 +using namespace js::gc; 1.70 +using namespace js::types; 1.71 + 1.72 +using mozilla::Maybe; 1.73 +using mozilla::RoundUpPow2; 1.74 + 1.75 +JS_STATIC_ASSERT(int32_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) == int64_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value))); 1.76 + 1.77 +const Class JSObject::class_ = { 1.78 + js_Object_str, 1.79 + JSCLASS_HAS_CACHED_PROTO(JSProto_Object), 1.80 + JS_PropertyStub, /* addProperty */ 1.81 + JS_DeletePropertyStub, /* delProperty */ 1.82 + JS_PropertyStub, /* getProperty */ 1.83 + JS_StrictPropertyStub, /* setProperty */ 1.84 + JS_EnumerateStub, 1.85 + JS_ResolveStub, 1.86 + JS_ConvertStub 1.87 +}; 1.88 + 1.89 +const Class* const js::ObjectClassPtr = &JSObject::class_; 1.90 + 1.91 +JS_FRIEND_API(JSObject *) 1.92 +JS_ObjectToInnerObject(JSContext *cx, HandleObject obj) 1.93 +{ 1.94 + if (!obj) { 1.95 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INACTIVE); 1.96 + return nullptr; 1.97 + } 1.98 + return GetInnerObject(cx, obj); 1.99 +} 1.100 + 1.101 +JS_FRIEND_API(JSObject *) 1.102 +JS_ObjectToOuterObject(JSContext *cx, HandleObject obj) 1.103 +{ 1.104 + assertSameCompartment(cx, obj); 1.105 + return GetOuterObject(cx, obj); 1.106 +} 1.107 + 1.108 +JSObject * 1.109 +js::NonNullObject(JSContext *cx, const Value &v) 1.110 +{ 1.111 + if (v.isPrimitive()) { 1.112 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT); 1.113 + return nullptr; 1.114 + } 1.115 + return &v.toObject(); 1.116 +} 1.117 + 1.118 +const char * 1.119 +js::InformalValueTypeName(const Value &v) 1.120 +{ 1.121 + if (v.isObject()) 1.122 + return v.toObject().getClass()->name; 1.123 + if (v.isString()) 1.124 + return "string"; 1.125 + if (v.isNumber()) 1.126 + return "number"; 1.127 + if (v.isBoolean()) 1.128 + return "boolean"; 1.129 + if (v.isNull()) 1.130 + return "null"; 1.131 + if (v.isUndefined()) 1.132 + return "undefined"; 1.133 + return "value"; 1.134 +} 1.135 + 1.136 +bool 1.137 +js::NewPropertyDescriptorObject(JSContext *cx, Handle<PropertyDescriptor> desc, 1.138 + MutableHandleValue vp) 1.139 +{ 1.140 + if (!desc.object()) { 1.141 + vp.setUndefined(); 1.142 + return true; 1.143 + } 1.144 + 1.145 + /* We have our own property, so start creating the descriptor. */ 1.146 + AutoPropDescRooter d(cx); 1.147 + 1.148 + d.initFromPropertyDescriptor(desc); 1.149 + if (!d.makeObject(cx)) 1.150 + return false; 1.151 + vp.set(d.pd()); 1.152 + return true; 1.153 +} 1.154 + 1.155 +void 1.156 +PropDesc::initFromPropertyDescriptor(Handle<PropertyDescriptor> desc) 1.157 +{ 1.158 + isUndefined_ = false; 1.159 + pd_.setUndefined(); 1.160 + attrs = uint8_t(desc.attributes()); 1.161 + JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER))); 1.162 + if (desc.hasGetterOrSetterObject()) { 1.163 + hasGet_ = true; 1.164 + get_ = desc.hasGetterObject() && desc.getterObject() 1.165 + ? ObjectValue(*desc.getterObject()) 1.166 + : UndefinedValue(); 1.167 + hasSet_ = true; 1.168 + set_ = desc.hasSetterObject() && desc.setterObject() 1.169 + ? ObjectValue(*desc.setterObject()) 1.170 + : UndefinedValue(); 1.171 + hasValue_ = false; 1.172 + value_.setUndefined(); 1.173 + hasWritable_ = false; 1.174 + } else { 1.175 + hasGet_ = false; 1.176 + get_.setUndefined(); 1.177 + hasSet_ = false; 1.178 + set_.setUndefined(); 1.179 + hasValue_ = true; 1.180 + value_ = desc.value(); 1.181 + hasWritable_ = true; 1.182 + } 1.183 + hasEnumerable_ = true; 1.184 + hasConfigurable_ = true; 1.185 +} 1.186 + 1.187 +bool 1.188 +PropDesc::makeObject(JSContext *cx) 1.189 +{ 1.190 + MOZ_ASSERT(!isUndefined()); 1.191 + 1.192 + RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); 1.193 + if (!obj) 1.194 + return false; 1.195 + 1.196 + const JSAtomState &names = cx->names(); 1.197 + RootedValue configurableVal(cx, BooleanValue((attrs & JSPROP_PERMANENT) == 0)); 1.198 + RootedValue enumerableVal(cx, BooleanValue((attrs & JSPROP_ENUMERATE) != 0)); 1.199 + RootedValue writableVal(cx, BooleanValue((attrs & JSPROP_READONLY) == 0)); 1.200 + if ((hasConfigurable() && 1.201 + !JSObject::defineProperty(cx, obj, names.configurable, configurableVal)) || 1.202 + (hasEnumerable() && 1.203 + !JSObject::defineProperty(cx, obj, names.enumerable, enumerableVal)) || 1.204 + (hasGet() && 1.205 + !JSObject::defineProperty(cx, obj, names.get, getterValue())) || 1.206 + (hasSet() && 1.207 + !JSObject::defineProperty(cx, obj, names.set, setterValue())) || 1.208 + (hasValue() && 1.209 + !JSObject::defineProperty(cx, obj, names.value, value())) || 1.210 + (hasWritable() && 1.211 + !JSObject::defineProperty(cx, obj, names.writable, writableVal))) 1.212 + { 1.213 + return false; 1.214 + } 1.215 + 1.216 + pd_.setObject(*obj); 1.217 + return true; 1.218 +} 1.219 + 1.220 +bool 1.221 +js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, 1.222 + MutableHandle<PropertyDescriptor> desc) 1.223 +{ 1.224 + // FIXME: Call TrapGetOwnProperty directly once ScriptedIndirectProxies is removed 1.225 + if (obj->is<ProxyObject>()) 1.226 + return Proxy::getOwnPropertyDescriptor(cx, obj, id, desc); 1.227 + 1.228 + RootedObject pobj(cx); 1.229 + RootedShape shape(cx); 1.230 + if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape)) 1.231 + return false; 1.232 + if (!shape) { 1.233 + desc.object().set(nullptr); 1.234 + return true; 1.235 + } 1.236 + 1.237 + bool doGet = true; 1.238 + if (pobj->isNative()) { 1.239 + desc.setAttributes(GetShapeAttributes(pobj, shape)); 1.240 + if (desc.hasGetterOrSetterObject()) { 1.241 + MOZ_ASSERT(desc.isShared()); 1.242 + doGet = false; 1.243 + if (desc.hasGetterObject()) 1.244 + desc.setGetterObject(shape->getterObject()); 1.245 + if (desc.hasSetterObject()) 1.246 + desc.setSetterObject(shape->setterObject()); 1.247 + } else { 1.248 + // This is either a straight-up data property or (rarely) a 1.249 + // property with a JSPropertyOp getter/setter. The latter must be 1.250 + // reported to the caller as a plain data property, so don't 1.251 + // populate desc.getter/setter, and mask away the SHARED bit. 1.252 + desc.attributesRef() &= ~JSPROP_SHARED; 1.253 + } 1.254 + } else { 1.255 + if (!JSObject::getGenericAttributes(cx, pobj, id, &desc.attributesRef())) 1.256 + return false; 1.257 + } 1.258 + 1.259 + RootedValue value(cx); 1.260 + if (doGet && !JSObject::getGeneric(cx, obj, obj, id, &value)) 1.261 + return false; 1.262 + 1.263 + desc.value().set(value); 1.264 + desc.object().set(obj); 1.265 + return true; 1.266 +} 1.267 + 1.268 +bool 1.269 +js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) 1.270 +{ 1.271 + Rooted<PropertyDescriptor> desc(cx); 1.272 + return GetOwnPropertyDescriptor(cx, obj, id, &desc) && 1.273 + NewPropertyDescriptorObject(cx, desc, vp); 1.274 +} 1.275 + 1.276 +bool 1.277 +js::GetFirstArgumentAsObject(JSContext *cx, const CallArgs &args, const char *method, 1.278 + MutableHandleObject objp) 1.279 +{ 1.280 + if (args.length() == 0) { 1.281 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, 1.282 + method, "0", "s"); 1.283 + return false; 1.284 + } 1.285 + 1.286 + HandleValue v = args[0]; 1.287 + if (!v.isObject()) { 1.288 + char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr()); 1.289 + if (!bytes) 1.290 + return false; 1.291 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 1.292 + bytes, "not an object"); 1.293 + js_free(bytes); 1.294 + return false; 1.295 + } 1.296 + 1.297 + objp.set(&v.toObject()); 1.298 + return true; 1.299 +} 1.300 + 1.301 +static bool 1.302 +HasProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, bool *foundp) 1.303 +{ 1.304 + if (!JSObject::hasProperty(cx, obj, id, foundp)) 1.305 + return false; 1.306 + if (!*foundp) { 1.307 + vp.setUndefined(); 1.308 + return true; 1.309 + } 1.310 + 1.311 + /* 1.312 + * We must go through the method read barrier in case id is 'get' or 'set'. 1.313 + * There is no obvious way to defer cloning a joined function object whose 1.314 + * identity will be used by DefinePropertyOnObject, e.g., or reflected via 1.315 + * js::GetOwnPropertyDescriptor, as the getter or setter callable object. 1.316 + */ 1.317 + return !!JSObject::getGeneric(cx, obj, obj, id, vp); 1.318 +} 1.319 + 1.320 +bool 1.321 +PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors) 1.322 +{ 1.323 + RootedValue v(cx, origval); 1.324 + 1.325 + /* 8.10.5 step 1 */ 1.326 + if (v.isPrimitive()) { 1.327 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT); 1.328 + return false; 1.329 + } 1.330 + RootedObject desc(cx, &v.toObject()); 1.331 + 1.332 + /* Make a copy of the descriptor. We might need it later. */ 1.333 + pd_ = v; 1.334 + 1.335 + isUndefined_ = false; 1.336 + 1.337 + /* 1.338 + * Start with the proper defaults. XXX shouldn't be necessary when we get 1.339 + * rid of PropDesc::attributes() 1.340 + */ 1.341 + attrs = JSPROP_PERMANENT | JSPROP_READONLY; 1.342 + 1.343 + bool found = false; 1.344 + RootedId id(cx); 1.345 + 1.346 + /* 8.10.5 step 3 */ 1.347 + id = NameToId(cx->names().enumerable); 1.348 + if (!HasProperty(cx, desc, id, &v, &found)) 1.349 + return false; 1.350 + if (found) { 1.351 + hasEnumerable_ = true; 1.352 + if (ToBoolean(v)) 1.353 + attrs |= JSPROP_ENUMERATE; 1.354 + } 1.355 + 1.356 + /* 8.10.5 step 4 */ 1.357 + id = NameToId(cx->names().configurable); 1.358 + if (!HasProperty(cx, desc, id, &v, &found)) 1.359 + return false; 1.360 + if (found) { 1.361 + hasConfigurable_ = true; 1.362 + if (ToBoolean(v)) 1.363 + attrs &= ~JSPROP_PERMANENT; 1.364 + } 1.365 + 1.366 + /* 8.10.5 step 5 */ 1.367 + id = NameToId(cx->names().value); 1.368 + if (!HasProperty(cx, desc, id, &v, &found)) 1.369 + return false; 1.370 + if (found) { 1.371 + hasValue_ = true; 1.372 + value_ = v; 1.373 + } 1.374 + 1.375 + /* 8.10.6 step 6 */ 1.376 + id = NameToId(cx->names().writable); 1.377 + if (!HasProperty(cx, desc, id, &v, &found)) 1.378 + return false; 1.379 + if (found) { 1.380 + hasWritable_ = true; 1.381 + if (ToBoolean(v)) 1.382 + attrs &= ~JSPROP_READONLY; 1.383 + } 1.384 + 1.385 + /* 8.10.7 step 7 */ 1.386 + id = NameToId(cx->names().get); 1.387 + if (!HasProperty(cx, desc, id, &v, &found)) 1.388 + return false; 1.389 + if (found) { 1.390 + hasGet_ = true; 1.391 + get_ = v; 1.392 + attrs |= JSPROP_GETTER | JSPROP_SHARED; 1.393 + attrs &= ~JSPROP_READONLY; 1.394 + if (checkAccessors && !checkGetter(cx)) 1.395 + return false; 1.396 + } 1.397 + 1.398 + /* 8.10.7 step 8 */ 1.399 + id = NameToId(cx->names().set); 1.400 + if (!HasProperty(cx, desc, id, &v, &found)) 1.401 + return false; 1.402 + if (found) { 1.403 + hasSet_ = true; 1.404 + set_ = v; 1.405 + attrs |= JSPROP_SETTER | JSPROP_SHARED; 1.406 + attrs &= ~JSPROP_READONLY; 1.407 + if (checkAccessors && !checkSetter(cx)) 1.408 + return false; 1.409 + } 1.410 + 1.411 + /* 8.10.7 step 9 */ 1.412 + if ((hasGet() || hasSet()) && (hasValue() || hasWritable())) { 1.413 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DESCRIPTOR); 1.414 + return false; 1.415 + } 1.416 + 1.417 + JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER))); 1.418 + 1.419 + return true; 1.420 +} 1.421 + 1.422 +void 1.423 +PropDesc::complete() 1.424 +{ 1.425 + if (isGenericDescriptor() || isDataDescriptor()) { 1.426 + if (!hasValue_) { 1.427 + hasValue_ = true; 1.428 + value_.setUndefined(); 1.429 + } 1.430 + if (!hasWritable_) { 1.431 + hasWritable_ = true; 1.432 + attrs |= JSPROP_READONLY; 1.433 + } 1.434 + } else { 1.435 + if (!hasGet_) { 1.436 + hasGet_ = true; 1.437 + get_.setUndefined(); 1.438 + } 1.439 + if (!hasSet_) { 1.440 + hasSet_ = true; 1.441 + set_.setUndefined(); 1.442 + } 1.443 + } 1.444 + if (!hasEnumerable_) { 1.445 + hasEnumerable_ = true; 1.446 + attrs &= ~JSPROP_ENUMERATE; 1.447 + } 1.448 + if (!hasConfigurable_) { 1.449 + hasConfigurable_ = true; 1.450 + attrs |= JSPROP_PERMANENT; 1.451 + } 1.452 +} 1.453 + 1.454 +bool 1.455 +js::Throw(JSContext *cx, jsid id, unsigned errorNumber) 1.456 +{ 1.457 + JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1); 1.458 + 1.459 + JSString *idstr = IdToString(cx, id); 1.460 + if (!idstr) 1.461 + return false; 1.462 + JSAutoByteString bytes(cx, idstr); 1.463 + if (!bytes) 1.464 + return false; 1.465 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber, bytes.ptr()); 1.466 + return false; 1.467 +} 1.468 + 1.469 +bool 1.470 +js::Throw(JSContext *cx, JSObject *obj, unsigned errorNumber) 1.471 +{ 1.472 + if (js_ErrorFormatString[errorNumber].argCount == 1) { 1.473 + RootedValue val(cx, ObjectValue(*obj)); 1.474 + js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, 1.475 + JSDVG_IGNORE_STACK, val, NullPtr(), 1.476 + nullptr, nullptr); 1.477 + } else { 1.478 + JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0); 1.479 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber); 1.480 + } 1.481 + return false; 1.482 +} 1.483 + 1.484 +static bool 1.485 +Reject(JSContext *cx, unsigned errorNumber, bool throwError, jsid id, bool *rval) 1.486 +{ 1.487 + if (throwError) 1.488 + return Throw(cx, id, errorNumber); 1.489 + 1.490 + *rval = false; 1.491 + return true; 1.492 +} 1.493 + 1.494 +static bool 1.495 +Reject(JSContext *cx, JSObject *obj, unsigned errorNumber, bool throwError, bool *rval) 1.496 +{ 1.497 + if (throwError) 1.498 + return Throw(cx, obj, errorNumber); 1.499 + 1.500 + *rval = false; 1.501 + return true; 1.502 +} 1.503 + 1.504 +static bool 1.505 +Reject(JSContext *cx, HandleId id, unsigned errorNumber, bool throwError, bool *rval) 1.506 +{ 1.507 + if (throwError) 1.508 + return Throw(cx, id, errorNumber); 1.509 + 1.510 + *rval = false; 1.511 + return true; 1.512 +} 1.513 + 1.514 +// See comments on CheckDefineProperty in jsobj.h. 1.515 +// 1.516 +// DefinePropertyOnObject has its own implementation of these checks. 1.517 +// 1.518 +JS_FRIEND_API(bool) 1.519 +js::CheckDefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, 1.520 + PropertyOp getter, StrictPropertyOp setter, unsigned attrs) 1.521 +{ 1.522 + if (!obj->isNative()) 1.523 + return true; 1.524 + 1.525 + // ES5 8.12.9 Step 1. Even though we know obj is native, we use generic 1.526 + // APIs for shorter, more readable code. 1.527 + Rooted<PropertyDescriptor> desc(cx); 1.528 + if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) 1.529 + return false; 1.530 + 1.531 + // This does not have to check obj's extensibility when !desc.obj (steps 1.532 + // 2-3) because the low-level methods JSObject::{add,put}Property check 1.533 + // for that. 1.534 + if (desc.object() && desc.isPermanent()) { 1.535 + // Steps 6-11, skipping step 10.a.ii. Prohibit redefining a permanent 1.536 + // property with different metadata, except to make a writable property 1.537 + // non-writable. 1.538 + if (getter != desc.getter() || 1.539 + setter != desc.setter() || 1.540 + (attrs != desc.attributes() && attrs != (desc.attributes() | JSPROP_READONLY))) 1.541 + { 1.542 + return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP); 1.543 + } 1.544 + 1.545 + // Step 10.a.ii. Prohibit changing the value of a non-configurable, 1.546 + // non-writable data property. 1.547 + if ((desc.attributes() & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_READONLY)) == JSPROP_READONLY) { 1.548 + bool same; 1.549 + if (!SameValue(cx, value, desc.value(), &same)) 1.550 + return false; 1.551 + if (!same) 1.552 + return JSObject::reportReadOnly(cx, id); 1.553 + } 1.554 + } 1.555 + return true; 1.556 +} 1.557 + 1.558 +static bool 1.559 +DefinePropertyOnObject(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc, 1.560 + bool throwError, bool *rval) 1.561 +{ 1.562 + /* 8.12.9 step 1. */ 1.563 + RootedShape shape(cx); 1.564 + RootedObject obj2(cx); 1.565 + JS_ASSERT(!obj->getOps()->lookupGeneric); 1.566 + if (!HasOwnProperty<CanGC>(cx, nullptr, obj, id, &obj2, &shape)) 1.567 + return false; 1.568 + 1.569 + JS_ASSERT(!obj->getOps()->defineProperty); 1.570 + 1.571 + /* 8.12.9 steps 2-4. */ 1.572 + if (!shape) { 1.573 + bool extensible; 1.574 + if (!JSObject::isExtensible(cx, obj, &extensible)) 1.575 + return false; 1.576 + if (!extensible) 1.577 + return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval); 1.578 + 1.579 + *rval = true; 1.580 + 1.581 + if (desc.isGenericDescriptor() || desc.isDataDescriptor()) { 1.582 + JS_ASSERT(!obj->getOps()->defineProperty); 1.583 + RootedValue v(cx, desc.hasValue() ? desc.value() : UndefinedValue()); 1.584 + return baseops::DefineGeneric(cx, obj, id, v, 1.585 + JS_PropertyStub, JS_StrictPropertyStub, 1.586 + desc.attributes()); 1.587 + } 1.588 + 1.589 + JS_ASSERT(desc.isAccessorDescriptor()); 1.590 + 1.591 + return baseops::DefineGeneric(cx, obj, id, UndefinedHandleValue, 1.592 + desc.getter(), desc.setter(), desc.attributes()); 1.593 + } 1.594 + 1.595 + /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */ 1.596 + RootedValue v(cx); 1.597 + 1.598 + JS_ASSERT(obj == obj2); 1.599 + 1.600 + bool shapeDataDescriptor = true, 1.601 + shapeAccessorDescriptor = false, 1.602 + shapeWritable = true, 1.603 + shapeConfigurable = true, 1.604 + shapeEnumerable = true, 1.605 + shapeHasDefaultGetter = true, 1.606 + shapeHasDefaultSetter = true, 1.607 + shapeHasGetterValue = false, 1.608 + shapeHasSetterValue = false; 1.609 + uint8_t shapeAttributes = GetShapeAttributes(obj, shape); 1.610 + if (!IsImplicitDenseOrTypedArrayElement(shape)) { 1.611 + shapeDataDescriptor = shape->isDataDescriptor(); 1.612 + shapeAccessorDescriptor = shape->isAccessorDescriptor(); 1.613 + shapeWritable = shape->writable(); 1.614 + shapeConfigurable = shape->configurable(); 1.615 + shapeEnumerable = shape->enumerable(); 1.616 + shapeHasDefaultGetter = shape->hasDefaultGetter(); 1.617 + shapeHasDefaultSetter = shape->hasDefaultSetter(); 1.618 + shapeHasGetterValue = shape->hasGetterValue(); 1.619 + shapeHasSetterValue = shape->hasSetterValue(); 1.620 + shapeAttributes = shape->attributes(); 1.621 + } 1.622 + 1.623 + do { 1.624 + if (desc.isAccessorDescriptor()) { 1.625 + if (!shapeAccessorDescriptor) 1.626 + break; 1.627 + 1.628 + if (desc.hasGet()) { 1.629 + bool same; 1.630 + if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same)) 1.631 + return false; 1.632 + if (!same) 1.633 + break; 1.634 + } 1.635 + 1.636 + if (desc.hasSet()) { 1.637 + bool same; 1.638 + if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same)) 1.639 + return false; 1.640 + if (!same) 1.641 + break; 1.642 + } 1.643 + } else { 1.644 + /* 1.645 + * Determine the current value of the property once, if the current 1.646 + * value might actually need to be used or preserved later. NB: we 1.647 + * guard on whether the current property is a data descriptor to 1.648 + * avoid calling a getter; we won't need the value if it's not a 1.649 + * data descriptor. 1.650 + */ 1.651 + if (IsImplicitDenseOrTypedArrayElement(shape)) { 1.652 + v = obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)); 1.653 + } else if (shape->isDataDescriptor()) { 1.654 + /* 1.655 + * We must rule out a non-configurable js::PropertyOp-guarded 1.656 + * property becoming a writable unguarded data property, since 1.657 + * such a property can have its value changed to one the getter 1.658 + * and setter preclude. 1.659 + * 1.660 + * A desc lacking writable but with value is a data descriptor 1.661 + * and we must reject it as if it had writable: true if current 1.662 + * is writable. 1.663 + */ 1.664 + if (!shape->configurable() && 1.665 + (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) && 1.666 + desc.isDataDescriptor() && 1.667 + (desc.hasWritable() ? desc.writable() : shape->writable())) 1.668 + { 1.669 + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); 1.670 + } 1.671 + 1.672 + if (!NativeGet(cx, obj, obj2, shape, &v)) 1.673 + return false; 1.674 + } 1.675 + 1.676 + if (desc.isDataDescriptor()) { 1.677 + if (!shapeDataDescriptor) 1.678 + break; 1.679 + 1.680 + bool same; 1.681 + if (desc.hasValue()) { 1.682 + if (!SameValue(cx, desc.value(), v, &same)) 1.683 + return false; 1.684 + if (!same) { 1.685 + /* 1.686 + * Insist that a non-configurable js::PropertyOp data 1.687 + * property is frozen at exactly the last-got value. 1.688 + * 1.689 + * Duplicate the first part of the big conjunction that 1.690 + * we tested above, rather than add a local bool flag. 1.691 + * Likewise, don't try to keep shape->writable() in a 1.692 + * flag we veto from true to false for non-configurable 1.693 + * PropertyOp-based data properties and test before the 1.694 + * SameValue check later on in order to re-use that "if 1.695 + * (!SameValue) Reject" logic. 1.696 + * 1.697 + * This function is large and complex enough that it 1.698 + * seems best to repeat a small bit of code and return 1.699 + * Reject(...) ASAP, instead of being clever. 1.700 + */ 1.701 + if (!shapeConfigurable && 1.702 + (!shape->hasDefaultGetter() || !shape->hasDefaultSetter())) 1.703 + { 1.704 + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); 1.705 + } 1.706 + break; 1.707 + } 1.708 + } 1.709 + if (desc.hasWritable() && desc.writable() != shapeWritable) 1.710 + break; 1.711 + } else { 1.712 + /* The only fields in desc will be handled below. */ 1.713 + JS_ASSERT(desc.isGenericDescriptor()); 1.714 + } 1.715 + } 1.716 + 1.717 + if (desc.hasConfigurable() && desc.configurable() != shapeConfigurable) 1.718 + break; 1.719 + if (desc.hasEnumerable() && desc.enumerable() != shapeEnumerable) 1.720 + break; 1.721 + 1.722 + /* The conditions imposed by step 5 or step 6 apply. */ 1.723 + *rval = true; 1.724 + return true; 1.725 + } while (0); 1.726 + 1.727 + /* 8.12.9 step 7. */ 1.728 + if (!shapeConfigurable) { 1.729 + if ((desc.hasConfigurable() && desc.configurable()) || 1.730 + (desc.hasEnumerable() && desc.enumerable() != shape->enumerable())) { 1.731 + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); 1.732 + } 1.733 + } 1.734 + 1.735 + bool callDelProperty = false; 1.736 + 1.737 + if (desc.isGenericDescriptor()) { 1.738 + /* 8.12.9 step 8, no validation required */ 1.739 + } else if (desc.isDataDescriptor() != shapeDataDescriptor) { 1.740 + /* 8.12.9 step 9. */ 1.741 + if (!shapeConfigurable) 1.742 + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); 1.743 + } else if (desc.isDataDescriptor()) { 1.744 + /* 8.12.9 step 10. */ 1.745 + JS_ASSERT(shapeDataDescriptor); 1.746 + if (!shapeConfigurable && !shape->writable()) { 1.747 + if (desc.hasWritable() && desc.writable()) 1.748 + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); 1.749 + if (desc.hasValue()) { 1.750 + bool same; 1.751 + if (!SameValue(cx, desc.value(), v, &same)) 1.752 + return false; 1.753 + if (!same) 1.754 + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); 1.755 + } 1.756 + } 1.757 + 1.758 + callDelProperty = !shapeHasDefaultGetter || !shapeHasDefaultSetter; 1.759 + } else { 1.760 + /* 8.12.9 step 11. */ 1.761 + JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor()); 1.762 + if (!shape->configurable()) { 1.763 + if (desc.hasSet()) { 1.764 + bool same; 1.765 + if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same)) 1.766 + return false; 1.767 + if (!same) 1.768 + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); 1.769 + } 1.770 + 1.771 + if (desc.hasGet()) { 1.772 + bool same; 1.773 + if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same)) 1.774 + return false; 1.775 + if (!same) 1.776 + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); 1.777 + } 1.778 + } 1.779 + } 1.780 + 1.781 + /* 8.12.9 step 12. */ 1.782 + unsigned attrs; 1.783 + PropertyOp getter; 1.784 + StrictPropertyOp setter; 1.785 + if (desc.isGenericDescriptor()) { 1.786 + unsigned changed = 0; 1.787 + if (desc.hasConfigurable()) 1.788 + changed |= JSPROP_PERMANENT; 1.789 + if (desc.hasEnumerable()) 1.790 + changed |= JSPROP_ENUMERATE; 1.791 + 1.792 + attrs = (shapeAttributes & ~changed) | (desc.attributes() & changed); 1.793 + getter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_PropertyStub : shape->getter(); 1.794 + setter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_StrictPropertyStub : shape->setter(); 1.795 + } else if (desc.isDataDescriptor()) { 1.796 + unsigned unchanged = 0; 1.797 + if (!desc.hasConfigurable()) 1.798 + unchanged |= JSPROP_PERMANENT; 1.799 + if (!desc.hasEnumerable()) 1.800 + unchanged |= JSPROP_ENUMERATE; 1.801 + /* Watch out for accessor -> data transformations here. */ 1.802 + if (!desc.hasWritable() && shapeDataDescriptor) 1.803 + unchanged |= JSPROP_READONLY; 1.804 + 1.805 + if (desc.hasValue()) 1.806 + v = desc.value(); 1.807 + attrs = (desc.attributes() & ~unchanged) | (shapeAttributes & unchanged); 1.808 + getter = JS_PropertyStub; 1.809 + setter = JS_StrictPropertyStub; 1.810 + } else { 1.811 + JS_ASSERT(desc.isAccessorDescriptor()); 1.812 + 1.813 + /* 8.12.9 step 12. */ 1.814 + unsigned changed = 0; 1.815 + if (desc.hasConfigurable()) 1.816 + changed |= JSPROP_PERMANENT; 1.817 + if (desc.hasEnumerable()) 1.818 + changed |= JSPROP_ENUMERATE; 1.819 + if (desc.hasGet()) 1.820 + changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY; 1.821 + if (desc.hasSet()) 1.822 + changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY; 1.823 + 1.824 + attrs = (desc.attributes() & changed) | (shapeAttributes & ~changed); 1.825 + if (desc.hasGet()) { 1.826 + getter = desc.getter(); 1.827 + } else { 1.828 + getter = (shapeHasDefaultGetter && !shapeHasGetterValue) 1.829 + ? JS_PropertyStub 1.830 + : shape->getter(); 1.831 + } 1.832 + if (desc.hasSet()) { 1.833 + setter = desc.setter(); 1.834 + } else { 1.835 + setter = (shapeHasDefaultSetter && !shapeHasSetterValue) 1.836 + ? JS_StrictPropertyStub 1.837 + : shape->setter(); 1.838 + } 1.839 + } 1.840 + 1.841 + *rval = true; 1.842 + 1.843 + /* 1.844 + * Since "data" properties implemented using native C functions may rely on 1.845 + * side effects during setting, we must make them aware that they have been 1.846 + * "assigned"; deleting the property before redefining it does the trick. 1.847 + * See bug 539766, where we ran into problems when we redefined 1.848 + * arguments.length without making the property aware that its value had 1.849 + * been changed (which would have happened if we had deleted it before 1.850 + * redefining it or we had invoked its setter to change its value). 1.851 + */ 1.852 + if (callDelProperty) { 1.853 + bool succeeded; 1.854 + if (!CallJSDeletePropertyOp(cx, obj2->getClass()->delProperty, obj2, id, &succeeded)) 1.855 + return false; 1.856 + } 1.857 + 1.858 + return baseops::DefineGeneric(cx, obj, id, v, getter, setter, attrs); 1.859 +} 1.860 + 1.861 +/* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */ 1.862 +static bool 1.863 +DefinePropertyOnArray(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, const PropDesc &desc, 1.864 + bool throwError, bool *rval) 1.865 +{ 1.866 + /* Step 2. */ 1.867 + if (id == NameToId(cx->names().length)) { 1.868 + // Canonicalize value, if necessary, before proceeding any further. It 1.869 + // would be better if this were always/only done by ArraySetLength. 1.870 + // But canonicalization may throw a RangeError (or other exception, if 1.871 + // the value is an object with user-defined conversion semantics) 1.872 + // before other attributes are checked. So as long as our internal 1.873 + // defineProperty hook doesn't match the ECMA one, this duplicate 1.874 + // checking can't be helped. 1.875 + RootedValue v(cx); 1.876 + if (desc.hasValue()) { 1.877 + uint32_t newLen; 1.878 + if (!CanonicalizeArrayLengthValue<SequentialExecution>(cx, desc.value(), &newLen)) 1.879 + return false; 1.880 + v.setNumber(newLen); 1.881 + } else { 1.882 + v.setNumber(arr->length()); 1.883 + } 1.884 + 1.885 + if (desc.hasConfigurable() && desc.configurable()) 1.886 + return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); 1.887 + if (desc.hasEnumerable() && desc.enumerable()) 1.888 + return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); 1.889 + 1.890 + if (desc.isAccessorDescriptor()) 1.891 + return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); 1.892 + 1.893 + unsigned attrs = arr->nativeLookup(cx, id)->attributes(); 1.894 + if (!arr->lengthIsWritable()) { 1.895 + if (desc.hasWritable() && desc.writable()) 1.896 + return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); 1.897 + } else { 1.898 + if (desc.hasWritable() && !desc.writable()) 1.899 + attrs = attrs | JSPROP_READONLY; 1.900 + } 1.901 + 1.902 + return ArraySetLength<SequentialExecution>(cx, arr, id, attrs, v, throwError); 1.903 + } 1.904 + 1.905 + /* Step 3. */ 1.906 + uint32_t index; 1.907 + if (js_IdIsIndex(id, &index)) { 1.908 + /* Step 3b. */ 1.909 + uint32_t oldLen = arr->length(); 1.910 + 1.911 + /* Steps 3a, 3e. */ 1.912 + if (index >= oldLen && !arr->lengthIsWritable()) 1.913 + return Reject(cx, arr, JSMSG_CANT_APPEND_TO_ARRAY, throwError, rval); 1.914 + 1.915 + /* Steps 3f-j. */ 1.916 + return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval); 1.917 + } 1.918 + 1.919 + /* Step 4. */ 1.920 + return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval); 1.921 +} 1.922 + 1.923 +bool 1.924 +js::DefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc, 1.925 + bool throwError, bool *rval) 1.926 +{ 1.927 + if (obj->is<ArrayObject>()) { 1.928 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.929 + return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval); 1.930 + } 1.931 + 1.932 + if (obj->getOps()->lookupGeneric) { 1.933 + /* 1.934 + * FIXME: Once ScriptedIndirectProxies are removed, this code should call 1.935 + * TrapDefineOwnProperty directly 1.936 + */ 1.937 + if (obj->is<ProxyObject>()) { 1.938 + RootedValue pd(cx, desc.pd()); 1.939 + return Proxy::defineProperty(cx, obj, id, pd); 1.940 + } 1.941 + return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval); 1.942 + } 1.943 + 1.944 + return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval); 1.945 +} 1.946 + 1.947 +bool 1.948 +js::DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue descriptor, 1.949 + bool *bp) 1.950 +{ 1.951 + AutoPropDescArrayRooter descs(cx); 1.952 + PropDesc *desc = descs.append(); 1.953 + if (!desc || !desc->initialize(cx, descriptor)) 1.954 + return false; 1.955 + 1.956 + bool rval; 1.957 + if (!DefineProperty(cx, obj, id, *desc, true, &rval)) 1.958 + return false; 1.959 + *bp = !!rval; 1.960 + return true; 1.961 +} 1.962 + 1.963 +bool 1.964 +js::DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id, 1.965 + Handle<PropertyDescriptor> descriptor, bool *bp) 1.966 +{ 1.967 + AutoPropDescArrayRooter descs(cx); 1.968 + PropDesc *desc = descs.append(); 1.969 + if (!desc) 1.970 + return false; 1.971 + 1.972 + desc->initFromPropertyDescriptor(descriptor); 1.973 + 1.974 + bool rval; 1.975 + if (!DefineProperty(cx, obj, id, *desc, true, &rval)) 1.976 + return false; 1.977 + *bp = !!rval; 1.978 + return true; 1.979 +} 1.980 + 1.981 + 1.982 +bool 1.983 +js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors, 1.984 + AutoIdVector *ids, AutoPropDescArrayRooter *descs) 1.985 +{ 1.986 + if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids)) 1.987 + return false; 1.988 + 1.989 + RootedId id(cx); 1.990 + for (size_t i = 0, len = ids->length(); i < len; i++) { 1.991 + id = (*ids)[i]; 1.992 + PropDesc* desc = descs->append(); 1.993 + RootedValue v(cx); 1.994 + if (!desc || 1.995 + !JSObject::getGeneric(cx, props, props, id, &v) || 1.996 + !desc->initialize(cx, v, checkAccessors)) 1.997 + { 1.998 + return false; 1.999 + } 1.1000 + } 1.1001 + return true; 1.1002 +} 1.1003 + 1.1004 +bool 1.1005 +js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props) 1.1006 +{ 1.1007 + AutoIdVector ids(cx); 1.1008 + AutoPropDescArrayRooter descs(cx); 1.1009 + if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs)) 1.1010 + return false; 1.1011 + 1.1012 + if (obj->is<ArrayObject>()) { 1.1013 + bool dummy; 1.1014 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.1015 + for (size_t i = 0, len = ids.length(); i < len; i++) { 1.1016 + if (!DefinePropertyOnArray(cx, arr, ids.handleAt(i), descs[i], true, &dummy)) 1.1017 + return false; 1.1018 + } 1.1019 + return true; 1.1020 + } 1.1021 + 1.1022 + if (obj->getOps()->lookupGeneric) { 1.1023 + /* 1.1024 + * FIXME: Once ScriptedIndirectProxies are removed, this code should call 1.1025 + * TrapDefineOwnProperty directly 1.1026 + */ 1.1027 + if (obj->is<ProxyObject>()) { 1.1028 + for (size_t i = 0, len = ids.length(); i < len; i++) { 1.1029 + RootedValue pd(cx, descs[i].pd()); 1.1030 + if (!Proxy::defineProperty(cx, obj, ids.handleAt(i), pd)) 1.1031 + return false; 1.1032 + } 1.1033 + return true; 1.1034 + } 1.1035 + bool dummy; 1.1036 + return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, true, &dummy); 1.1037 + } 1.1038 + 1.1039 + bool dummy; 1.1040 + for (size_t i = 0, len = ids.length(); i < len; i++) { 1.1041 + if (!DefinePropertyOnObject(cx, obj, ids.handleAt(i), descs[i], true, &dummy)) 1.1042 + return false; 1.1043 + } 1.1044 + 1.1045 + return true; 1.1046 +} 1.1047 + 1.1048 +extern bool 1.1049 +js_PopulateObject(JSContext *cx, HandleObject newborn, HandleObject props) 1.1050 +{ 1.1051 + return DefineProperties(cx, newborn, props); 1.1052 +} 1.1053 + 1.1054 +js::types::TypeObject* 1.1055 +JSObject::uninlinedGetType(JSContext *cx) 1.1056 +{ 1.1057 + return getType(cx); 1.1058 +} 1.1059 + 1.1060 +void 1.1061 +JSObject::uninlinedSetType(js::types::TypeObject *newType) 1.1062 +{ 1.1063 + setType(newType); 1.1064 +} 1.1065 + 1.1066 +/* static */ inline unsigned 1.1067 +JSObject::getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it) 1.1068 +{ 1.1069 + /* Make all attributes permanent; if freezing, make data attributes read-only. */ 1.1070 + if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER))) 1.1071 + return JSPROP_PERMANENT | JSPROP_READONLY; 1.1072 + return JSPROP_PERMANENT; 1.1073 +} 1.1074 + 1.1075 +/* static */ bool 1.1076 +JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it) 1.1077 +{ 1.1078 + assertSameCompartment(cx, obj); 1.1079 + JS_ASSERT(it == SEAL || it == FREEZE); 1.1080 + 1.1081 + bool extensible; 1.1082 + if (!JSObject::isExtensible(cx, obj, &extensible)) 1.1083 + return false; 1.1084 + if (extensible && !JSObject::preventExtensions(cx, obj)) 1.1085 + return false; 1.1086 + 1.1087 + AutoIdVector props(cx); 1.1088 + if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props)) 1.1089 + return false; 1.1090 + 1.1091 + /* preventExtensions must sparsify dense objects, so we can assign to holes without checks. */ 1.1092 + JS_ASSERT_IF(obj->isNative(), obj->getDenseCapacity() == 0); 1.1093 + 1.1094 + if (obj->isNative() && !obj->inDictionaryMode() && !obj->is<TypedArrayObject>()) { 1.1095 + /* 1.1096 + * Seal/freeze non-dictionary objects by constructing a new shape 1.1097 + * hierarchy mirroring the original one, which can be shared if many 1.1098 + * objects with the same structure are sealed/frozen. If we use the 1.1099 + * generic path below then any non-empty object will be converted to 1.1100 + * dictionary mode. 1.1101 + */ 1.1102 + RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(), 1.1103 + obj->getTaggedProto(), 1.1104 + obj->getParent(), 1.1105 + obj->getMetadata(), 1.1106 + obj->numFixedSlots(), 1.1107 + obj->lastProperty()->getObjectFlags())); 1.1108 + if (!last) 1.1109 + return false; 1.1110 + 1.1111 + /* Get an in order list of the shapes in this object. */ 1.1112 + AutoShapeVector shapes(cx); 1.1113 + for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) { 1.1114 + if (!shapes.append(&r.front())) 1.1115 + return false; 1.1116 + } 1.1117 + Reverse(shapes.begin(), shapes.end()); 1.1118 + 1.1119 + for (size_t i = 0; i < shapes.length(); i++) { 1.1120 + StackShape unrootedChild(shapes[i]); 1.1121 + RootedGeneric<StackShape*> child(cx, &unrootedChild); 1.1122 + child->attrs |= getSealedOrFrozenAttributes(child->attrs, it); 1.1123 + 1.1124 + if (!JSID_IS_EMPTY(child->propid) && it == FREEZE) 1.1125 + MarkTypePropertyNonWritable(cx, obj, child->propid); 1.1126 + 1.1127 + last = cx->compartment()->propertyTree.getChild(cx, last, *child); 1.1128 + if (!last) 1.1129 + return false; 1.1130 + } 1.1131 + 1.1132 + JS_ASSERT(obj->lastProperty()->slotSpan() == last->slotSpan()); 1.1133 + JS_ALWAYS_TRUE(setLastProperty(cx, obj, last)); 1.1134 + } else { 1.1135 + RootedId id(cx); 1.1136 + for (size_t i = 0; i < props.length(); i++) { 1.1137 + id = props[i]; 1.1138 + 1.1139 + unsigned attrs; 1.1140 + if (!getGenericAttributes(cx, obj, id, &attrs)) 1.1141 + return false; 1.1142 + 1.1143 + unsigned new_attrs = getSealedOrFrozenAttributes(attrs, it); 1.1144 + 1.1145 + /* If we already have the attributes we need, skip the setAttributes call. */ 1.1146 + if ((attrs | new_attrs) == attrs) 1.1147 + continue; 1.1148 + 1.1149 + attrs |= new_attrs; 1.1150 + if (!setGenericAttributes(cx, obj, id, &attrs)) 1.1151 + return false; 1.1152 + } 1.1153 + } 1.1154 + 1.1155 + // Ordinarily ArraySetLength handles this, but we're going behind its back 1.1156 + // right now, so we must do this manually. Neither the custom property 1.1157 + // tree mutations nor the setGenericAttributes call in the above code will 1.1158 + // do this for us. 1.1159 + // 1.1160 + // ArraySetLength also implements the capacity <= length invariant for 1.1161 + // arrays with non-writable length. We don't need to do anything special 1.1162 + // for that, because capacity was zeroed out by preventExtensions. (See 1.1163 + // the assertion before the if-else above.) 1.1164 + if (it == FREEZE && obj->is<ArrayObject>()) 1.1165 + obj->getElementsHeader()->setNonwritableArrayLength(); 1.1166 + 1.1167 + return true; 1.1168 +} 1.1169 + 1.1170 +/* static */ bool 1.1171 +JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp) 1.1172 +{ 1.1173 + bool extensible; 1.1174 + if (!JSObject::isExtensible(cx, obj, &extensible)) 1.1175 + return false; 1.1176 + if (extensible) { 1.1177 + *resultp = false; 1.1178 + return true; 1.1179 + } 1.1180 + 1.1181 + if (obj->is<TypedArrayObject>()) { 1.1182 + if (it == SEAL) { 1.1183 + // Typed arrays are always sealed. 1.1184 + *resultp = true; 1.1185 + } else { 1.1186 + // Typed arrays cannot be frozen, but an empty typed array is 1.1187 + // trivially frozen. 1.1188 + *resultp = (obj->as<TypedArrayObject>().length() == 0); 1.1189 + } 1.1190 + return true; 1.1191 + } 1.1192 + 1.1193 + AutoIdVector props(cx); 1.1194 + if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props)) 1.1195 + return false; 1.1196 + 1.1197 + RootedId id(cx); 1.1198 + for (size_t i = 0, len = props.length(); i < len; i++) { 1.1199 + id = props[i]; 1.1200 + 1.1201 + unsigned attrs; 1.1202 + if (!getGenericAttributes(cx, obj, id, &attrs)) 1.1203 + return false; 1.1204 + 1.1205 + /* 1.1206 + * If the property is configurable, this object is neither sealed nor 1.1207 + * frozen. If the property is a writable data property, this object is 1.1208 + * not frozen. 1.1209 + */ 1.1210 + if (!(attrs & JSPROP_PERMANENT) || 1.1211 + (it == FREEZE && !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER)))) 1.1212 + { 1.1213 + *resultp = false; 1.1214 + return true; 1.1215 + } 1.1216 + } 1.1217 + 1.1218 + /* All properties checked out. This object is sealed/frozen. */ 1.1219 + *resultp = true; 1.1220 + return true; 1.1221 +} 1.1222 + 1.1223 +/* static */ 1.1224 +const char * 1.1225 +JSObject::className(JSContext *cx, HandleObject obj) 1.1226 +{ 1.1227 + assertSameCompartment(cx, obj); 1.1228 + 1.1229 + if (obj->is<ProxyObject>()) 1.1230 + return Proxy::className(cx, obj); 1.1231 + 1.1232 + return obj->getClass()->name; 1.1233 +} 1.1234 + 1.1235 +/* 1.1236 + * Get the GC kind to use for scripted 'new' on the given class. 1.1237 + * FIXME bug 547327: estimate the size from the allocation site. 1.1238 + */ 1.1239 +static inline gc::AllocKind 1.1240 +NewObjectGCKind(const js::Class *clasp) 1.1241 +{ 1.1242 + if (clasp == &ArrayObject::class_) 1.1243 + return gc::FINALIZE_OBJECT8; 1.1244 + if (clasp == &JSFunction::class_) 1.1245 + return gc::FINALIZE_OBJECT2; 1.1246 + return gc::FINALIZE_OBJECT4; 1.1247 +} 1.1248 + 1.1249 +static inline JSObject * 1.1250 +NewObject(ExclusiveContext *cx, types::TypeObject *type_, JSObject *parent, gc::AllocKind kind, 1.1251 + NewObjectKind newKind) 1.1252 +{ 1.1253 + const Class *clasp = type_->clasp(); 1.1254 + 1.1255 + JS_ASSERT(clasp != &ArrayObject::class_); 1.1256 + JS_ASSERT_IF(clasp == &JSFunction::class_, 1.1257 + kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind); 1.1258 + JS_ASSERT_IF(parent, &parent->global() == cx->global()); 1.1259 + 1.1260 + RootedTypeObject type(cx, type_); 1.1261 + 1.1262 + JSObject *metadata = nullptr; 1.1263 + if (!NewObjectMetadata(cx, &metadata)) 1.1264 + return nullptr; 1.1265 + 1.1266 + // For objects which can have fixed data following the object, only use 1.1267 + // enough fixed slots to cover the number of reserved slots in the object, 1.1268 + // regardless of the allocation kind specified. 1.1269 + size_t nfixed = ClassCanHaveFixedData(clasp) 1.1270 + ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp) 1.1271 + : GetGCKindSlots(kind, clasp); 1.1272 + 1.1273 + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, type->proto(), 1.1274 + parent, metadata, nfixed)); 1.1275 + if (!shape) 1.1276 + return nullptr; 1.1277 + 1.1278 + gc::InitialHeap heap = GetInitialHeap(newKind, clasp); 1.1279 + JSObject *obj = JSObject::create(cx, kind, heap, shape, type); 1.1280 + if (!obj) 1.1281 + return nullptr; 1.1282 + 1.1283 + if (newKind == SingletonObject) { 1.1284 + RootedObject nobj(cx, obj); 1.1285 + if (!JSObject::setSingletonType(cx, nobj)) 1.1286 + return nullptr; 1.1287 + obj = nobj; 1.1288 + } 1.1289 + 1.1290 + /* 1.1291 + * This will cancel an already-running incremental GC from doing any more 1.1292 + * slices, and it will prevent any future incremental GCs. 1.1293 + */ 1.1294 + bool globalWithoutCustomTrace = clasp->trace == JS_GlobalObjectTraceHook && 1.1295 + !cx->compartment()->options().getTrace(); 1.1296 + if (clasp->trace && 1.1297 + !globalWithoutCustomTrace && 1.1298 + !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS)) 1.1299 + { 1.1300 + if (!cx->shouldBeJSContext()) 1.1301 + return nullptr; 1.1302 + JSRuntime *rt = cx->asJSContext()->runtime(); 1.1303 + rt->gcIncrementalEnabled = false; 1.1304 + 1.1305 +#ifdef DEBUG 1.1306 + if (rt->gcMode() == JSGC_MODE_INCREMENTAL) { 1.1307 + fprintf(stderr, 1.1308 + "The class %s has a trace hook but does not declare the\n" 1.1309 + "JSCLASS_IMPLEMENTS_BARRIERS flag. Please ensure that it correctly\n" 1.1310 + "implements write barriers and then set the flag.\n", 1.1311 + clasp->name); 1.1312 + MOZ_CRASH(); 1.1313 + } 1.1314 +#endif 1.1315 + } 1.1316 + 1.1317 + probes::CreateObject(cx, obj); 1.1318 + return obj; 1.1319 +} 1.1320 + 1.1321 +void 1.1322 +NewObjectCache::fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto proto, 1.1323 + gc::AllocKind kind, JSObject *obj) 1.1324 +{ 1.1325 + JS_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>()); 1.1326 + JS_ASSERT(obj->getTaggedProto() == proto); 1.1327 + return fill(entry, clasp, proto.raw(), kind, obj); 1.1328 +} 1.1329 + 1.1330 +JSObject * 1.1331 +js::NewObjectWithGivenProto(ExclusiveContext *cxArg, const js::Class *clasp, 1.1332 + js::TaggedProto protoArg, JSObject *parentArg, 1.1333 + gc::AllocKind allocKind, NewObjectKind newKind) 1.1334 +{ 1.1335 + if (CanBeFinalizedInBackground(allocKind, clasp)) 1.1336 + allocKind = GetBackgroundAllocKind(allocKind); 1.1337 + 1.1338 + NewObjectCache::EntryIndex entry = -1; 1.1339 + if (JSContext *cx = cxArg->maybeJSContext()) { 1.1340 + NewObjectCache &cache = cx->runtime()->newObjectCache; 1.1341 + if (protoArg.isObject() && 1.1342 + newKind == GenericObject && 1.1343 + !cx->compartment()->hasObjectMetadataCallback() && 1.1344 + (!parentArg || parentArg == protoArg.toObject()->getParent()) && 1.1345 + !protoArg.toObject()->is<GlobalObject>()) 1.1346 + { 1.1347 + if (cache.lookupProto(clasp, protoArg.toObject(), allocKind, &entry)) { 1.1348 + JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp)); 1.1349 + if (obj) { 1.1350 + return obj; 1.1351 + } else { 1.1352 + Rooted<TaggedProto> proto(cxArg, protoArg); 1.1353 + RootedObject parent(cxArg, parentArg); 1.1354 + obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp)); 1.1355 + JS_ASSERT(!obj); 1.1356 + parentArg = parent; 1.1357 + protoArg = proto; 1.1358 + } 1.1359 + } 1.1360 + } 1.1361 + } 1.1362 + 1.1363 + Rooted<TaggedProto> proto(cxArg, protoArg); 1.1364 + RootedObject parent(cxArg, parentArg); 1.1365 + 1.1366 + types::TypeObject *type = cxArg->getNewType(clasp, proto, nullptr); 1.1367 + if (!type) 1.1368 + return nullptr; 1.1369 + 1.1370 + /* 1.1371 + * Default parent to the parent of the prototype, which was set from 1.1372 + * the parent of the prototype's constructor. 1.1373 + */ 1.1374 + if (!parent && proto.isObject()) 1.1375 + parent = proto.toObject()->getParent(); 1.1376 + 1.1377 + RootedObject obj(cxArg, NewObject(cxArg, type, parent, allocKind, newKind)); 1.1378 + if (!obj) 1.1379 + return nullptr; 1.1380 + 1.1381 + if (entry != -1 && !obj->hasDynamicSlots()) { 1.1382 + cxArg->asJSContext()->runtime()->newObjectCache.fillProto(entry, clasp, 1.1383 + proto, allocKind, obj); 1.1384 + } 1.1385 + 1.1386 + return obj; 1.1387 +} 1.1388 + 1.1389 +JSObject * 1.1390 +js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, 1.1391 + const js::Class *clasp, JSObject *protoArg, JSObject *parentArg, 1.1392 + gc::AllocKind allocKind, NewObjectKind newKind) 1.1393 +{ 1.1394 + if (protoArg) 1.1395 + return NewObjectWithGivenProto(cxArg, clasp, protoArg, parentArg, allocKind, newKind); 1.1396 + 1.1397 + if (CanBeFinalizedInBackground(allocKind, clasp)) 1.1398 + allocKind = GetBackgroundAllocKind(allocKind); 1.1399 + 1.1400 + if (!parentArg) 1.1401 + parentArg = cxArg->global(); 1.1402 + 1.1403 + /* 1.1404 + * Use the object cache, except for classes without a cached proto key. 1.1405 + * On these objects, FindProto will do a dynamic property lookup to get 1.1406 + * global[className].prototype, where changes to either the className or 1.1407 + * prototype property would render the cached lookup incorrect. For classes 1.1408 + * with a proto key, the prototype created during class initialization is 1.1409 + * stored in an immutable slot on the global (except for ClearScope, which 1.1410 + * will flush the new object cache). 1.1411 + */ 1.1412 + JSProtoKey protoKey = GetClassProtoKey(clasp); 1.1413 + 1.1414 + NewObjectCache::EntryIndex entry = -1; 1.1415 + if (JSContext *cx = cxArg->maybeJSContext()) { 1.1416 + NewObjectCache &cache = cx->runtime()->newObjectCache; 1.1417 + if (parentArg->is<GlobalObject>() && 1.1418 + protoKey != JSProto_Null && 1.1419 + newKind == GenericObject && 1.1420 + !cx->compartment()->hasObjectMetadataCallback()) 1.1421 + { 1.1422 + if (cache.lookupGlobal(clasp, &parentArg->as<GlobalObject>(), allocKind, &entry)) { 1.1423 + JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp)); 1.1424 + if (obj) { 1.1425 + return obj; 1.1426 + } else { 1.1427 + RootedObject parent(cxArg, parentArg); 1.1428 + RootedObject proto(cxArg, protoArg); 1.1429 + obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp)); 1.1430 + JS_ASSERT(!obj); 1.1431 + protoArg = proto; 1.1432 + parentArg = parent; 1.1433 + } 1.1434 + } 1.1435 + } 1.1436 + } 1.1437 + 1.1438 + RootedObject parent(cxArg, parentArg); 1.1439 + RootedObject proto(cxArg, protoArg); 1.1440 + 1.1441 + if (!FindProto(cxArg, clasp, &proto)) 1.1442 + return nullptr; 1.1443 + 1.1444 + types::TypeObject *type = cxArg->getNewType(clasp, proto.get()); 1.1445 + if (!type) 1.1446 + return nullptr; 1.1447 + 1.1448 + JSObject *obj = NewObject(cxArg, type, parent, allocKind, newKind); 1.1449 + if (!obj) 1.1450 + return nullptr; 1.1451 + 1.1452 + if (entry != -1 && !obj->hasDynamicSlots()) { 1.1453 + cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, clasp, 1.1454 + &parent->as<GlobalObject>(), 1.1455 + allocKind, obj); 1.1456 + } 1.1457 + 1.1458 + return obj; 1.1459 +} 1.1460 + 1.1461 +/* 1.1462 + * Create a plain object with the specified type. This bypasses getNewType to 1.1463 + * avoid losing creation site information for objects made by scripted 'new'. 1.1464 + */ 1.1465 +JSObject * 1.1466 +js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind, 1.1467 + NewObjectKind newKind) 1.1468 +{ 1.1469 + JS_ASSERT(parent); 1.1470 + 1.1471 + JS_ASSERT(allocKind <= gc::FINALIZE_OBJECT_LAST); 1.1472 + if (CanBeFinalizedInBackground(allocKind, type->clasp())) 1.1473 + allocKind = GetBackgroundAllocKind(allocKind); 1.1474 + 1.1475 + NewObjectCache &cache = cx->runtime()->newObjectCache; 1.1476 + 1.1477 + NewObjectCache::EntryIndex entry = -1; 1.1478 + if (parent == type->proto().toObject()->getParent() && 1.1479 + newKind == GenericObject && 1.1480 + !cx->compartment()->hasObjectMetadataCallback()) 1.1481 + { 1.1482 + if (cache.lookupType(type, allocKind, &entry)) { 1.1483 + JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, type->clasp())); 1.1484 + if (obj) { 1.1485 + return obj; 1.1486 + } else { 1.1487 + obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, type->clasp())); 1.1488 + parent = type->proto().toObject()->getParent(); 1.1489 + } 1.1490 + } 1.1491 + } 1.1492 + 1.1493 + JSObject *obj = NewObject(cx, type, parent, allocKind, newKind); 1.1494 + if (!obj) 1.1495 + return nullptr; 1.1496 + 1.1497 + if (entry != -1 && !obj->hasDynamicSlots()) 1.1498 + cache.fillType(entry, type, allocKind, obj); 1.1499 + 1.1500 + return obj; 1.1501 +} 1.1502 + 1.1503 +bool 1.1504 +js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj) 1.1505 +{ 1.1506 + jsbytecode *pc; 1.1507 + RootedScript script(cx, cx->currentScript(&pc)); 1.1508 + gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_); 1.1509 + NewObjectKind newKind = script 1.1510 + ? UseNewTypeForInitializer(script, pc, &JSObject::class_) 1.1511 + : GenericObject; 1.1512 + RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind)); 1.1513 + if (!obj) 1.1514 + return false; 1.1515 + 1.1516 + if (script) { 1.1517 + /* Try to specialize the type of the object to the scripted call site. */ 1.1518 + if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) 1.1519 + return false; 1.1520 + } 1.1521 + 1.1522 + pobj.set(obj); 1.1523 + return true; 1.1524 +} 1.1525 + 1.1526 +JSObject* 1.1527 +js::CreateThis(JSContext *cx, const Class *newclasp, HandleObject callee) 1.1528 +{ 1.1529 + RootedValue protov(cx); 1.1530 + if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov)) 1.1531 + return nullptr; 1.1532 + 1.1533 + JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : nullptr; 1.1534 + JSObject *parent = callee->getParent(); 1.1535 + gc::AllocKind kind = NewObjectGCKind(newclasp); 1.1536 + return NewObjectWithClassProto(cx, newclasp, proto, parent, kind); 1.1537 +} 1.1538 + 1.1539 +static inline JSObject * 1.1540 +CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, 1.1541 + NewObjectKind newKind) 1.1542 +{ 1.1543 + if (type->hasNewScript()) { 1.1544 + /* 1.1545 + * Make an object with the type's associated finalize kind and shape, 1.1546 + * which reflects any properties that will definitely be added to the 1.1547 + * object before it is read from. 1.1548 + */ 1.1549 + RootedObject templateObject(cx, type->newScript()->templateObject); 1.1550 + JS_ASSERT(templateObject->type() == type); 1.1551 + 1.1552 + RootedObject res(cx, CopyInitializerObject(cx, templateObject, newKind)); 1.1553 + if (!res) 1.1554 + return nullptr; 1.1555 + if (newKind == SingletonObject) { 1.1556 + Rooted<TaggedProto> proto(cx, templateObject->getProto()); 1.1557 + if (!res->splicePrototype(cx, &JSObject::class_, proto)) 1.1558 + return nullptr; 1.1559 + } else { 1.1560 + res->setType(type); 1.1561 + } 1.1562 + return res; 1.1563 + } 1.1564 + 1.1565 + gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_); 1.1566 + return NewObjectWithType(cx, type, parent, allocKind, newKind); 1.1567 +} 1.1568 + 1.1569 +JSObject * 1.1570 +js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto, 1.1571 + NewObjectKind newKind /* = GenericObject */) 1.1572 +{ 1.1573 + RootedObject res(cx); 1.1574 + 1.1575 + if (proto) { 1.1576 + RootedTypeObject type(cx, cx->getNewType(&JSObject::class_, proto, &callee->as<JSFunction>())); 1.1577 + if (!type) 1.1578 + return nullptr; 1.1579 + res = CreateThisForFunctionWithType(cx, type, callee->getParent(), newKind); 1.1580 + } else { 1.1581 + gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_); 1.1582 + res = NewObjectWithClassProto(cx, &JSObject::class_, proto, callee->getParent(), allocKind, newKind); 1.1583 + } 1.1584 + 1.1585 + if (res) { 1.1586 + JSScript *script = callee->as<JSFunction>().getOrCreateScript(cx); 1.1587 + if (!script) 1.1588 + return nullptr; 1.1589 + TypeScript::SetThis(cx, script, types::Type::ObjectType(res)); 1.1590 + } 1.1591 + 1.1592 + return res; 1.1593 +} 1.1594 + 1.1595 +JSObject * 1.1596 +js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newKind) 1.1597 +{ 1.1598 + RootedValue protov(cx); 1.1599 + if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov)) 1.1600 + return nullptr; 1.1601 + JSObject *proto; 1.1602 + if (protov.isObject()) 1.1603 + proto = &protov.toObject(); 1.1604 + else 1.1605 + proto = nullptr; 1.1606 + JSObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind); 1.1607 + 1.1608 + if (obj && newKind == SingletonObject) { 1.1609 + RootedObject nobj(cx, obj); 1.1610 + 1.1611 + /* Reshape the singleton before passing it as the 'this' value. */ 1.1612 + JSObject::clear(cx, nobj); 1.1613 + 1.1614 + JSScript *calleeScript = callee->as<JSFunction>().nonLazyScript(); 1.1615 + TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj)); 1.1616 + 1.1617 + return nobj; 1.1618 + } 1.1619 + 1.1620 + return obj; 1.1621 +} 1.1622 + 1.1623 +/* 1.1624 + * Given pc pointing after a property accessing bytecode, return true if the 1.1625 + * access is "object-detecting" in the sense used by web scripts, e.g., when 1.1626 + * checking whether document.all is defined. 1.1627 + */ 1.1628 +static bool 1.1629 +Detecting(JSContext *cx, JSScript *script, jsbytecode *pc) 1.1630 +{ 1.1631 + JS_ASSERT(script->containsPC(pc)); 1.1632 + 1.1633 + /* General case: a branch or equality op follows the access. */ 1.1634 + JSOp op = JSOp(*pc); 1.1635 + if (js_CodeSpec[op].format & JOF_DETECTING) 1.1636 + return true; 1.1637 + 1.1638 + jsbytecode *endpc = script->codeEnd(); 1.1639 + 1.1640 + if (op == JSOP_NULL) { 1.1641 + /* 1.1642 + * Special case #1: handle (document.all == null). Don't sweat 1.1643 + * about JS1.2's revision of the equality operators here. 1.1644 + */ 1.1645 + if (++pc < endpc) { 1.1646 + op = JSOp(*pc); 1.1647 + return op == JSOP_EQ || op == JSOP_NE; 1.1648 + } 1.1649 + return false; 1.1650 + } 1.1651 + 1.1652 + if (op == JSOP_GETGNAME || op == JSOP_NAME) { 1.1653 + /* 1.1654 + * Special case #2: handle (document.all == undefined). Don't worry 1.1655 + * about a local variable named |undefined| shadowing the immutable 1.1656 + * global binding...because, really? 1.1657 + */ 1.1658 + JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc)); 1.1659 + if (atom == cx->names().undefined && 1.1660 + (pc += js_CodeSpec[op].length) < endpc) { 1.1661 + op = JSOp(*pc); 1.1662 + return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE; 1.1663 + } 1.1664 + } 1.1665 + 1.1666 + return false; 1.1667 +} 1.1668 + 1.1669 +/* static */ bool 1.1670 +JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj, 1.1671 + HandleId id, MutableHandleValue vp, bool strict) 1.1672 +{ 1.1673 + if (MOZ_UNLIKELY(obj->watched())) { 1.1674 + WatchpointMap *wpmap = cx->compartment()->watchpointMap; 1.1675 + if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp)) 1.1676 + return false; 1.1677 + } 1.1678 + return obj->getOps()->setGeneric(cx, obj, id, vp, strict); 1.1679 +} 1.1680 + 1.1681 +/* static */ bool 1.1682 +JSObject::nonNativeSetElement(JSContext *cx, HandleObject obj, 1.1683 + uint32_t index, MutableHandleValue vp, bool strict) 1.1684 +{ 1.1685 + if (MOZ_UNLIKELY(obj->watched())) { 1.1686 + RootedId id(cx); 1.1687 + if (!IndexToId(cx, index, &id)) 1.1688 + return false; 1.1689 + 1.1690 + WatchpointMap *wpmap = cx->compartment()->watchpointMap; 1.1691 + if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp)) 1.1692 + return false; 1.1693 + } 1.1694 + return obj->getOps()->setElement(cx, obj, index, vp, strict); 1.1695 +} 1.1696 + 1.1697 +/* static */ bool 1.1698 +JSObject::deleteByValue(JSContext *cx, HandleObject obj, const Value &property, bool *succeeded) 1.1699 +{ 1.1700 + uint32_t index; 1.1701 + if (IsDefinitelyIndex(property, &index)) 1.1702 + return deleteElement(cx, obj, index, succeeded); 1.1703 + 1.1704 + RootedValue propval(cx, property); 1.1705 + 1.1706 + JSAtom *name = ToAtom<CanGC>(cx, propval); 1.1707 + if (!name) 1.1708 + return false; 1.1709 + 1.1710 + if (name->isIndex(&index)) 1.1711 + return deleteElement(cx, obj, index, succeeded); 1.1712 + 1.1713 + Rooted<PropertyName*> propname(cx, name->asPropertyName()); 1.1714 + return deleteProperty(cx, obj, propname, succeeded); 1.1715 +} 1.1716 + 1.1717 +JS_FRIEND_API(bool) 1.1718 +JS_CopyPropertyFrom(JSContext *cx, HandleId id, HandleObject target, 1.1719 + HandleObject obj) 1.1720 +{ 1.1721 + // |obj| and |cx| are generally not same-compartment with |target| here. 1.1722 + assertSameCompartment(cx, obj, id); 1.1723 + Rooted<JSPropertyDescriptor> desc(cx); 1.1724 + 1.1725 + if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) 1.1726 + return false; 1.1727 + MOZ_ASSERT(desc.object()); 1.1728 + 1.1729 + // Silently skip JSPropertyOp-implemented accessors. 1.1730 + if (desc.getter() && !desc.hasGetterObject()) 1.1731 + return true; 1.1732 + if (desc.setter() && !desc.hasSetterObject()) 1.1733 + return true; 1.1734 + 1.1735 + JSAutoCompartment ac(cx, target); 1.1736 + RootedId wrappedId(cx, id); 1.1737 + if (!cx->compartment()->wrap(cx, &desc)) 1.1738 + return false; 1.1739 + if (!cx->compartment()->wrapId(cx, wrappedId.address())) 1.1740 + return false; 1.1741 + 1.1742 + bool ignored; 1.1743 + return DefineOwnProperty(cx, target, wrappedId, desc, &ignored); 1.1744 +} 1.1745 + 1.1746 +JS_FRIEND_API(bool) 1.1747 +JS_CopyPropertiesFrom(JSContext *cx, HandleObject target, HandleObject obj) 1.1748 +{ 1.1749 + JSAutoCompartment ac(cx, obj); 1.1750 + 1.1751 + AutoIdVector props(cx); 1.1752 + if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &props)) 1.1753 + return false; 1.1754 + 1.1755 + for (size_t i = 0; i < props.length(); ++i) { 1.1756 + if (!JS_CopyPropertyFrom(cx, props.handleAt(i), target, obj)) 1.1757 + return false; 1.1758 + } 1.1759 + 1.1760 + return true; 1.1761 +} 1.1762 + 1.1763 +static bool 1.1764 +CopySlots(JSContext *cx, HandleObject from, HandleObject to) 1.1765 +{ 1.1766 + JS_ASSERT(!from->isNative() && !to->isNative()); 1.1767 + JS_ASSERT(from->getClass() == to->getClass()); 1.1768 + 1.1769 + size_t n = 0; 1.1770 + if (from->is<WrapperObject>() && 1.1771 + (Wrapper::wrapperHandler(from)->flags() & 1.1772 + Wrapper::CROSS_COMPARTMENT)) { 1.1773 + to->setSlot(0, from->getSlot(0)); 1.1774 + to->setSlot(1, from->getSlot(1)); 1.1775 + n = 2; 1.1776 + } 1.1777 + 1.1778 + size_t span = JSCLASS_RESERVED_SLOTS(from->getClass()); 1.1779 + RootedValue v(cx); 1.1780 + for (; n < span; ++n) { 1.1781 + v = from->getSlot(n); 1.1782 + if (!cx->compartment()->wrap(cx, &v)) 1.1783 + return false; 1.1784 + to->setSlot(n, v); 1.1785 + } 1.1786 + return true; 1.1787 +} 1.1788 + 1.1789 +JSObject * 1.1790 +js::CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent) 1.1791 +{ 1.1792 + if (!obj->isNative() && !obj->is<ProxyObject>()) { 1.1793 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.1794 + JSMSG_CANT_CLONE_OBJECT); 1.1795 + return nullptr; 1.1796 + } 1.1797 + 1.1798 + RootedObject clone(cx, NewObjectWithGivenProto(cx, obj->getClass(), proto, parent)); 1.1799 + if (!clone) 1.1800 + return nullptr; 1.1801 + if (obj->isNative()) { 1.1802 + if (clone->is<JSFunction>() && (obj->compartment() != clone->compartment())) { 1.1803 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.1804 + JSMSG_CANT_CLONE_OBJECT); 1.1805 + return nullptr; 1.1806 + } 1.1807 + 1.1808 + if (obj->hasPrivate()) 1.1809 + clone->setPrivate(obj->getPrivate()); 1.1810 + } else { 1.1811 + JS_ASSERT(obj->is<ProxyObject>()); 1.1812 + if (!CopySlots(cx, obj, clone)) 1.1813 + return nullptr; 1.1814 + } 1.1815 + 1.1816 + return clone; 1.1817 +} 1.1818 + 1.1819 +JSObject * 1.1820 +js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind) 1.1821 +{ 1.1822 + /* NB: Keep this in sync with XDRObjectLiteral. */ 1.1823 + JS_ASSERT(JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()); 1.1824 + JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>()); 1.1825 + 1.1826 + // Result of the clone function. 1.1827 + RootedObject clone(cx); 1.1828 + 1.1829 + // Temporary element/slot which would be stored in the cloned object. 1.1830 + RootedValue v(cx); 1.1831 + RootedObject deepObj(cx); 1.1832 + 1.1833 + if (obj->getClass() == &ArrayObject::class_) { 1.1834 + clone = NewDenseUnallocatedArray(cx, obj->as<ArrayObject>().length(), nullptr, newKind); 1.1835 + } else { 1.1836 + // Object literals are tenured by default as holded by the JSScript. 1.1837 + JS_ASSERT(obj->isTenured()); 1.1838 + AllocKind kind = obj->tenuredGetAllocKind(); 1.1839 + Rooted<TypeObject*> typeObj(cx, obj->getType(cx)); 1.1840 + if (!typeObj) 1.1841 + return nullptr; 1.1842 + RootedObject parent(cx, obj->getParent()); 1.1843 + clone = NewObjectWithGivenProto(cx, &JSObject::class_, typeObj->proto().toObject(), 1.1844 + parent, kind, newKind); 1.1845 + } 1.1846 + 1.1847 + // Allocate the same number of slots. 1.1848 + if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity())) 1.1849 + return nullptr; 1.1850 + 1.1851 + // Copy the number of initialized elements. 1.1852 + uint32_t initialized = obj->getDenseInitializedLength(); 1.1853 + if (initialized) 1.1854 + clone->setDenseInitializedLength(initialized); 1.1855 + 1.1856 + // Recursive copy of dense element. 1.1857 + for (uint32_t i = 0; i < initialized; ++i) { 1.1858 + v = obj->getDenseElement(i); 1.1859 + if (v.isObject()) { 1.1860 + deepObj = &v.toObject(); 1.1861 + deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind); 1.1862 + if (!deepObj) { 1.1863 + JS_ReportOutOfMemory(cx); 1.1864 + return nullptr; 1.1865 + } 1.1866 + v.setObject(*deepObj); 1.1867 + } 1.1868 + clone->initDenseElement(i, v); 1.1869 + } 1.1870 + 1.1871 + JS_ASSERT(obj->compartment() == clone->compartment()); 1.1872 + JS_ASSERT(!obj->hasPrivate()); 1.1873 + RootedShape shape(cx, obj->lastProperty()); 1.1874 + size_t span = shape->slotSpan(); 1.1875 + clone->setLastProperty(cx, clone, shape); 1.1876 + for (size_t i = 0; i < span; i++) { 1.1877 + v = obj->getSlot(i); 1.1878 + if (v.isObject()) { 1.1879 + deepObj = &v.toObject(); 1.1880 + deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind); 1.1881 + if (!deepObj) 1.1882 + return nullptr; 1.1883 + v.setObject(*deepObj); 1.1884 + } 1.1885 + clone->setSlot(i, v); 1.1886 + } 1.1887 + 1.1888 + if (obj->getClass() == &ArrayObject::class_) 1.1889 + FixArrayType(cx, clone); 1.1890 + else 1.1891 + FixObjectType(cx, clone); 1.1892 + 1.1893 +#ifdef DEBUG 1.1894 + Rooted<TypeObject*> typeObj(cx, obj->getType(cx)); 1.1895 + Rooted<TypeObject*> cloneTypeObj(cx, clone->getType(cx)); 1.1896 + if (!typeObj || !cloneTypeObj) 1.1897 + return nullptr; 1.1898 + JS_ASSERT(typeObj == cloneTypeObj); 1.1899 +#endif 1.1900 + 1.1901 + return clone; 1.1902 +} 1.1903 + 1.1904 +template<XDRMode mode> 1.1905 +bool 1.1906 +js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj) 1.1907 +{ 1.1908 + /* NB: Keep this in sync with DeepCloneObjectLiteral. */ 1.1909 + 1.1910 + JSContext *cx = xdr->cx(); 1.1911 + JS_ASSERT_IF(mode == XDR_ENCODE, JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()); 1.1912 + 1.1913 + // Distinguish between objects and array classes. 1.1914 + uint32_t isArray = 0; 1.1915 + { 1.1916 + if (mode == XDR_ENCODE) { 1.1917 + JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>()); 1.1918 + isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0; 1.1919 + } 1.1920 + 1.1921 + if (!xdr->codeUint32(&isArray)) 1.1922 + return false; 1.1923 + } 1.1924 + 1.1925 + if (isArray) { 1.1926 + uint32_t length; 1.1927 + 1.1928 + if (mode == XDR_ENCODE) 1.1929 + length = obj->as<ArrayObject>().length(); 1.1930 + 1.1931 + if (!xdr->codeUint32(&length)) 1.1932 + return false; 1.1933 + 1.1934 + if (mode == XDR_DECODE) 1.1935 + obj.set(NewDenseUnallocatedArray(cx, length, NULL, js::MaybeSingletonObject)); 1.1936 + 1.1937 + } else { 1.1938 + // Code the alloc kind of the object. 1.1939 + AllocKind kind; 1.1940 + { 1.1941 + if (mode == XDR_ENCODE) { 1.1942 + JS_ASSERT(obj->getClass() == &JSObject::class_); 1.1943 + JS_ASSERT(obj->isTenured()); 1.1944 + kind = obj->tenuredGetAllocKind(); 1.1945 + } 1.1946 + 1.1947 + if (!xdr->codeEnum32(&kind)) 1.1948 + return false; 1.1949 + 1.1950 + if (mode == XDR_DECODE) 1.1951 + obj.set(NewBuiltinClassInstance(cx, &JSObject::class_, kind, js::MaybeSingletonObject)); 1.1952 + } 1.1953 + } 1.1954 + 1.1955 + { 1.1956 + uint32_t capacity; 1.1957 + if (mode == XDR_ENCODE) 1.1958 + capacity = obj->getDenseCapacity(); 1.1959 + if (!xdr->codeUint32(&capacity)) 1.1960 + return false; 1.1961 + if (mode == XDR_DECODE) { 1.1962 + if (!obj->ensureElements(cx, capacity)) { 1.1963 + JS_ReportOutOfMemory(cx); 1.1964 + return false; 1.1965 + } 1.1966 + } 1.1967 + } 1.1968 + 1.1969 + uint32_t initialized; 1.1970 + { 1.1971 + if (mode == XDR_ENCODE) 1.1972 + initialized = obj->getDenseInitializedLength(); 1.1973 + if (!xdr->codeUint32(&initialized)) 1.1974 + return false; 1.1975 + if (mode == XDR_DECODE) { 1.1976 + if (initialized) 1.1977 + obj->setDenseInitializedLength(initialized); 1.1978 + } 1.1979 + } 1.1980 + 1.1981 + RootedValue tmpValue(cx); 1.1982 + 1.1983 + // Recursively copy dense elements. 1.1984 + { 1.1985 + for (unsigned i = 0; i < initialized; i++) { 1.1986 + if (mode == XDR_ENCODE) 1.1987 + tmpValue = obj->getDenseElement(i); 1.1988 + 1.1989 + if (!xdr->codeConstValue(&tmpValue)) 1.1990 + return false; 1.1991 + 1.1992 + if (mode == XDR_DECODE) 1.1993 + obj->initDenseElement(i, tmpValue); 1.1994 + } 1.1995 + } 1.1996 + 1.1997 + JS_ASSERT(!obj->hasPrivate()); 1.1998 + RootedShape shape(cx, obj->lastProperty()); 1.1999 + 1.2000 + // Code the number of slots in the vector. 1.2001 + unsigned nslot = 0; 1.2002 + 1.2003 + // Code ids of the object in order. As opposed to DeepCloneObjectLiteral we 1.2004 + // cannot just re-use the shape of the original bytecode value and we have 1.2005 + // to write down the shape as well as the corresponding values. Ideally we 1.2006 + // would have a mechanism to serialize the shape too. 1.2007 + js::AutoIdVector ids(cx); 1.2008 + { 1.2009 + if (mode == XDR_ENCODE && !shape->isEmptyShape()) { 1.2010 + nslot = shape->slotSpan(); 1.2011 + if (!ids.reserve(nslot)) 1.2012 + return false; 1.2013 + 1.2014 + for (unsigned i = 0; i < nslot; i++) 1.2015 + ids.infallibleAppend(JSID_VOID); 1.2016 + 1.2017 + for (Shape::Range<NoGC> it(shape); !it.empty(); it.popFront()) { 1.2018 + // If we have reached the native property of the array class, we 1.2019 + // exit as the remaining would only be reserved slots. 1.2020 + if (!it.front().hasSlot()) { 1.2021 + JS_ASSERT(isArray); 1.2022 + break; 1.2023 + } 1.2024 + 1.2025 + JS_ASSERT(it.front().hasDefaultGetter()); 1.2026 + ids[it.front().slot()] = it.front().propid(); 1.2027 + } 1.2028 + } 1.2029 + 1.2030 + if (!xdr->codeUint32(&nslot)) 1.2031 + return false; 1.2032 + 1.2033 + RootedAtom atom(cx); 1.2034 + RootedId id(cx); 1.2035 + uint32_t idType = 0; 1.2036 + for (unsigned i = 0; i < nslot; i++) { 1.2037 + if (mode == XDR_ENCODE) { 1.2038 + id = ids[i]; 1.2039 + if (JSID_IS_INT(id)) 1.2040 + idType = JSID_TYPE_INT; 1.2041 + else if (JSID_IS_ATOM(id)) 1.2042 + idType = JSID_TYPE_STRING; 1.2043 + else 1.2044 + MOZ_ASSUME_UNREACHABLE("Object property is not yet supported by XDR."); 1.2045 + 1.2046 + tmpValue = obj->getSlot(i); 1.2047 + } 1.2048 + 1.2049 + if (!xdr->codeUint32(&idType)) 1.2050 + return false; 1.2051 + 1.2052 + if (idType == JSID_TYPE_STRING) { 1.2053 + if (mode == XDR_ENCODE) 1.2054 + atom = JSID_TO_ATOM(id); 1.2055 + if (!XDRAtom(xdr, &atom)) 1.2056 + return false; 1.2057 + if (mode == XDR_DECODE) 1.2058 + id = AtomToId(atom); 1.2059 + } else { 1.2060 + JS_ASSERT(idType == JSID_TYPE_INT); 1.2061 + uint32_t indexVal; 1.2062 + if (mode == XDR_ENCODE) 1.2063 + indexVal = uint32_t(JSID_TO_INT(id)); 1.2064 + if (!xdr->codeUint32(&indexVal)) 1.2065 + return false; 1.2066 + if (mode == XDR_DECODE) 1.2067 + id = INT_TO_JSID(int32_t(indexVal)); 1.2068 + } 1.2069 + 1.2070 + if (!xdr->codeConstValue(&tmpValue)) 1.2071 + return false; 1.2072 + 1.2073 + if (mode == XDR_DECODE) { 1.2074 + if (!DefineNativeProperty(cx, obj, id, tmpValue, NULL, NULL, JSPROP_ENUMERATE)) 1.2075 + return false; 1.2076 + } 1.2077 + } 1.2078 + 1.2079 + JS_ASSERT_IF(mode == XDR_DECODE, !obj->inDictionaryMode()); 1.2080 + } 1.2081 + 1.2082 + if (mode == XDR_DECODE) { 1.2083 + if (isArray) 1.2084 + FixArrayType(cx, obj); 1.2085 + else 1.2086 + FixObjectType(cx, obj); 1.2087 + } 1.2088 + 1.2089 + return true; 1.2090 +} 1.2091 + 1.2092 +template bool 1.2093 +js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleObject obj); 1.2094 + 1.2095 +template bool 1.2096 +js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj); 1.2097 + 1.2098 +JSObject * 1.2099 +js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj) 1.2100 +{ 1.2101 + Rooted<TypeObject*> typeObj(cx); 1.2102 + typeObj = cx->getNewType(&JSObject::class_, cx->global()->getOrCreateObjectPrototype(cx)); 1.2103 + 1.2104 + JS_ASSERT(srcObj->getClass() == &JSObject::class_); 1.2105 + AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->numFixedSlots())); 1.2106 + JS_ASSERT_IF(srcObj->isTenured(), kind == srcObj->tenuredGetAllocKind()); 1.2107 + 1.2108 + RootedShape shape(cx, srcObj->lastProperty()); 1.2109 + return NewReshapedObject(cx, typeObj, parent, kind, shape); 1.2110 +} 1.2111 + 1.2112 +struct JSObject::TradeGutsReserved { 1.2113 + Vector<Value> avals; 1.2114 + Vector<Value> bvals; 1.2115 + int newafixed; 1.2116 + int newbfixed; 1.2117 + RootedShape newashape; 1.2118 + RootedShape newbshape; 1.2119 + HeapSlot *newaslots; 1.2120 + HeapSlot *newbslots; 1.2121 + 1.2122 + TradeGutsReserved(JSContext *cx) 1.2123 + : avals(cx), bvals(cx), 1.2124 + newafixed(0), newbfixed(0), 1.2125 + newashape(cx), newbshape(cx), 1.2126 + newaslots(nullptr), newbslots(nullptr) 1.2127 + {} 1.2128 + 1.2129 + ~TradeGutsReserved() 1.2130 + { 1.2131 + js_free(newaslots); 1.2132 + js_free(newbslots); 1.2133 + } 1.2134 +}; 1.2135 + 1.2136 +bool 1.2137 +JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *aArg, JSObject *bArg, 1.2138 + TradeGutsReserved &reserved) 1.2139 +{ 1.2140 + /* 1.2141 + * Avoid GC in here to avoid confusing the tracing code with our 1.2142 + * intermediate state. 1.2143 + */ 1.2144 + AutoSuppressGC suppress(cx); 1.2145 + 1.2146 + RootedObject a(cx, aArg); 1.2147 + RootedObject b(cx, bArg); 1.2148 + JS_ASSERT(a->compartment() == b->compartment()); 1.2149 + AutoCompartment ac(cx, a); 1.2150 + 1.2151 + /* 1.2152 + * When performing multiple swaps between objects which may have different 1.2153 + * numbers of fixed slots, we reserve all space ahead of time so that the 1.2154 + * swaps can be performed infallibly. 1.2155 + */ 1.2156 + 1.2157 + /* 1.2158 + * Swap prototypes and classes on the two objects, so that TradeGuts can 1.2159 + * preserve the types of the two objects. 1.2160 + */ 1.2161 + const Class *aClass = a->getClass(); 1.2162 + const Class *bClass = b->getClass(); 1.2163 + Rooted<TaggedProto> aProto(cx, a->getTaggedProto()); 1.2164 + Rooted<TaggedProto> bProto(cx, b->getTaggedProto()); 1.2165 + bool success; 1.2166 + if (!SetClassAndProto(cx, a, bClass, bProto, &success) || !success) 1.2167 + return false; 1.2168 + if (!SetClassAndProto(cx, b, aClass, aProto, &success) || !success) 1.2169 + return false; 1.2170 + 1.2171 + if (a->tenuredSizeOfThis() == b->tenuredSizeOfThis()) 1.2172 + return true; 1.2173 + 1.2174 + /* 1.2175 + * If either object is native, it needs a new shape to preserve the 1.2176 + * invariant that objects with the same shape have the same number of 1.2177 + * inline slots. The fixed slots will be updated in place during TradeGuts. 1.2178 + * Non-native objects need to be reshaped according to the new count. 1.2179 + */ 1.2180 + if (a->isNative()) { 1.2181 + if (!a->generateOwnShape(cx)) 1.2182 + return false; 1.2183 + } else { 1.2184 + reserved.newbshape = EmptyShape::getInitialShape(cx, aClass, aProto, a->getParent(), a->getMetadata(), 1.2185 + b->tenuredGetAllocKind()); 1.2186 + if (!reserved.newbshape) 1.2187 + return false; 1.2188 + } 1.2189 + if (b->isNative()) { 1.2190 + if (!b->generateOwnShape(cx)) 1.2191 + return false; 1.2192 + } else { 1.2193 + reserved.newashape = EmptyShape::getInitialShape(cx, bClass, bProto, b->getParent(), b->getMetadata(), 1.2194 + a->tenuredGetAllocKind()); 1.2195 + if (!reserved.newashape) 1.2196 + return false; 1.2197 + } 1.2198 + 1.2199 + /* The avals/bvals vectors hold all original values from the objects. */ 1.2200 + 1.2201 + if (!reserved.avals.reserve(a->slotSpan())) 1.2202 + return false; 1.2203 + if (!reserved.bvals.reserve(b->slotSpan())) 1.2204 + return false; 1.2205 + 1.2206 + /* 1.2207 + * The newafixed/newbfixed hold the number of fixed slots in the objects 1.2208 + * after the swap. Adjust these counts according to whether the objects 1.2209 + * use their last fixed slot for storing private data. 1.2210 + */ 1.2211 + 1.2212 + reserved.newafixed = a->numFixedSlots(); 1.2213 + reserved.newbfixed = b->numFixedSlots(); 1.2214 + 1.2215 + if (aClass->hasPrivate()) { 1.2216 + reserved.newafixed++; 1.2217 + reserved.newbfixed--; 1.2218 + } 1.2219 + if (bClass->hasPrivate()) { 1.2220 + reserved.newbfixed++; 1.2221 + reserved.newafixed--; 1.2222 + } 1.2223 + 1.2224 + JS_ASSERT(reserved.newafixed >= 0); 1.2225 + JS_ASSERT(reserved.newbfixed >= 0); 1.2226 + 1.2227 + /* 1.2228 + * The newaslots/newbslots arrays hold any dynamic slots for the objects 1.2229 + * if they do not have enough fixed slots to accomodate the slots in the 1.2230 + * other object. 1.2231 + */ 1.2232 + 1.2233 + unsigned adynamic = dynamicSlotsCount(reserved.newafixed, b->slotSpan(), b->getClass()); 1.2234 + unsigned bdynamic = dynamicSlotsCount(reserved.newbfixed, a->slotSpan(), a->getClass()); 1.2235 + 1.2236 + if (adynamic) { 1.2237 + reserved.newaslots = cx->pod_malloc<HeapSlot>(adynamic); 1.2238 + if (!reserved.newaslots) 1.2239 + return false; 1.2240 + Debug_SetSlotRangeToCrashOnTouch(reserved.newaslots, adynamic); 1.2241 + } 1.2242 + if (bdynamic) { 1.2243 + reserved.newbslots = cx->pod_malloc<HeapSlot>(bdynamic); 1.2244 + if (!reserved.newbslots) 1.2245 + return false; 1.2246 + Debug_SetSlotRangeToCrashOnTouch(reserved.newbslots, bdynamic); 1.2247 + } 1.2248 + 1.2249 + return true; 1.2250 +} 1.2251 + 1.2252 +void 1.2253 +JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved) 1.2254 +{ 1.2255 + JS_ASSERT(a->compartment() == b->compartment()); 1.2256 + JS_ASSERT(a->is<JSFunction>() == b->is<JSFunction>()); 1.2257 + 1.2258 + /* 1.2259 + * Swap the object's types, to restore their initial type information. 1.2260 + * The prototypes and classes of the objects were swapped in ReserveForTradeGuts. 1.2261 + */ 1.2262 + TypeObject *tmp = a->type_; 1.2263 + a->type_ = b->type_; 1.2264 + b->type_ = tmp; 1.2265 + 1.2266 + /* Don't try to swap a JSFunction for a plain function JSObject. */ 1.2267 + JS_ASSERT_IF(a->is<JSFunction>(), a->tenuredSizeOfThis() == b->tenuredSizeOfThis()); 1.2268 + 1.2269 + /* 1.2270 + * Regexp guts are more complicated -- we would need to migrate the 1.2271 + * refcounted JIT code blob for them across compartments instead of just 1.2272 + * swapping guts. 1.2273 + */ 1.2274 + JS_ASSERT(!a->is<RegExpObject>() && !b->is<RegExpObject>()); 1.2275 + 1.2276 + /* Arrays can use their fixed storage for elements. */ 1.2277 + JS_ASSERT(!a->is<ArrayObject>() && !b->is<ArrayObject>()); 1.2278 + 1.2279 + /* 1.2280 + * Callers should not try to swap ArrayBuffer objects, 1.2281 + * these use a different slot representation from other objects. 1.2282 + */ 1.2283 + JS_ASSERT(!a->is<ArrayBufferObject>() && !b->is<ArrayBufferObject>()); 1.2284 + 1.2285 + /* Trade the guts of the objects. */ 1.2286 + const size_t size = a->tenuredSizeOfThis(); 1.2287 + if (size == b->tenuredSizeOfThis()) { 1.2288 + /* 1.2289 + * If the objects are the same size, then we make no assumptions about 1.2290 + * whether they have dynamically allocated slots and instead just copy 1.2291 + * them over wholesale. 1.2292 + */ 1.2293 + char tmp[mozilla::tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::value]; 1.2294 + JS_ASSERT(size <= sizeof(tmp)); 1.2295 + 1.2296 + js_memcpy(tmp, a, size); 1.2297 + js_memcpy(a, b, size); 1.2298 + js_memcpy(b, tmp, size); 1.2299 + 1.2300 +#ifdef JSGC_GENERATIONAL 1.2301 + /* 1.2302 + * Trigger post barriers for fixed slots. JSObject bits are barriered 1.2303 + * below, in common with the other case. 1.2304 + */ 1.2305 + for (size_t i = 0; i < a->numFixedSlots(); ++i) { 1.2306 + HeapSlot::writeBarrierPost(cx->runtime(), a, HeapSlot::Slot, i, a->getSlot(i)); 1.2307 + HeapSlot::writeBarrierPost(cx->runtime(), b, HeapSlot::Slot, i, b->getSlot(i)); 1.2308 + } 1.2309 +#endif 1.2310 + } else { 1.2311 + /* 1.2312 + * If the objects are of differing sizes, use the space we reserved 1.2313 + * earlier to save the slots from each object and then copy them into 1.2314 + * the new layout for the other object. 1.2315 + */ 1.2316 + 1.2317 + uint32_t acap = a->slotSpan(); 1.2318 + uint32_t bcap = b->slotSpan(); 1.2319 + 1.2320 + for (size_t i = 0; i < acap; i++) 1.2321 + reserved.avals.infallibleAppend(a->getSlot(i)); 1.2322 + 1.2323 + for (size_t i = 0; i < bcap; i++) 1.2324 + reserved.bvals.infallibleAppend(b->getSlot(i)); 1.2325 + 1.2326 + /* Done with the dynamic slots. */ 1.2327 + if (a->hasDynamicSlots()) 1.2328 + js_free(a->slots); 1.2329 + if (b->hasDynamicSlots()) 1.2330 + js_free(b->slots); 1.2331 + 1.2332 + void *apriv = a->hasPrivate() ? a->getPrivate() : nullptr; 1.2333 + void *bpriv = b->hasPrivate() ? b->getPrivate() : nullptr; 1.2334 + 1.2335 + char tmp[sizeof(JSObject)]; 1.2336 + js_memcpy(&tmp, a, sizeof tmp); 1.2337 + js_memcpy(a, b, sizeof tmp); 1.2338 + js_memcpy(b, &tmp, sizeof tmp); 1.2339 + 1.2340 + if (a->isNative()) 1.2341 + a->shape_->setNumFixedSlots(reserved.newafixed); 1.2342 + else 1.2343 + a->shape_ = reserved.newashape; 1.2344 + 1.2345 + a->slots = reserved.newaslots; 1.2346 + a->initSlotRange(0, reserved.bvals.begin(), bcap); 1.2347 + if (a->hasPrivate()) 1.2348 + a->initPrivate(bpriv); 1.2349 + 1.2350 + if (b->isNative()) 1.2351 + b->shape_->setNumFixedSlots(reserved.newbfixed); 1.2352 + else 1.2353 + b->shape_ = reserved.newbshape; 1.2354 + 1.2355 + b->slots = reserved.newbslots; 1.2356 + b->initSlotRange(0, reserved.avals.begin(), acap); 1.2357 + if (b->hasPrivate()) 1.2358 + b->initPrivate(apriv); 1.2359 + 1.2360 + /* Make sure the destructor for reserved doesn't free the slots. */ 1.2361 + reserved.newaslots = nullptr; 1.2362 + reserved.newbslots = nullptr; 1.2363 + } 1.2364 + 1.2365 +#ifdef JSGC_GENERATIONAL 1.2366 + Shape::writeBarrierPost(a->shape_, &a->shape_); 1.2367 + Shape::writeBarrierPost(b->shape_, &b->shape_); 1.2368 + types::TypeObject::writeBarrierPost(a->type_, &a->type_); 1.2369 + types::TypeObject::writeBarrierPost(b->type_, &b->type_); 1.2370 +#endif 1.2371 + 1.2372 + if (a->inDictionaryMode()) 1.2373 + a->lastProperty()->listp = &a->shape_; 1.2374 + if (b->inDictionaryMode()) 1.2375 + b->lastProperty()->listp = &b->shape_; 1.2376 + 1.2377 +#ifdef JSGC_INCREMENTAL 1.2378 + /* 1.2379 + * We need a write barrier here. If |a| was marked and |b| was not, then 1.2380 + * after the swap, |b|'s guts would never be marked. The write barrier 1.2381 + * solves this. 1.2382 + * 1.2383 + * Normally write barriers happen before the write. However, that's not 1.2384 + * necessary here because nothing is being destroyed. We're just swapping. 1.2385 + * We don't do the barrier before TradeGuts because ReserveForTradeGuts 1.2386 + * makes changes to the objects that might confuse the tracing code. 1.2387 + */ 1.2388 + JS::Zone *zone = a->zone(); 1.2389 + if (zone->needsBarrier()) { 1.2390 + MarkChildren(zone->barrierTracer(), a); 1.2391 + MarkChildren(zone->barrierTracer(), b); 1.2392 + } 1.2393 +#endif 1.2394 +} 1.2395 + 1.2396 +/* Use this method with extreme caution. It trades the guts of two objects. */ 1.2397 +bool 1.2398 +JSObject::swap(JSContext *cx, HandleObject a, HandleObject b) 1.2399 +{ 1.2400 + AutoMarkInDeadZone adc1(a->zone()); 1.2401 + AutoMarkInDeadZone adc2(b->zone()); 1.2402 + 1.2403 + // Ensure swap doesn't cause a finalizer to not be run. 1.2404 + JS_ASSERT(IsBackgroundFinalized(a->tenuredGetAllocKind()) == 1.2405 + IsBackgroundFinalized(b->tenuredGetAllocKind())); 1.2406 + JS_ASSERT(a->compartment() == b->compartment()); 1.2407 + 1.2408 + unsigned r = NotifyGCPreSwap(a, b); 1.2409 + 1.2410 + TradeGutsReserved reserved(cx); 1.2411 + if (!ReserveForTradeGuts(cx, a, b, reserved)) { 1.2412 + NotifyGCPostSwap(b, a, r); 1.2413 + return false; 1.2414 + } 1.2415 + TradeGuts(cx, a, b, reserved); 1.2416 + 1.2417 + NotifyGCPostSwap(a, b, r); 1.2418 + return true; 1.2419 +} 1.2420 + 1.2421 +static bool 1.2422 +DefineStandardSlot(JSContext *cx, HandleObject obj, JSProtoKey key, JSAtom *atom, 1.2423 + HandleValue v, uint32_t attrs, bool &named) 1.2424 +{ 1.2425 + RootedId id(cx, AtomToId(atom)); 1.2426 + 1.2427 + if (key != JSProto_Null) { 1.2428 + /* 1.2429 + * Initializing an actual standard class on a global object. If the 1.2430 + * property is not yet present, force it into a new one bound to a 1.2431 + * reserved slot. Otherwise, go through the normal property path. 1.2432 + */ 1.2433 + JS_ASSERT(obj->is<GlobalObject>()); 1.2434 + JS_ASSERT(obj->isNative()); 1.2435 + 1.2436 + if (!obj->nativeLookup(cx, id)) { 1.2437 + obj->as<GlobalObject>().setConstructorPropertySlot(key, v); 1.2438 + 1.2439 + uint32_t slot = GlobalObject::constructorPropertySlot(key); 1.2440 + if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0)) 1.2441 + return false; 1.2442 + 1.2443 + named = true; 1.2444 + return true; 1.2445 + } 1.2446 + } 1.2447 + 1.2448 + named = JSObject::defineGeneric(cx, obj, id, 1.2449 + v, JS_PropertyStub, JS_StrictPropertyStub, attrs); 1.2450 + return named; 1.2451 +} 1.2452 + 1.2453 +static void 1.2454 +SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto) 1.2455 +{ 1.2456 + JS_ASSERT(!obj->getParent()); 1.2457 + if (!obj->is<GlobalObject>()) 1.2458 + return; 1.2459 + 1.2460 + obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj)); 1.2461 + obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto)); 1.2462 +} 1.2463 + 1.2464 +static void 1.2465 +ClearClassObject(JSObject *obj, JSProtoKey key) 1.2466 +{ 1.2467 + JS_ASSERT(!obj->getParent()); 1.2468 + if (!obj->is<GlobalObject>()) 1.2469 + return; 1.2470 + 1.2471 + obj->as<GlobalObject>().setConstructor(key, UndefinedValue()); 1.2472 + obj->as<GlobalObject>().setPrototype(key, UndefinedValue()); 1.2473 +} 1.2474 + 1.2475 +static JSObject * 1.2476 +DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom, 1.2477 + JSObject *protoProto, const Class *clasp, 1.2478 + Native constructor, unsigned nargs, 1.2479 + const JSPropertySpec *ps, const JSFunctionSpec *fs, 1.2480 + const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs, 1.2481 + JSObject **ctorp, AllocKind ctorKind) 1.2482 +{ 1.2483 + /* 1.2484 + * Create a prototype object for this class. 1.2485 + * 1.2486 + * FIXME: lazy standard (built-in) class initialization and even older 1.2487 + * eager boostrapping code rely on all of these properties: 1.2488 + * 1.2489 + * 1. NewObject attempting to compute a default prototype object when 1.2490 + * passed null for proto; and 1.2491 + * 1.2492 + * 2. NewObject tolerating no default prototype (null proto slot value) 1.2493 + * due to this js_InitClass call coming from js_InitFunctionClass on an 1.2494 + * otherwise-uninitialized global. 1.2495 + * 1.2496 + * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is 1.2497 + * &JSFunction::class_, not a JSObject-sized (smaller) GC-thing. 1.2498 + * 1.2499 + * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to 1.2500 + * be &JSFunction::class_ (we could break compatibility easily). But 1.2501 + * fixing (3) is not enough without addressing the bootstrapping dependency 1.2502 + * on (1) and (2). 1.2503 + */ 1.2504 + 1.2505 + /* 1.2506 + * Create the prototype object. (GlobalObject::createBlankPrototype isn't 1.2507 + * used because it parents the prototype object to the global and because 1.2508 + * it uses WithProto::Given. FIXME: Undo dependencies on this parentage 1.2509 + * [which already needs to happen for bug 638316], figure out nicer 1.2510 + * semantics for null-protoProto, and use createBlankPrototype.) 1.2511 + */ 1.2512 + RootedObject proto(cx, NewObjectWithClassProto(cx, clasp, protoProto, obj, SingletonObject)); 1.2513 + if (!proto) 1.2514 + return nullptr; 1.2515 + 1.2516 + /* After this point, control must exit via label bad or out. */ 1.2517 + RootedObject ctor(cx); 1.2518 + bool named = false; 1.2519 + bool cached = false; 1.2520 + if (!constructor) { 1.2521 + /* 1.2522 + * Lacking a constructor, name the prototype (e.g., Math) unless this 1.2523 + * class (a) is anonymous, i.e. for internal use only; (b) the class 1.2524 + * of obj (the global object) is has a reserved slot indexed by key; 1.2525 + * and (c) key is not the null key. 1.2526 + */ 1.2527 + if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->is<GlobalObject>() || 1.2528 + key == JSProto_Null) 1.2529 + { 1.2530 + uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS) 1.2531 + ? JSPROP_READONLY | JSPROP_PERMANENT 1.2532 + : 0; 1.2533 + RootedValue value(cx, ObjectValue(*proto)); 1.2534 + if (!DefineStandardSlot(cx, obj, key, atom, value, attrs, named)) 1.2535 + goto bad; 1.2536 + } 1.2537 + 1.2538 + ctor = proto; 1.2539 + } else { 1.2540 + /* 1.2541 + * Create the constructor, not using GlobalObject::createConstructor 1.2542 + * because the constructor currently must have |obj| as its parent. 1.2543 + * (FIXME: remove this dependency on the exact identity of the parent, 1.2544 + * perhaps as part of bug 638316.) 1.2545 + */ 1.2546 + RootedFunction fun(cx, NewFunction(cx, js::NullPtr(), constructor, nargs, 1.2547 + JSFunction::NATIVE_CTOR, obj, atom, ctorKind)); 1.2548 + if (!fun) 1.2549 + goto bad; 1.2550 + 1.2551 + /* 1.2552 + * Set the class object early for standard class constructors. Type 1.2553 + * inference may need to access these, and js::GetBuiltinPrototype will 1.2554 + * fail if it tries to do a reentrant reconstruction of the class. 1.2555 + */ 1.2556 + if (key != JSProto_Null) { 1.2557 + SetClassObject(obj, key, fun, proto); 1.2558 + cached = true; 1.2559 + } 1.2560 + 1.2561 + RootedValue value(cx, ObjectValue(*fun)); 1.2562 + if (!DefineStandardSlot(cx, obj, key, atom, value, 0, named)) 1.2563 + goto bad; 1.2564 + 1.2565 + /* 1.2566 + * Optionally construct the prototype object, before the class has 1.2567 + * been fully initialized. Allow the ctor to replace proto with a 1.2568 + * different object, as is done for operator new. 1.2569 + */ 1.2570 + ctor = fun; 1.2571 + if (!LinkConstructorAndPrototype(cx, ctor, proto)) 1.2572 + goto bad; 1.2573 + 1.2574 + /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ 1.2575 + Rooted<TaggedProto> tagged(cx, TaggedProto(proto)); 1.2576 + if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, clasp, tagged)) 1.2577 + goto bad; 1.2578 + } 1.2579 + 1.2580 + if (!DefinePropertiesAndBrand(cx, proto, ps, fs) || 1.2581 + (ctor != proto && !DefinePropertiesAndBrand(cx, ctor, static_ps, static_fs))) 1.2582 + { 1.2583 + goto bad; 1.2584 + } 1.2585 + 1.2586 + /* If this is a standard class, cache its prototype. */ 1.2587 + if (!cached && key != JSProto_Null) 1.2588 + SetClassObject(obj, key, ctor, proto); 1.2589 + 1.2590 + if (ctorp) 1.2591 + *ctorp = ctor; 1.2592 + return proto; 1.2593 + 1.2594 +bad: 1.2595 + if (named) { 1.2596 + bool succeeded; 1.2597 + JSObject::deleteByValue(cx, obj, StringValue(atom), &succeeded); 1.2598 + } 1.2599 + if (cached) 1.2600 + ClearClassObject(obj, key); 1.2601 + return nullptr; 1.2602 +} 1.2603 + 1.2604 +JSObject * 1.2605 +js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto_, 1.2606 + const Class *clasp, Native constructor, unsigned nargs, 1.2607 + const JSPropertySpec *ps, const JSFunctionSpec *fs, 1.2608 + const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs, 1.2609 + JSObject **ctorp, AllocKind ctorKind) 1.2610 +{ 1.2611 + RootedObject protoProto(cx, protoProto_); 1.2612 + 1.2613 + /* Assert mandatory function pointer members. */ 1.2614 + JS_ASSERT(clasp->addProperty); 1.2615 + JS_ASSERT(clasp->delProperty); 1.2616 + JS_ASSERT(clasp->getProperty); 1.2617 + JS_ASSERT(clasp->setProperty); 1.2618 + JS_ASSERT(clasp->enumerate); 1.2619 + JS_ASSERT(clasp->resolve); 1.2620 + JS_ASSERT(clasp->convert); 1.2621 + 1.2622 + RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name))); 1.2623 + if (!atom) 1.2624 + return nullptr; 1.2625 + 1.2626 + /* 1.2627 + * All instances of the class will inherit properties from the prototype 1.2628 + * object we are about to create (in DefineConstructorAndPrototype), which 1.2629 + * in turn will inherit from protoProto. 1.2630 + * 1.2631 + * When initializing a standard class (other than Object), if protoProto is 1.2632 + * null, default to Object.prototype. The engine's internal uses of 1.2633 + * js_InitClass depend on this nicety. 1.2634 + */ 1.2635 + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp); 1.2636 + if (key != JSProto_Null && 1.2637 + !protoProto && 1.2638 + !GetBuiltinPrototype(cx, JSProto_Object, &protoProto)) 1.2639 + { 1.2640 + return nullptr; 1.2641 + } 1.2642 + 1.2643 + return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs, 1.2644 + ps, fs, static_ps, static_fs, ctorp, ctorKind); 1.2645 +} 1.2646 + 1.2647 +/* static */ inline bool 1.2648 +JSObject::updateSlotsForSpan(ThreadSafeContext *cx, 1.2649 + HandleObject obj, size_t oldSpan, size_t newSpan) 1.2650 +{ 1.2651 + JS_ASSERT(cx->isThreadLocal(obj)); 1.2652 + JS_ASSERT(oldSpan != newSpan); 1.2653 + 1.2654 + size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass()); 1.2655 + size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass()); 1.2656 + 1.2657 + if (oldSpan < newSpan) { 1.2658 + if (oldCount < newCount && !JSObject::growSlots(cx, obj, oldCount, newCount)) 1.2659 + return false; 1.2660 + 1.2661 + if (newSpan == oldSpan + 1) 1.2662 + obj->initSlotUnchecked(oldSpan, UndefinedValue()); 1.2663 + else 1.2664 + obj->initializeSlotRange(oldSpan, newSpan - oldSpan); 1.2665 + } else { 1.2666 + /* Trigger write barriers on the old slots before reallocating. */ 1.2667 + obj->prepareSlotRangeForOverwrite(newSpan, oldSpan); 1.2668 + obj->invalidateSlotRange(newSpan, oldSpan - newSpan); 1.2669 + 1.2670 + if (oldCount > newCount) 1.2671 + JSObject::shrinkSlots(cx, obj, oldCount, newCount); 1.2672 + } 1.2673 + 1.2674 + return true; 1.2675 +} 1.2676 + 1.2677 +/* static */ bool 1.2678 +JSObject::setLastProperty(ThreadSafeContext *cx, HandleObject obj, HandleShape shape) 1.2679 +{ 1.2680 + JS_ASSERT(cx->isThreadLocal(obj)); 1.2681 + JS_ASSERT(!obj->inDictionaryMode()); 1.2682 + JS_ASSERT(!shape->inDictionary()); 1.2683 + JS_ASSERT(shape->compartment() == obj->compartment()); 1.2684 + JS_ASSERT(shape->numFixedSlots() == obj->numFixedSlots()); 1.2685 + 1.2686 + size_t oldSpan = obj->lastProperty()->slotSpan(); 1.2687 + size_t newSpan = shape->slotSpan(); 1.2688 + 1.2689 + if (oldSpan == newSpan) { 1.2690 + obj->shape_ = shape; 1.2691 + return true; 1.2692 + } 1.2693 + 1.2694 + if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan)) 1.2695 + return false; 1.2696 + 1.2697 + obj->shape_ = shape; 1.2698 + return true; 1.2699 +} 1.2700 + 1.2701 +/* static */ bool 1.2702 +JSObject::setSlotSpan(ThreadSafeContext *cx, HandleObject obj, uint32_t span) 1.2703 +{ 1.2704 + JS_ASSERT(cx->isThreadLocal(obj)); 1.2705 + JS_ASSERT(obj->inDictionaryMode()); 1.2706 + 1.2707 + size_t oldSpan = obj->lastProperty()->base()->slotSpan(); 1.2708 + if (oldSpan == span) 1.2709 + return true; 1.2710 + 1.2711 + if (!JSObject::updateSlotsForSpan(cx, obj, oldSpan, span)) 1.2712 + return false; 1.2713 + 1.2714 + obj->lastProperty()->base()->setSlotSpan(span); 1.2715 + return true; 1.2716 +} 1.2717 + 1.2718 +static HeapSlot * 1.2719 +AllocateSlots(ThreadSafeContext *cx, JSObject *obj, uint32_t nslots) 1.2720 +{ 1.2721 +#ifdef JSGC_GENERATIONAL 1.2722 + if (cx->isJSContext()) 1.2723 + return cx->asJSContext()->runtime()->gcNursery.allocateSlots(cx->asJSContext(), obj, nslots); 1.2724 +#endif 1.2725 + return cx->pod_malloc<HeapSlot>(nslots); 1.2726 +} 1.2727 + 1.2728 +static HeapSlot * 1.2729 +ReallocateSlots(ThreadSafeContext *cx, JSObject *obj, HeapSlot *oldSlots, 1.2730 + uint32_t oldCount, uint32_t newCount) 1.2731 +{ 1.2732 +#ifdef JSGC_GENERATIONAL 1.2733 + if (cx->isJSContext()) { 1.2734 + return cx->asJSContext()->runtime()->gcNursery.reallocateSlots(cx->asJSContext(), 1.2735 + obj, oldSlots, 1.2736 + oldCount, newCount); 1.2737 + } 1.2738 +#endif 1.2739 + return (HeapSlot *)cx->realloc_(oldSlots, oldCount * sizeof(HeapSlot), 1.2740 + newCount * sizeof(HeapSlot)); 1.2741 +} 1.2742 + 1.2743 +/* static */ bool 1.2744 +JSObject::growSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount) 1.2745 +{ 1.2746 + JS_ASSERT(cx->isThreadLocal(obj)); 1.2747 + JS_ASSERT(newCount > oldCount); 1.2748 + JS_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN); 1.2749 + 1.2750 + /* 1.2751 + * Slot capacities are determined by the span of allocated objects. Due to 1.2752 + * the limited number of bits to store shape slots, object growth is 1.2753 + * throttled well before the slot capacity can overflow. 1.2754 + */ 1.2755 + JS_ASSERT(newCount < NELEMENTS_LIMIT); 1.2756 + 1.2757 + /* 1.2758 + * If we are allocating slots for an object whose type is always created 1.2759 + * by calling 'new' on a particular script, bump the GC kind for that 1.2760 + * type to give these objects a larger number of fixed slots when future 1.2761 + * objects are constructed. 1.2762 + */ 1.2763 + if (!obj->hasLazyType() && !oldCount && obj->type()->hasNewScript()) { 1.2764 + JSObject *oldTemplate = obj->type()->newScript()->templateObject; 1.2765 + gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(oldTemplate->numFixedSlots()); 1.2766 + uint32_t newScriptSlots = gc::GetGCKindSlots(kind); 1.2767 + if (newScriptSlots == obj->numFixedSlots() && 1.2768 + gc::TryIncrementAllocKind(&kind) && 1.2769 + cx->isJSContext()) 1.2770 + { 1.2771 + JSContext *ncx = cx->asJSContext(); 1.2772 + AutoEnterAnalysis enter(ncx); 1.2773 + 1.2774 + Rooted<TypeObject*> typeObj(cx, obj->type()); 1.2775 + RootedShape shape(cx, oldTemplate->lastProperty()); 1.2776 + JSObject *reshapedObj = NewReshapedObject(ncx, typeObj, obj->getParent(), kind, shape, 1.2777 + MaybeSingletonObject); 1.2778 + if (!reshapedObj) 1.2779 + return false; 1.2780 + 1.2781 + typeObj->newScript()->templateObject = reshapedObj; 1.2782 + typeObj->markStateChange(ncx); 1.2783 + } 1.2784 + } 1.2785 + 1.2786 + if (!oldCount) { 1.2787 + obj->slots = AllocateSlots(cx, obj, newCount); 1.2788 + if (!obj->slots) 1.2789 + return false; 1.2790 + Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount); 1.2791 + return true; 1.2792 + } 1.2793 + 1.2794 + HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount); 1.2795 + if (!newslots) 1.2796 + return false; /* Leave slots at its old size. */ 1.2797 + 1.2798 + obj->slots = newslots; 1.2799 + 1.2800 + Debug_SetSlotRangeToCrashOnTouch(obj->slots + oldCount, newCount - oldCount); 1.2801 + 1.2802 + return true; 1.2803 +} 1.2804 + 1.2805 +static void 1.2806 +FreeSlots(ThreadSafeContext *cx, HeapSlot *slots) 1.2807 +{ 1.2808 + // Note: threads without a JSContext do not have access to nursery allocated things. 1.2809 +#ifdef JSGC_GENERATIONAL 1.2810 + if (cx->isJSContext()) 1.2811 + return cx->asJSContext()->runtime()->gcNursery.freeSlots(cx->asJSContext(), slots); 1.2812 +#endif 1.2813 + js_free(slots); 1.2814 +} 1.2815 + 1.2816 +/* static */ void 1.2817 +JSObject::shrinkSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount) 1.2818 +{ 1.2819 + JS_ASSERT(cx->isThreadLocal(obj)); 1.2820 + JS_ASSERT(newCount < oldCount); 1.2821 + 1.2822 + if (newCount == 0) { 1.2823 + FreeSlots(cx, obj->slots); 1.2824 + obj->slots = nullptr; 1.2825 + return; 1.2826 + } 1.2827 + 1.2828 + JS_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN); 1.2829 + 1.2830 + HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount); 1.2831 + if (!newslots) 1.2832 + return; /* Leave slots at its old size. */ 1.2833 + 1.2834 + obj->slots = newslots; 1.2835 +} 1.2836 + 1.2837 +/* static */ bool 1.2838 +JSObject::sparsifyDenseElement(ExclusiveContext *cx, HandleObject obj, uint32_t index) 1.2839 +{ 1.2840 + RootedValue value(cx, obj->getDenseElement(index)); 1.2841 + JS_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE)); 1.2842 + 1.2843 + JSObject::removeDenseElementForSparseIndex(cx, obj, index); 1.2844 + 1.2845 + uint32_t slot = obj->slotSpan(); 1.2846 + if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) { 1.2847 + obj->setDenseElement(index, value); 1.2848 + return false; 1.2849 + } 1.2850 + 1.2851 + JS_ASSERT(slot == obj->slotSpan() - 1); 1.2852 + obj->initSlot(slot, value); 1.2853 + 1.2854 + return true; 1.2855 +} 1.2856 + 1.2857 +/* static */ bool 1.2858 +JSObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleObject obj) 1.2859 +{ 1.2860 + uint32_t initialized = obj->getDenseInitializedLength(); 1.2861 + 1.2862 + /* Create new properties with the value of non-hole dense elements. */ 1.2863 + for (uint32_t i = 0; i < initialized; i++) { 1.2864 + if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) 1.2865 + continue; 1.2866 + 1.2867 + if (!sparsifyDenseElement(cx, obj, i)) 1.2868 + return false; 1.2869 + } 1.2870 + 1.2871 + if (initialized) 1.2872 + obj->setDenseInitializedLength(0); 1.2873 + 1.2874 + /* 1.2875 + * Reduce storage for dense elements which are now holes. Explicitly mark 1.2876 + * the elements capacity as zero, so that any attempts to add dense 1.2877 + * elements will be caught in ensureDenseElements. 1.2878 + */ 1.2879 + if (obj->getDenseCapacity()) { 1.2880 + obj->shrinkElements(cx, 0); 1.2881 + obj->getElementsHeader()->capacity = 0; 1.2882 + } 1.2883 + 1.2884 + return true; 1.2885 +} 1.2886 + 1.2887 +bool 1.2888 +JSObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint) 1.2889 +{ 1.2890 + JS_ASSERT(isNative()); 1.2891 + JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX); 1.2892 + 1.2893 + uint32_t cap = getDenseCapacity(); 1.2894 + JS_ASSERT(requiredCapacity >= cap); 1.2895 + 1.2896 + if (requiredCapacity >= NELEMENTS_LIMIT) 1.2897 + return true; 1.2898 + 1.2899 + uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO; 1.2900 + if (newElementsHint >= minimalDenseCount) 1.2901 + return false; 1.2902 + minimalDenseCount -= newElementsHint; 1.2903 + 1.2904 + if (minimalDenseCount > cap) 1.2905 + return true; 1.2906 + 1.2907 + uint32_t len = getDenseInitializedLength(); 1.2908 + const Value *elems = getDenseElements(); 1.2909 + for (uint32_t i = 0; i < len; i++) { 1.2910 + if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount) 1.2911 + return false; 1.2912 + } 1.2913 + return true; 1.2914 +} 1.2915 + 1.2916 +/* static */ JSObject::EnsureDenseResult 1.2917 +JSObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleObject obj) 1.2918 +{ 1.2919 + /* 1.2920 + * Wait until after the object goes into dictionary mode, which must happen 1.2921 + * when sparsely packing any array with more than MIN_SPARSE_INDEX elements 1.2922 + * (see PropertyTree::MAX_HEIGHT). 1.2923 + */ 1.2924 + if (!obj->inDictionaryMode()) 1.2925 + return ED_SPARSE; 1.2926 + 1.2927 + /* 1.2928 + * Only measure the number of indexed properties every log(n) times when 1.2929 + * populating the object. 1.2930 + */ 1.2931 + uint32_t slotSpan = obj->slotSpan(); 1.2932 + if (slotSpan != RoundUpPow2(slotSpan)) 1.2933 + return ED_SPARSE; 1.2934 + 1.2935 + /* Watch for conditions under which an object's elements cannot be dense. */ 1.2936 + if (!obj->nonProxyIsExtensible() || obj->watched()) 1.2937 + return ED_SPARSE; 1.2938 + 1.2939 + /* 1.2940 + * The indexes in the object need to be sufficiently dense before they can 1.2941 + * be converted to dense mode. 1.2942 + */ 1.2943 + uint32_t numDenseElements = 0; 1.2944 + uint32_t newInitializedLength = 0; 1.2945 + 1.2946 + RootedShape shape(cx, obj->lastProperty()); 1.2947 + while (!shape->isEmptyShape()) { 1.2948 + uint32_t index; 1.2949 + if (js_IdIsIndex(shape->propid(), &index)) { 1.2950 + if (shape->attributes() == JSPROP_ENUMERATE && 1.2951 + shape->hasDefaultGetter() && 1.2952 + shape->hasDefaultSetter()) 1.2953 + { 1.2954 + numDenseElements++; 1.2955 + newInitializedLength = Max(newInitializedLength, index + 1); 1.2956 + } else { 1.2957 + /* 1.2958 + * For simplicity, only densify the object if all indexed 1.2959 + * properties can be converted to dense elements. 1.2960 + */ 1.2961 + return ED_SPARSE; 1.2962 + } 1.2963 + } 1.2964 + shape = shape->previous(); 1.2965 + } 1.2966 + 1.2967 + if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength) 1.2968 + return ED_SPARSE; 1.2969 + 1.2970 + if (newInitializedLength >= NELEMENTS_LIMIT) 1.2971 + return ED_SPARSE; 1.2972 + 1.2973 + /* 1.2974 + * This object meets all necessary restrictions, convert all indexed 1.2975 + * properties into dense elements. 1.2976 + */ 1.2977 + 1.2978 + if (newInitializedLength > obj->getDenseCapacity()) { 1.2979 + if (!obj->growElements(cx, newInitializedLength)) 1.2980 + return ED_FAILED; 1.2981 + } 1.2982 + 1.2983 + obj->ensureDenseInitializedLength(cx, newInitializedLength, 0); 1.2984 + 1.2985 + RootedValue value(cx); 1.2986 + 1.2987 + shape = obj->lastProperty(); 1.2988 + while (!shape->isEmptyShape()) { 1.2989 + jsid id = shape->propid(); 1.2990 + uint32_t index; 1.2991 + if (js_IdIsIndex(id, &index)) { 1.2992 + value = obj->getSlot(shape->slot()); 1.2993 + 1.2994 + /* 1.2995 + * When removing a property from a dictionary, the specified 1.2996 + * property will be removed from the dictionary list and the 1.2997 + * last property will then be changed due to reshaping the object. 1.2998 + * Compute the next shape in the traverse, watching for such 1.2999 + * removals from the list. 1.3000 + */ 1.3001 + if (shape != obj->lastProperty()) { 1.3002 + shape = shape->previous(); 1.3003 + if (!obj->removeProperty(cx, id)) 1.3004 + return ED_FAILED; 1.3005 + } else { 1.3006 + if (!obj->removeProperty(cx, id)) 1.3007 + return ED_FAILED; 1.3008 + shape = obj->lastProperty(); 1.3009 + } 1.3010 + 1.3011 + obj->setDenseElement(index, value); 1.3012 + } else { 1.3013 + shape = shape->previous(); 1.3014 + } 1.3015 + } 1.3016 + 1.3017 + /* 1.3018 + * All indexed properties on the object are now dense, clear the indexed 1.3019 + * flag so that we will not start using sparse indexes again if we need 1.3020 + * to grow the object. 1.3021 + */ 1.3022 + if (!obj->clearFlag(cx, BaseShape::INDEXED)) 1.3023 + return ED_FAILED; 1.3024 + 1.3025 + return ED_OK; 1.3026 +} 1.3027 + 1.3028 +static ObjectElements * 1.3029 +AllocateElements(ThreadSafeContext *cx, JSObject *obj, uint32_t nelems) 1.3030 +{ 1.3031 +#ifdef JSGC_GENERATIONAL 1.3032 + if (cx->isJSContext()) 1.3033 + return cx->asJSContext()->runtime()->gcNursery.allocateElements(cx->asJSContext(), obj, nelems); 1.3034 +#endif 1.3035 + 1.3036 + return static_cast<js::ObjectElements *>(cx->malloc_(nelems * sizeof(HeapValue))); 1.3037 +} 1.3038 + 1.3039 +static ObjectElements * 1.3040 +ReallocateElements(ThreadSafeContext *cx, JSObject *obj, ObjectElements *oldHeader, 1.3041 + uint32_t oldCount, uint32_t newCount) 1.3042 +{ 1.3043 +#ifdef JSGC_GENERATIONAL 1.3044 + if (cx->isJSContext()) { 1.3045 + return cx->asJSContext()->runtime()->gcNursery.reallocateElements(cx->asJSContext(), obj, 1.3046 + oldHeader, oldCount, 1.3047 + newCount); 1.3048 + } 1.3049 +#endif 1.3050 + 1.3051 + return static_cast<js::ObjectElements *>(cx->realloc_(oldHeader, oldCount * sizeof(HeapSlot), 1.3052 + newCount * sizeof(HeapSlot))); 1.3053 +} 1.3054 + 1.3055 +bool 1.3056 +JSObject::growElements(ThreadSafeContext *cx, uint32_t newcap) 1.3057 +{ 1.3058 + JS_ASSERT(nonProxyIsExtensible()); 1.3059 + JS_ASSERT(canHaveNonEmptyElements()); 1.3060 + 1.3061 + /* 1.3062 + * When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to 1.3063 + * grow, double its capacity, to add N elements in amortized O(N) time. 1.3064 + * 1.3065 + * Above this limit, grow by 12.5% each time. Speed is still amortized 1.3066 + * O(N), with a higher constant factor, and we waste less space. 1.3067 + */ 1.3068 + static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024; 1.3069 + static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value); 1.3070 + 1.3071 + uint32_t oldcap = getDenseCapacity(); 1.3072 + JS_ASSERT(oldcap <= newcap); 1.3073 + 1.3074 + uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) 1.3075 + ? oldcap * 2 1.3076 + : oldcap + (oldcap >> 3); 1.3077 + 1.3078 + uint32_t actualCapacity; 1.3079 + if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) { 1.3080 + JS_ASSERT(newcap <= as<ArrayObject>().length()); 1.3081 + // Preserve the |capacity <= length| invariant for arrays with 1.3082 + // non-writable length. See also js::ArraySetLength which initially 1.3083 + // enforces this requirement. 1.3084 + actualCapacity = newcap; 1.3085 + } else { 1.3086 + actualCapacity = Max(newcap, nextsize); 1.3087 + if (actualCapacity >= CAPACITY_CHUNK) 1.3088 + actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK); 1.3089 + else if (actualCapacity < SLOT_CAPACITY_MIN) 1.3090 + actualCapacity = SLOT_CAPACITY_MIN; 1.3091 + 1.3092 + /* Don't let nelements get close to wrapping around uint32_t. */ 1.3093 + if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) 1.3094 + return false; 1.3095 + } 1.3096 + 1.3097 + uint32_t initlen = getDenseInitializedLength(); 1.3098 + uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER; 1.3099 + uint32_t newAllocated = actualCapacity + ObjectElements::VALUES_PER_HEADER; 1.3100 + 1.3101 + ObjectElements *newheader; 1.3102 + if (hasDynamicElements()) { 1.3103 + newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated); 1.3104 + if (!newheader) 1.3105 + return false; /* Leave elements as its old size. */ 1.3106 + } else { 1.3107 + newheader = AllocateElements(cx, this, newAllocated); 1.3108 + if (!newheader) 1.3109 + return false; /* Leave elements as its old size. */ 1.3110 + js_memcpy(newheader, getElementsHeader(), 1.3111 + (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value)); 1.3112 + } 1.3113 + 1.3114 + newheader->capacity = actualCapacity; 1.3115 + elements = newheader->elements(); 1.3116 + 1.3117 + Debug_SetSlotRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen); 1.3118 + 1.3119 + return true; 1.3120 +} 1.3121 + 1.3122 +void 1.3123 +JSObject::shrinkElements(ThreadSafeContext *cx, uint32_t newcap) 1.3124 +{ 1.3125 + JS_ASSERT(cx->isThreadLocal(this)); 1.3126 + JS_ASSERT(canHaveNonEmptyElements()); 1.3127 + 1.3128 + uint32_t oldcap = getDenseCapacity(); 1.3129 + JS_ASSERT(newcap <= oldcap); 1.3130 + 1.3131 + // Don't shrink elements below the minimum capacity. 1.3132 + if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements()) 1.3133 + return; 1.3134 + 1.3135 + newcap = Max(newcap, SLOT_CAPACITY_MIN); 1.3136 + 1.3137 + uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER; 1.3138 + uint32_t newAllocated = newcap + ObjectElements::VALUES_PER_HEADER; 1.3139 + 1.3140 + ObjectElements *newheader = ReallocateElements(cx, this, getElementsHeader(), 1.3141 + oldAllocated, newAllocated); 1.3142 + if (!newheader) { 1.3143 + cx->recoverFromOutOfMemory(); 1.3144 + return; // Leave elements at its old size. 1.3145 + } 1.3146 + 1.3147 + newheader->capacity = newcap; 1.3148 + elements = newheader->elements(); 1.3149 +} 1.3150 + 1.3151 +bool 1.3152 +js::SetClassAndProto(JSContext *cx, HandleObject obj, 1.3153 + const Class *clasp, Handle<js::TaggedProto> proto, 1.3154 + bool *succeeded) 1.3155 +{ 1.3156 + /* 1.3157 + * Regenerate shapes for all of the scopes along the old prototype chain, 1.3158 + * in case any entries were filled by looking up through obj. Stop when a 1.3159 + * non-native object is found, prototype lookups will not be cached across 1.3160 + * these. 1.3161 + * 1.3162 + * How this shape change is done is very delicate; the change can be made 1.3163 + * either by marking the object's prototype as uncacheable (such that the 1.3164 + * property cache and JIT'ed ICs cannot assume the shape determines the 1.3165 + * prototype) or by just generating a new shape for the object. Choosing 1.3166 + * the former is bad if the object is on the prototype chain of other 1.3167 + * objects, as the uncacheable prototype can inhibit iterator caches on 1.3168 + * those objects and slow down prototype accesses. Choosing the latter is 1.3169 + * bad if there are many similar objects to this one which will have their 1.3170 + * prototype mutated, as the generateOwnShape forces the object into 1.3171 + * dictionary mode and similar property lineages will be repeatedly cloned. 1.3172 + * 1.3173 + * :XXX: bug 707717 make this code less brittle. 1.3174 + */ 1.3175 + *succeeded = false; 1.3176 + RootedObject oldproto(cx, obj); 1.3177 + while (oldproto && oldproto->isNative()) { 1.3178 + if (oldproto->hasSingletonType()) { 1.3179 + if (!oldproto->generateOwnShape(cx)) 1.3180 + return false; 1.3181 + } else { 1.3182 + if (!oldproto->setUncacheableProto(cx)) 1.3183 + return false; 1.3184 + } 1.3185 + oldproto = oldproto->getProto(); 1.3186 + } 1.3187 + 1.3188 + if (obj->hasSingletonType()) { 1.3189 + /* 1.3190 + * Just splice the prototype, but mark the properties as unknown for 1.3191 + * consistent behavior. 1.3192 + */ 1.3193 + if (!obj->splicePrototype(cx, clasp, proto)) 1.3194 + return false; 1.3195 + MarkTypeObjectUnknownProperties(cx, obj->type()); 1.3196 + *succeeded = true; 1.3197 + return true; 1.3198 + } 1.3199 + 1.3200 + if (proto.isObject()) { 1.3201 + RootedObject protoObj(cx, proto.toObject()); 1.3202 + if (!JSObject::setNewTypeUnknown(cx, clasp, protoObj)) 1.3203 + return false; 1.3204 + } 1.3205 + 1.3206 + TypeObject *type = cx->getNewType(clasp, proto); 1.3207 + if (!type) 1.3208 + return false; 1.3209 + 1.3210 + /* 1.3211 + * Setting __proto__ on an object that has escaped and may be referenced by 1.3212 + * other heap objects can only be done if the properties of both objects 1.3213 + * are unknown. Type sets containing this object will contain the original 1.3214 + * type but not the new type of the object, so we need to go and scan the 1.3215 + * entire compartment for type sets which have these objects and mark them 1.3216 + * as containing generic objects. 1.3217 + */ 1.3218 + MarkTypeObjectUnknownProperties(cx, obj->type(), true); 1.3219 + MarkTypeObjectUnknownProperties(cx, type, true); 1.3220 + 1.3221 + obj->setType(type); 1.3222 + 1.3223 + *succeeded = true; 1.3224 + return true; 1.3225 +} 1.3226 + 1.3227 +static bool 1.3228 +MaybeResolveConstructor(ExclusiveContext *cxArg, Handle<GlobalObject*> global, JSProtoKey key) 1.3229 +{ 1.3230 + if (global->isStandardClassResolved(key)) 1.3231 + return true; 1.3232 + if (!cxArg->shouldBeJSContext()) 1.3233 + return false; 1.3234 + 1.3235 + JSContext *cx = cxArg->asJSContext(); 1.3236 + return GlobalObject::resolveConstructor(cx, global, key); 1.3237 +} 1.3238 + 1.3239 +bool 1.3240 +js::GetBuiltinConstructor(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp) 1.3241 +{ 1.3242 + MOZ_ASSERT(key != JSProto_Null); 1.3243 + Rooted<GlobalObject*> global(cx, cx->global()); 1.3244 + if (!MaybeResolveConstructor(cx, global, key)) 1.3245 + return false; 1.3246 + 1.3247 + objp.set(&global->getConstructor(key).toObject()); 1.3248 + return true; 1.3249 +} 1.3250 + 1.3251 +bool 1.3252 +js::GetBuiltinPrototype(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject protop) 1.3253 +{ 1.3254 + MOZ_ASSERT(key != JSProto_Null); 1.3255 + Rooted<GlobalObject*> global(cx, cx->global()); 1.3256 + if (!MaybeResolveConstructor(cx, global, key)) 1.3257 + return false; 1.3258 + 1.3259 + protop.set(&global->getPrototype(key).toObject()); 1.3260 + return true; 1.3261 +} 1.3262 + 1.3263 +static bool 1.3264 +IsStandardPrototype(JSObject *obj, JSProtoKey key) 1.3265 +{ 1.3266 + GlobalObject &global = obj->global(); 1.3267 + Value v = global.getPrototype(key); 1.3268 + return v.isObject() && obj == &v.toObject(); 1.3269 +} 1.3270 + 1.3271 +JSProtoKey 1.3272 +JS::IdentifyStandardInstance(JSObject *obj) 1.3273 +{ 1.3274 + // Note: The prototype shares its JSClass with instances. 1.3275 + JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>()); 1.3276 + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); 1.3277 + if (key != JSProto_Null && !IsStandardPrototype(obj, key)) 1.3278 + return key; 1.3279 + return JSProto_Null; 1.3280 +} 1.3281 + 1.3282 +JSProtoKey 1.3283 +JS::IdentifyStandardPrototype(JSObject *obj) 1.3284 +{ 1.3285 + // Note: The prototype shares its JSClass with instances. 1.3286 + JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>()); 1.3287 + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); 1.3288 + if (key != JSProto_Null && IsStandardPrototype(obj, key)) 1.3289 + return key; 1.3290 + return JSProto_Null; 1.3291 +} 1.3292 + 1.3293 +JSProtoKey 1.3294 +JS::IdentifyStandardInstanceOrPrototype(JSObject *obj) 1.3295 +{ 1.3296 + return JSCLASS_CACHED_PROTO_KEY(obj->getClass()); 1.3297 +} 1.3298 + 1.3299 +bool 1.3300 +js::FindClassObject(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp) 1.3301 +{ 1.3302 + JSProtoKey protoKey = GetClassProtoKey(clasp); 1.3303 + if (protoKey != JSProto_Null) { 1.3304 + JS_ASSERT(JSProto_Null < protoKey); 1.3305 + JS_ASSERT(protoKey < JSProto_LIMIT); 1.3306 + return GetBuiltinConstructor(cx, protoKey, protop); 1.3307 + } 1.3308 + 1.3309 + JSAtom *atom = Atomize(cx, clasp->name, strlen(clasp->name)); 1.3310 + if (!atom) 1.3311 + return false; 1.3312 + RootedId id(cx, AtomToId(atom)); 1.3313 + 1.3314 + RootedObject pobj(cx); 1.3315 + RootedShape shape(cx); 1.3316 + if (!LookupNativeProperty(cx, cx->global(), id, &pobj, &shape)) 1.3317 + return false; 1.3318 + RootedValue v(cx); 1.3319 + if (shape && pobj->isNative()) { 1.3320 + if (shape->hasSlot()) 1.3321 + v = pobj->nativeGetSlot(shape->slot()); 1.3322 + } 1.3323 + if (v.isObject()) 1.3324 + protop.set(&v.toObject()); 1.3325 + return true; 1.3326 +} 1.3327 + 1.3328 +/* static */ bool 1.3329 +JSObject::allocSlot(ThreadSafeContext *cx, HandleObject obj, uint32_t *slotp) 1.3330 +{ 1.3331 + JS_ASSERT(cx->isThreadLocal(obj)); 1.3332 + 1.3333 + uint32_t slot = obj->slotSpan(); 1.3334 + JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass())); 1.3335 + 1.3336 + /* 1.3337 + * If this object is in dictionary mode, try to pull a free slot from the 1.3338 + * shape table's slot-number freelist. 1.3339 + */ 1.3340 + if (obj->inDictionaryMode()) { 1.3341 + ShapeTable &table = obj->lastProperty()->table(); 1.3342 + uint32_t last = table.freelist; 1.3343 + if (last != SHAPE_INVALID_SLOT) { 1.3344 +#ifdef DEBUG 1.3345 + JS_ASSERT(last < slot); 1.3346 + uint32_t next = obj->getSlot(last).toPrivateUint32(); 1.3347 + JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot); 1.3348 +#endif 1.3349 + 1.3350 + *slotp = last; 1.3351 + 1.3352 + const Value &vref = obj->getSlot(last); 1.3353 + table.freelist = vref.toPrivateUint32(); 1.3354 + obj->setSlot(last, UndefinedValue()); 1.3355 + return true; 1.3356 + } 1.3357 + } 1.3358 + 1.3359 + if (slot >= SHAPE_MAXIMUM_SLOT) { 1.3360 + js_ReportOutOfMemory(cx); 1.3361 + return false; 1.3362 + } 1.3363 + 1.3364 + *slotp = slot; 1.3365 + 1.3366 + if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1)) 1.3367 + return false; 1.3368 + 1.3369 + return true; 1.3370 +} 1.3371 + 1.3372 +void 1.3373 +JSObject::freeSlot(uint32_t slot) 1.3374 +{ 1.3375 + JS_ASSERT(slot < slotSpan()); 1.3376 + 1.3377 + if (inDictionaryMode()) { 1.3378 + uint32_t &last = lastProperty()->table().freelist; 1.3379 + 1.3380 + /* Can't afford to check the whole freelist, but let's check the head. */ 1.3381 + JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot); 1.3382 + 1.3383 + /* 1.3384 + * Place all freed slots other than reserved slots (bug 595230) on the 1.3385 + * dictionary's free list. 1.3386 + */ 1.3387 + if (JSSLOT_FREE(getClass()) <= slot) { 1.3388 + JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan()); 1.3389 + setSlot(slot, PrivateUint32Value(last)); 1.3390 + last = slot; 1.3391 + return; 1.3392 + } 1.3393 + } 1.3394 + setSlot(slot, UndefinedValue()); 1.3395 +} 1.3396 + 1.3397 +static bool 1.3398 +PurgeProtoChain(ExclusiveContext *cx, JSObject *objArg, HandleId id) 1.3399 +{ 1.3400 + /* Root locally so we can re-assign. */ 1.3401 + RootedObject obj(cx, objArg); 1.3402 + 1.3403 + RootedShape shape(cx); 1.3404 + while (obj) { 1.3405 + /* Lookups will not be cached through non-native protos. */ 1.3406 + if (!obj->isNative()) 1.3407 + break; 1.3408 + 1.3409 + shape = obj->nativeLookup(cx, id); 1.3410 + if (shape) { 1.3411 + if (!obj->shadowingShapeChange(cx, *shape)) 1.3412 + return false; 1.3413 + 1.3414 + obj->shadowingShapeChange(cx, *shape); 1.3415 + return true; 1.3416 + } 1.3417 + obj = obj->getProto(); 1.3418 + } 1.3419 + 1.3420 + return true; 1.3421 +} 1.3422 + 1.3423 +static bool 1.3424 +PurgeScopeChainHelper(ExclusiveContext *cx, HandleObject objArg, HandleId id) 1.3425 +{ 1.3426 + /* Re-root locally so we can re-assign. */ 1.3427 + RootedObject obj(cx, objArg); 1.3428 + 1.3429 + JS_ASSERT(obj->isNative()); 1.3430 + JS_ASSERT(obj->isDelegate()); 1.3431 + 1.3432 + /* Lookups on integer ids cannot be cached through prototypes. */ 1.3433 + if (JSID_IS_INT(id)) 1.3434 + return true; 1.3435 + 1.3436 + PurgeProtoChain(cx, obj->getProto(), id); 1.3437 + 1.3438 + /* 1.3439 + * We must purge the scope chain only for Call objects as they are the only 1.3440 + * kind of cacheable non-global object that can gain properties after outer 1.3441 + * properties with the same names have been cached or traced. Call objects 1.3442 + * may gain such properties via eval introducing new vars; see bug 490364. 1.3443 + */ 1.3444 + if (obj->is<CallObject>()) { 1.3445 + while ((obj = obj->enclosingScope()) != nullptr) { 1.3446 + if (!PurgeProtoChain(cx, obj, id)) 1.3447 + return false; 1.3448 + } 1.3449 + } 1.3450 + 1.3451 + return true; 1.3452 +} 1.3453 + 1.3454 +/* 1.3455 + * PurgeScopeChain does nothing if obj is not itself a prototype or parent 1.3456 + * scope, else it reshapes the scope and prototype chains it links. It calls 1.3457 + * PurgeScopeChainHelper, which asserts that obj is flagged as a delegate 1.3458 + * (i.e., obj has ever been on a prototype or parent chain). 1.3459 + */ 1.3460 +static inline bool 1.3461 +PurgeScopeChain(ExclusiveContext *cx, JS::HandleObject obj, JS::HandleId id) 1.3462 +{ 1.3463 + if (obj->isDelegate()) 1.3464 + return PurgeScopeChainHelper(cx, obj, id); 1.3465 + return true; 1.3466 +} 1.3467 + 1.3468 +bool 1.3469 +baseops::DefineGeneric(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, 1.3470 + PropertyOp getter, StrictPropertyOp setter, unsigned attrs) 1.3471 +{ 1.3472 + return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs); 1.3473 +} 1.3474 + 1.3475 +/* static */ bool 1.3476 +JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj, 1.3477 + HandleId id, HandleValue value, 1.3478 + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) 1.3479 +{ 1.3480 + JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS)); 1.3481 + js::DefineGenericOp op = obj->getOps()->defineGeneric; 1.3482 + if (op) { 1.3483 + if (!cx->shouldBeJSContext()) 1.3484 + return false; 1.3485 + return op(cx->asJSContext(), obj, id, value, getter, setter, attrs); 1.3486 + } 1.3487 + return baseops::DefineGeneric(cx, obj, id, value, getter, setter, attrs); 1.3488 +} 1.3489 + 1.3490 +/* static */ bool 1.3491 +JSObject::defineProperty(ExclusiveContext *cx, HandleObject obj, 1.3492 + PropertyName *name, HandleValue value, 1.3493 + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) 1.3494 +{ 1.3495 + RootedId id(cx, NameToId(name)); 1.3496 + return defineGeneric(cx, obj, id, value, getter, setter, attrs); 1.3497 +} 1.3498 + 1.3499 +bool 1.3500 +baseops::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value, 1.3501 + PropertyOp getter, StrictPropertyOp setter, unsigned attrs) 1.3502 +{ 1.3503 + RootedId id(cx); 1.3504 + if (index <= JSID_INT_MAX) { 1.3505 + id = INT_TO_JSID(index); 1.3506 + return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs); 1.3507 + } 1.3508 + 1.3509 + AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); 1.3510 + 1.3511 + if (!IndexToId(cx, index, &id)) 1.3512 + return false; 1.3513 + 1.3514 + return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs); 1.3515 +} 1.3516 + 1.3517 +/* static */ bool 1.3518 +JSObject::defineElement(ExclusiveContext *cx, HandleObject obj, 1.3519 + uint32_t index, HandleValue value, 1.3520 + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) 1.3521 +{ 1.3522 + js::DefineElementOp op = obj->getOps()->defineElement; 1.3523 + if (op) { 1.3524 + if (!cx->shouldBeJSContext()) 1.3525 + return false; 1.3526 + return op(cx->asJSContext(), obj, index, value, getter, setter, attrs); 1.3527 + } 1.3528 + return baseops::DefineElement(cx, obj, index, value, getter, setter, attrs); 1.3529 +} 1.3530 + 1.3531 +Shape * 1.3532 +JSObject::addDataProperty(ExclusiveContext *cx, jsid idArg, uint32_t slot, unsigned attrs) 1.3533 +{ 1.3534 + JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); 1.3535 + RootedObject self(cx, this); 1.3536 + RootedId id(cx, idArg); 1.3537 + return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0); 1.3538 +} 1.3539 + 1.3540 +Shape * 1.3541 +JSObject::addDataProperty(ExclusiveContext *cx, HandlePropertyName name, 1.3542 + uint32_t slot, unsigned attrs) 1.3543 +{ 1.3544 + JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); 1.3545 + RootedObject self(cx, this); 1.3546 + RootedId id(cx, NameToId(name)); 1.3547 + return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0); 1.3548 +} 1.3549 + 1.3550 +/* 1.3551 + * Backward compatibility requires allowing addProperty hooks to mutate the 1.3552 + * nominal initial value of a slotful property, while GC safety wants that 1.3553 + * value to be stored before the call-out through the hook. Optimize to do 1.3554 + * both while saving cycles for classes that stub their addProperty hook. 1.3555 + */ 1.3556 +template <ExecutionMode mode> 1.3557 +static inline bool 1.3558 +CallAddPropertyHook(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg, 1.3559 + const Class *clasp, HandleObject obj, HandleShape shape, 1.3560 + HandleValue nominal) 1.3561 +{ 1.3562 + if (clasp->addProperty != JS_PropertyStub) { 1.3563 + if (mode == ParallelExecution) 1.3564 + return false; 1.3565 + 1.3566 + ExclusiveContext *cx = cxArg->asExclusiveContext(); 1.3567 + if (!cx->shouldBeJSContext()) 1.3568 + return false; 1.3569 + 1.3570 + /* Make a local copy of value so addProperty can mutate its inout parameter. */ 1.3571 + RootedValue value(cx, nominal); 1.3572 + 1.3573 + Rooted<jsid> id(cx, shape->propid()); 1.3574 + if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) { 1.3575 + obj->removeProperty(cx, shape->propid()); 1.3576 + return false; 1.3577 + } 1.3578 + if (value.get() != nominal) { 1.3579 + if (shape->hasSlot()) 1.3580 + obj->nativeSetSlotWithType(cx, shape, value); 1.3581 + } 1.3582 + } 1.3583 + return true; 1.3584 +} 1.3585 + 1.3586 +template <ExecutionMode mode> 1.3587 +static inline bool 1.3588 +CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg, 1.3589 + const Class *clasp, HandleObject obj, uint32_t index, 1.3590 + HandleValue nominal) 1.3591 +{ 1.3592 + /* Inline addProperty for array objects. */ 1.3593 + if (obj->is<ArrayObject>()) { 1.3594 + ArrayObject *arr = &obj->as<ArrayObject>(); 1.3595 + uint32_t length = arr->length(); 1.3596 + if (index >= length) { 1.3597 + if (mode == ParallelExecution) { 1.3598 + /* We cannot deal with overflows in parallel. */ 1.3599 + if (length > INT32_MAX) 1.3600 + return false; 1.3601 + arr->setLengthInt32(index + 1); 1.3602 + } else { 1.3603 + arr->setLength(cxArg->asExclusiveContext(), index + 1); 1.3604 + } 1.3605 + } 1.3606 + return true; 1.3607 + } 1.3608 + 1.3609 + if (clasp->addProperty != JS_PropertyStub) { 1.3610 + if (mode == ParallelExecution) 1.3611 + return false; 1.3612 + 1.3613 + ExclusiveContext *cx = cxArg->asExclusiveContext(); 1.3614 + if (!cx->shouldBeJSContext()) 1.3615 + return false; 1.3616 + 1.3617 + /* Make a local copy of value so addProperty can mutate its inout parameter. */ 1.3618 + RootedValue value(cx, nominal); 1.3619 + 1.3620 + Rooted<jsid> id(cx, INT_TO_JSID(index)); 1.3621 + if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) { 1.3622 + obj->setDenseElementHole(cx, index); 1.3623 + return false; 1.3624 + } 1.3625 + if (value.get() != nominal) 1.3626 + obj->setDenseElementWithType(cx, index, value); 1.3627 + } 1.3628 + 1.3629 + return true; 1.3630 +} 1.3631 + 1.3632 +template <ExecutionMode mode> 1.3633 +static bool 1.3634 +UpdateShapeTypeAndValue(typename ExecutionModeTraits<mode>::ExclusiveContextType cx, 1.3635 + JSObject *obj, Shape *shape, const Value &value) 1.3636 +{ 1.3637 + jsid id = shape->propid(); 1.3638 + if (shape->hasSlot()) { 1.3639 + if (mode == ParallelExecution) { 1.3640 + if (!obj->nativeSetSlotIfHasType(shape, value)) 1.3641 + return false; 1.3642 + } else { 1.3643 + obj->nativeSetSlotWithType(cx->asExclusiveContext(), shape, value); 1.3644 + } 1.3645 + } 1.3646 + if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter()) { 1.3647 + if (mode == ParallelExecution) { 1.3648 + if (!IsTypePropertyIdMarkedNonData(obj, id)) 1.3649 + return false; 1.3650 + } else { 1.3651 + MarkTypePropertyNonData(cx->asExclusiveContext(), obj, id); 1.3652 + } 1.3653 + } 1.3654 + if (!shape->writable()) { 1.3655 + if (mode == ParallelExecution) { 1.3656 + if (!IsTypePropertyIdMarkedNonWritable(obj, id)) 1.3657 + return false; 1.3658 + } else { 1.3659 + MarkTypePropertyNonWritable(cx->asExclusiveContext(), obj, id); 1.3660 + } 1.3661 + } 1.3662 + return true; 1.3663 +} 1.3664 + 1.3665 +template <ExecutionMode mode> 1.3666 +static inline bool 1.3667 +DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType cx, 1.3668 + HandleObject obj, HandleId id, 1.3669 + PropertyOp getter, StrictPropertyOp setter, 1.3670 + unsigned attrs, HandleValue value, 1.3671 + bool callSetterAfterwards, bool setterIsStrict) 1.3672 +{ 1.3673 + /* Use dense storage for new indexed properties where possible. */ 1.3674 + if (JSID_IS_INT(id) && 1.3675 + getter == JS_PropertyStub && 1.3676 + setter == JS_StrictPropertyStub && 1.3677 + attrs == JSPROP_ENUMERATE && 1.3678 + (!obj->isIndexed() || !obj->nativeContainsPure(id)) && 1.3679 + !obj->is<TypedArrayObject>()) 1.3680 + { 1.3681 + uint32_t index = JSID_TO_INT(id); 1.3682 + bool definesPast; 1.3683 + if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast)) 1.3684 + return false; 1.3685 + if (definesPast) 1.3686 + return true; 1.3687 + 1.3688 + JSObject::EnsureDenseResult result; 1.3689 + if (mode == ParallelExecution) { 1.3690 + if (obj->writeToIndexWouldMarkNotPacked(index)) 1.3691 + return false; 1.3692 + result = obj->ensureDenseElementsPreservePackedFlag(cx, index, 1); 1.3693 + } else { 1.3694 + result = obj->ensureDenseElements(cx->asExclusiveContext(), index, 1); 1.3695 + } 1.3696 + 1.3697 + if (result == JSObject::ED_FAILED) 1.3698 + return false; 1.3699 + if (result == JSObject::ED_OK) { 1.3700 + if (mode == ParallelExecution) { 1.3701 + if (!obj->setDenseElementIfHasType(index, value)) 1.3702 + return false; 1.3703 + } else { 1.3704 + obj->setDenseElementWithType(cx->asExclusiveContext(), index, value); 1.3705 + } 1.3706 + return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value); 1.3707 + } 1.3708 + } 1.3709 + 1.3710 + if (obj->is<ArrayObject>()) { 1.3711 + Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>()); 1.3712 + if (id == NameToId(cx->names().length)) { 1.3713 + if (mode == SequentialExecution && !cx->shouldBeJSContext()) 1.3714 + return false; 1.3715 + return ArraySetLength<mode>(ExecutionModeTraits<mode>::toContextType(cx), arr, id, 1.3716 + attrs, value, setterIsStrict); 1.3717 + } 1.3718 + 1.3719 + uint32_t index; 1.3720 + if (js_IdIsIndex(id, &index)) { 1.3721 + bool definesPast; 1.3722 + if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast)) 1.3723 + return false; 1.3724 + if (definesPast) 1.3725 + return true; 1.3726 + } 1.3727 + } 1.3728 + 1.3729 + // Don't define new indexed properties on typed arrays. 1.3730 + if (obj->is<TypedArrayObject>()) { 1.3731 + uint64_t index; 1.3732 + if (IsTypedArrayIndex(id, &index)) 1.3733 + return true; 1.3734 + } 1.3735 + 1.3736 + AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); 1.3737 + 1.3738 + RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter, 1.3739 + SHAPE_INVALID_SLOT, attrs, 0)); 1.3740 + if (!shape) 1.3741 + return false; 1.3742 + 1.3743 + if (!UpdateShapeTypeAndValue<mode>(cx, obj, shape, value)) 1.3744 + return false; 1.3745 + 1.3746 + /* 1.3747 + * Clear any existing dense index after adding a sparse indexed property, 1.3748 + * and investigate converting the object to dense indexes. 1.3749 + */ 1.3750 + if (JSID_IS_INT(id)) { 1.3751 + if (mode == ParallelExecution) 1.3752 + return false; 1.3753 + 1.3754 + ExclusiveContext *ncx = cx->asExclusiveContext(); 1.3755 + uint32_t index = JSID_TO_INT(id); 1.3756 + JSObject::removeDenseElementForSparseIndex(ncx, obj, index); 1.3757 + JSObject::EnsureDenseResult result = JSObject::maybeDensifySparseElements(ncx, obj); 1.3758 + if (result == JSObject::ED_FAILED) 1.3759 + return false; 1.3760 + if (result == JSObject::ED_OK) { 1.3761 + JS_ASSERT(setter == JS_StrictPropertyStub); 1.3762 + return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value); 1.3763 + } 1.3764 + } 1.3765 + 1.3766 + if (!CallAddPropertyHook<mode>(cx, obj->getClass(), obj, shape, value)) 1.3767 + return false; 1.3768 + 1.3769 + if (callSetterAfterwards && setter != JS_StrictPropertyStub) { 1.3770 + if (!cx->shouldBeJSContext()) 1.3771 + return false; 1.3772 + RootedValue nvalue(cx, value); 1.3773 + return NativeSet<mode>(ExecutionModeTraits<mode>::toContextType(cx), 1.3774 + obj, obj, shape, setterIsStrict, &nvalue); 1.3775 + } 1.3776 + return true; 1.3777 +} 1.3778 + 1.3779 +static bool 1.3780 +NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, 1.3781 + MutableHandle<Shape*> shapep); 1.3782 + 1.3783 +bool 1.3784 +js::DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, 1.3785 + PropertyOp getter, StrictPropertyOp setter, unsigned attrs) 1.3786 +{ 1.3787 + JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS)); 1.3788 + 1.3789 + AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); 1.3790 + 1.3791 + /* 1.3792 + * If defining a getter or setter, we must check for its counterpart and 1.3793 + * update the attributes and property ops. A getter or setter is really 1.3794 + * only half of a property. 1.3795 + */ 1.3796 + RootedShape shape(cx); 1.3797 + if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { 1.3798 + /* 1.3799 + * If we are defining a getter whose setter was already defined, or 1.3800 + * vice versa, finish the job via obj->changeProperty. 1.3801 + */ 1.3802 + if (!NativeLookupOwnProperty(cx, obj, id, &shape)) 1.3803 + return false; 1.3804 + if (shape) { 1.3805 + if (IsImplicitDenseOrTypedArrayElement(shape)) { 1.3806 + if (obj->is<TypedArrayObject>()) { 1.3807 + /* Ignore getter/setter properties added to typed arrays. */ 1.3808 + return true; 1.3809 + } 1.3810 + if (!JSObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id))) 1.3811 + return false; 1.3812 + shape = obj->nativeLookup(cx, id); 1.3813 + } 1.3814 + if (shape->isAccessorDescriptor()) { 1.3815 + shape = JSObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs, 1.3816 + JSPROP_GETTER | JSPROP_SETTER, 1.3817 + (attrs & JSPROP_GETTER) 1.3818 + ? getter 1.3819 + : shape->getter(), 1.3820 + (attrs & JSPROP_SETTER) 1.3821 + ? setter 1.3822 + : shape->setter()); 1.3823 + if (!shape) 1.3824 + return false; 1.3825 + } else { 1.3826 + shape = nullptr; 1.3827 + } 1.3828 + } 1.3829 + } 1.3830 + 1.3831 + /* 1.3832 + * Purge the property cache of any properties named by id that are about 1.3833 + * to be shadowed in obj's scope chain. 1.3834 + */ 1.3835 + if (!PurgeScopeChain(cx, obj, id)) 1.3836 + return false; 1.3837 + 1.3838 + /* Use the object's class getter and setter by default. */ 1.3839 + const Class *clasp = obj->getClass(); 1.3840 + if (!getter && !(attrs & JSPROP_GETTER)) 1.3841 + getter = clasp->getProperty; 1.3842 + if (!setter && !(attrs & JSPROP_SETTER)) 1.3843 + setter = clasp->setProperty; 1.3844 + 1.3845 + if (!shape) { 1.3846 + return DefinePropertyOrElement<SequentialExecution>(cx, obj, id, getter, setter, 1.3847 + attrs, value, false, false); 1.3848 + } 1.3849 + 1.3850 + JS_ALWAYS_TRUE(UpdateShapeTypeAndValue<SequentialExecution>(cx, obj, shape, value)); 1.3851 + 1.3852 + return CallAddPropertyHook<SequentialExecution>(cx, clasp, obj, shape, value); 1.3853 +} 1.3854 + 1.3855 +/* 1.3856 + * Call obj's resolve hook. 1.3857 + * 1.3858 + * cx, id, and flags are the parameters initially passed to the ongoing lookup; 1.3859 + * objp and propp are its out parameters. obj is an object along the prototype 1.3860 + * chain from where the lookup started. 1.3861 + * 1.3862 + * There are four possible outcomes: 1.3863 + * 1.3864 + * - On failure, report an error or exception and return false. 1.3865 + * 1.3866 + * - If we are already resolving a property of *curobjp, set *recursedp = true, 1.3867 + * and return true. 1.3868 + * 1.3869 + * - If the resolve hook finds or defines the sought property, set *objp and 1.3870 + * *propp appropriately, set *recursedp = false, and return true. 1.3871 + * 1.3872 + * - Otherwise no property was resolved. Set *propp = nullptr and 1.3873 + * *recursedp = false and return true. 1.3874 + */ 1.3875 +static MOZ_ALWAYS_INLINE bool 1.3876 +CallResolveOp(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp, 1.3877 + MutableHandleShape propp, bool *recursedp) 1.3878 +{ 1.3879 + const Class *clasp = obj->getClass(); 1.3880 + JSResolveOp resolve = clasp->resolve; 1.3881 + 1.3882 + /* 1.3883 + * Avoid recursion on (obj, id) already being resolved on cx. 1.3884 + * 1.3885 + * Once we have successfully added an entry for (obj, key) to 1.3886 + * cx->resolvingTable, control must go through cleanup: before 1.3887 + * returning. But note that JS_DHASH_ADD may find an existing 1.3888 + * entry, in which case we bail to suppress runaway recursion. 1.3889 + */ 1.3890 + AutoResolving resolving(cx, obj, id); 1.3891 + if (resolving.alreadyStarted()) { 1.3892 + /* Already resolving id in obj -- suppress recursion. */ 1.3893 + *recursedp = true; 1.3894 + return true; 1.3895 + } 1.3896 + *recursedp = false; 1.3897 + 1.3898 + propp.set(nullptr); 1.3899 + 1.3900 + if (clasp->flags & JSCLASS_NEW_RESOLVE) { 1.3901 + JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve); 1.3902 + RootedObject obj2(cx, nullptr); 1.3903 + if (!newresolve(cx, obj, id, &obj2)) 1.3904 + return false; 1.3905 + 1.3906 + /* 1.3907 + * We trust the new style resolve hook to set obj2 to nullptr when 1.3908 + * the id cannot be resolved. But, when obj2 is not null, we do 1.3909 + * not assume that id must exist and do full nativeLookup for 1.3910 + * compatibility. 1.3911 + */ 1.3912 + if (!obj2) 1.3913 + return true; 1.3914 + 1.3915 + if (!obj2->isNative()) { 1.3916 + /* Whoops, newresolve handed back a foreign obj2. */ 1.3917 + JS_ASSERT(obj2 != obj); 1.3918 + return JSObject::lookupGeneric(cx, obj2, id, objp, propp); 1.3919 + } 1.3920 + 1.3921 + objp.set(obj2); 1.3922 + } else { 1.3923 + if (!resolve(cx, obj, id)) 1.3924 + return false; 1.3925 + 1.3926 + objp.set(obj); 1.3927 + } 1.3928 + 1.3929 + if (JSID_IS_INT(id) && objp->containsDenseElement(JSID_TO_INT(id))) { 1.3930 + MarkDenseOrTypedArrayElementFound<CanGC>(propp); 1.3931 + return true; 1.3932 + } 1.3933 + 1.3934 + Shape *shape; 1.3935 + if (!objp->nativeEmpty() && (shape = objp->nativeLookup(cx, id))) 1.3936 + propp.set(shape); 1.3937 + else 1.3938 + objp.set(nullptr); 1.3939 + 1.3940 + return true; 1.3941 +} 1.3942 + 1.3943 +template <AllowGC allowGC> 1.3944 +static MOZ_ALWAYS_INLINE bool 1.3945 +LookupOwnPropertyInline(ExclusiveContext *cx, 1.3946 + typename MaybeRooted<JSObject*, allowGC>::HandleType obj, 1.3947 + typename MaybeRooted<jsid, allowGC>::HandleType id, 1.3948 + typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp, 1.3949 + typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp, 1.3950 + bool *donep) 1.3951 +{ 1.3952 + // Check for a native dense element. 1.3953 + if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) { 1.3954 + objp.set(obj); 1.3955 + MarkDenseOrTypedArrayElementFound<allowGC>(propp); 1.3956 + *donep = true; 1.3957 + return true; 1.3958 + } 1.3959 + 1.3960 + // Check for a typed array element. Integer lookups always finish here 1.3961 + // so that integer properties on the prototype are ignored even for out 1.3962 + // of bounds accesses. 1.3963 + if (obj->template is<TypedArrayObject>()) { 1.3964 + uint64_t index; 1.3965 + if (IsTypedArrayIndex(id, &index)) { 1.3966 + if (index < obj->template as<TypedArrayObject>().length()) { 1.3967 + objp.set(obj); 1.3968 + MarkDenseOrTypedArrayElementFound<allowGC>(propp); 1.3969 + } else { 1.3970 + objp.set(nullptr); 1.3971 + propp.set(nullptr); 1.3972 + } 1.3973 + *donep = true; 1.3974 + return true; 1.3975 + } 1.3976 + } 1.3977 + 1.3978 + // Check for a native property. 1.3979 + if (Shape *shape = obj->nativeLookup(cx, id)) { 1.3980 + objp.set(obj); 1.3981 + propp.set(shape); 1.3982 + *donep = true; 1.3983 + return true; 1.3984 + } 1.3985 + 1.3986 + // id was not found in obj. Try obj's resolve hook, if any. 1.3987 + if (obj->getClass()->resolve != JS_ResolveStub) { 1.3988 + if (!cx->shouldBeJSContext() || !allowGC) 1.3989 + return false; 1.3990 + 1.3991 + bool recursed; 1.3992 + if (!CallResolveOp(cx->asJSContext(), 1.3993 + MaybeRooted<JSObject*, allowGC>::toHandle(obj), 1.3994 + MaybeRooted<jsid, allowGC>::toHandle(id), 1.3995 + MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp), 1.3996 + MaybeRooted<Shape*, allowGC>::toMutableHandle(propp), 1.3997 + &recursed)) 1.3998 + { 1.3999 + return false; 1.4000 + } 1.4001 + 1.4002 + if (recursed) { 1.4003 + objp.set(nullptr); 1.4004 + propp.set(nullptr); 1.4005 + *donep = true; 1.4006 + return true; 1.4007 + } 1.4008 + 1.4009 + if (propp) { 1.4010 + *donep = true; 1.4011 + return true; 1.4012 + } 1.4013 + } 1.4014 + 1.4015 + *donep = false; 1.4016 + return true; 1.4017 +} 1.4018 + 1.4019 +static bool 1.4020 +NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, 1.4021 + MutableHandle<Shape*> shapep) 1.4022 +{ 1.4023 + RootedObject pobj(cx); 1.4024 + bool done; 1.4025 + 1.4026 + if (!LookupOwnPropertyInline<CanGC>(cx, obj, id, &pobj, shapep, &done)) 1.4027 + return false; 1.4028 + if (!done || pobj != obj) 1.4029 + shapep.set(nullptr); 1.4030 + return true; 1.4031 +} 1.4032 + 1.4033 +template <AllowGC allowGC> 1.4034 +static MOZ_ALWAYS_INLINE bool 1.4035 +LookupPropertyInline(ExclusiveContext *cx, 1.4036 + typename MaybeRooted<JSObject*, allowGC>::HandleType obj, 1.4037 + typename MaybeRooted<jsid, allowGC>::HandleType id, 1.4038 + typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp, 1.4039 + typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp) 1.4040 +{ 1.4041 + /* NB: The logic of this procedure is implicitly reflected in IonBuilder.cpp's 1.4042 + * |CanEffectlesslyCallLookupGenericOnObject| logic. 1.4043 + * If this changes, please remember to update the logic there as well. 1.4044 + */ 1.4045 + 1.4046 + /* Search scopes starting with obj and following the prototype link. */ 1.4047 + typename MaybeRooted<JSObject*, allowGC>::RootType current(cx, obj); 1.4048 + 1.4049 + while (true) { 1.4050 + bool done; 1.4051 + if (!LookupOwnPropertyInline<allowGC>(cx, current, id, objp, propp, &done)) 1.4052 + return false; 1.4053 + if (done) 1.4054 + return true; 1.4055 + 1.4056 + typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto()); 1.4057 + 1.4058 + if (!proto) 1.4059 + break; 1.4060 + if (!proto->isNative()) { 1.4061 + if (!cx->shouldBeJSContext() || !allowGC) 1.4062 + return false; 1.4063 + return JSObject::lookupGeneric(cx->asJSContext(), 1.4064 + MaybeRooted<JSObject*, allowGC>::toHandle(proto), 1.4065 + MaybeRooted<jsid, allowGC>::toHandle(id), 1.4066 + MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp), 1.4067 + MaybeRooted<Shape*, allowGC>::toMutableHandle(propp)); 1.4068 + } 1.4069 + 1.4070 + current = proto; 1.4071 + } 1.4072 + 1.4073 + objp.set(nullptr); 1.4074 + propp.set(nullptr); 1.4075 + return true; 1.4076 +} 1.4077 + 1.4078 +template <AllowGC allowGC> 1.4079 +bool 1.4080 +baseops::LookupProperty(ExclusiveContext *cx, 1.4081 + typename MaybeRooted<JSObject*, allowGC>::HandleType obj, 1.4082 + typename MaybeRooted<jsid, allowGC>::HandleType id, 1.4083 + typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp, 1.4084 + typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp) 1.4085 +{ 1.4086 + return LookupPropertyInline<allowGC>(cx, obj, id, objp, propp); 1.4087 +} 1.4088 + 1.4089 +template bool 1.4090 +baseops::LookupProperty<CanGC>(ExclusiveContext *cx, HandleObject obj, HandleId id, 1.4091 + MutableHandleObject objp, MutableHandleShape propp); 1.4092 + 1.4093 +template bool 1.4094 +baseops::LookupProperty<NoGC>(ExclusiveContext *cx, JSObject *obj, jsid id, 1.4095 + FakeMutableHandle<JSObject*> objp, 1.4096 + FakeMutableHandle<Shape*> propp); 1.4097 + 1.4098 +/* static */ bool 1.4099 +JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id, 1.4100 + MutableHandleObject objp, MutableHandleShape propp) 1.4101 +{ 1.4102 + /* 1.4103 + * NB: The logic of lookupGeneric is implicitly reflected in IonBuilder.cpp's 1.4104 + * |CanEffectlesslyCallLookupGenericOnObject| logic. 1.4105 + * If this changes, please remember to update the logic there as well. 1.4106 + */ 1.4107 + LookupGenericOp op = obj->getOps()->lookupGeneric; 1.4108 + if (op) 1.4109 + return op(cx, obj, id, objp, propp); 1.4110 + return baseops::LookupProperty<js::CanGC>(cx, obj, id, objp, propp); 1.4111 +} 1.4112 + 1.4113 +bool 1.4114 +baseops::LookupElement(JSContext *cx, HandleObject obj, uint32_t index, 1.4115 + MutableHandleObject objp, MutableHandleShape propp) 1.4116 +{ 1.4117 + RootedId id(cx); 1.4118 + if (!IndexToId(cx, index, &id)) 1.4119 + return false; 1.4120 + 1.4121 + return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp); 1.4122 +} 1.4123 + 1.4124 +bool 1.4125 +js::LookupNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, 1.4126 + MutableHandleObject objp, MutableHandleShape propp) 1.4127 +{ 1.4128 + return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp); 1.4129 +} 1.4130 + 1.4131 +bool 1.4132 +js::LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, 1.4133 + MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp) 1.4134 +{ 1.4135 + RootedId id(cx, NameToId(name)); 1.4136 + 1.4137 + for (RootedObject scope(cx, scopeChain); scope; scope = scope->enclosingScope()) { 1.4138 + if (!JSObject::lookupGeneric(cx, scope, id, pobjp, propp)) 1.4139 + return false; 1.4140 + if (propp) { 1.4141 + objp.set(scope); 1.4142 + return true; 1.4143 + } 1.4144 + } 1.4145 + 1.4146 + objp.set(nullptr); 1.4147 + pobjp.set(nullptr); 1.4148 + propp.set(nullptr); 1.4149 + return true; 1.4150 +} 1.4151 + 1.4152 +bool 1.4153 +js::LookupNameNoGC(JSContext *cx, PropertyName *name, JSObject *scopeChain, 1.4154 + JSObject **objp, JSObject **pobjp, Shape **propp) 1.4155 +{ 1.4156 + AutoAssertNoException nogc(cx); 1.4157 + 1.4158 + JS_ASSERT(!*objp && !*pobjp && !*propp); 1.4159 + 1.4160 + for (JSObject *scope = scopeChain; scope; scope = scope->enclosingScope()) { 1.4161 + if (scope->getOps()->lookupGeneric) 1.4162 + return false; 1.4163 + if (!LookupPropertyInline<NoGC>(cx, scope, NameToId(name), pobjp, propp)) 1.4164 + return false; 1.4165 + if (*propp) { 1.4166 + *objp = scope; 1.4167 + return true; 1.4168 + } 1.4169 + } 1.4170 + 1.4171 + return true; 1.4172 +} 1.4173 + 1.4174 +bool 1.4175 +js::LookupNameWithGlobalDefault(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, 1.4176 + MutableHandleObject objp) 1.4177 +{ 1.4178 + RootedId id(cx, NameToId(name)); 1.4179 + 1.4180 + RootedObject pobj(cx); 1.4181 + RootedShape prop(cx); 1.4182 + 1.4183 + RootedObject scope(cx, scopeChain); 1.4184 + for (; !scope->is<GlobalObject>(); scope = scope->enclosingScope()) { 1.4185 + if (!JSObject::lookupGeneric(cx, scope, id, &pobj, &prop)) 1.4186 + return false; 1.4187 + if (prop) 1.4188 + break; 1.4189 + } 1.4190 + 1.4191 + objp.set(scope); 1.4192 + return true; 1.4193 +} 1.4194 + 1.4195 +template <AllowGC allowGC> 1.4196 +bool 1.4197 +js::HasOwnProperty(JSContext *cx, LookupGenericOp lookup, 1.4198 + typename MaybeRooted<JSObject*, allowGC>::HandleType obj, 1.4199 + typename MaybeRooted<jsid, allowGC>::HandleType id, 1.4200 + typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp, 1.4201 + typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp) 1.4202 +{ 1.4203 + if (lookup) { 1.4204 + if (!allowGC) 1.4205 + return false; 1.4206 + if (!lookup(cx, 1.4207 + MaybeRooted<JSObject*, allowGC>::toHandle(obj), 1.4208 + MaybeRooted<jsid, allowGC>::toHandle(id), 1.4209 + MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp), 1.4210 + MaybeRooted<Shape*, allowGC>::toMutableHandle(propp))) 1.4211 + { 1.4212 + return false; 1.4213 + } 1.4214 + } else { 1.4215 + bool done; 1.4216 + if (!LookupOwnPropertyInline<allowGC>(cx, obj, id, objp, propp, &done)) 1.4217 + return false; 1.4218 + if (!done) { 1.4219 + objp.set(nullptr); 1.4220 + propp.set(nullptr); 1.4221 + return true; 1.4222 + } 1.4223 + } 1.4224 + 1.4225 + if (!propp) 1.4226 + return true; 1.4227 + 1.4228 + if (objp == obj) 1.4229 + return true; 1.4230 + 1.4231 + JSObject *outer = nullptr; 1.4232 + if (JSObjectOp op = objp->getClass()->ext.outerObject) { 1.4233 + if (!allowGC) 1.4234 + return false; 1.4235 + RootedObject inner(cx, objp); 1.4236 + outer = op(cx, inner); 1.4237 + if (!outer) 1.4238 + return false; 1.4239 + } 1.4240 + 1.4241 + if (outer != objp) 1.4242 + propp.set(nullptr); 1.4243 + return true; 1.4244 +} 1.4245 + 1.4246 +template bool 1.4247 +js::HasOwnProperty<CanGC>(JSContext *cx, LookupGenericOp lookup, 1.4248 + HandleObject obj, HandleId id, 1.4249 + MutableHandleObject objp, MutableHandleShape propp); 1.4250 + 1.4251 +template bool 1.4252 +js::HasOwnProperty<NoGC>(JSContext *cx, LookupGenericOp lookup, 1.4253 + JSObject *obj, jsid id, 1.4254 + FakeMutableHandle<JSObject*> objp, FakeMutableHandle<Shape*> propp); 1.4255 + 1.4256 +bool 1.4257 +js::HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *resultp) 1.4258 +{ 1.4259 + RootedObject pobj(cx); 1.4260 + RootedShape shape(cx); 1.4261 + if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape)) 1.4262 + return false; 1.4263 + *resultp = (shape != nullptr); 1.4264 + return true; 1.4265 +} 1.4266 + 1.4267 +template <AllowGC allowGC> 1.4268 +static MOZ_ALWAYS_INLINE bool 1.4269 +NativeGetInline(JSContext *cx, 1.4270 + typename MaybeRooted<JSObject*, allowGC>::HandleType obj, 1.4271 + typename MaybeRooted<JSObject*, allowGC>::HandleType receiver, 1.4272 + typename MaybeRooted<JSObject*, allowGC>::HandleType pobj, 1.4273 + typename MaybeRooted<Shape*, allowGC>::HandleType shape, 1.4274 + typename MaybeRooted<Value, allowGC>::MutableHandleType vp) 1.4275 +{ 1.4276 + JS_ASSERT(pobj->isNative()); 1.4277 + 1.4278 + if (shape->hasSlot()) { 1.4279 + vp.set(pobj->nativeGetSlot(shape->slot())); 1.4280 + JS_ASSERT(!vp.isMagic()); 1.4281 + JS_ASSERT_IF(!pobj->hasSingletonType() && 1.4282 + !pobj->template is<ScopeObject>() && 1.4283 + shape->hasDefaultGetter(), 1.4284 + js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp)); 1.4285 + } else { 1.4286 + vp.setUndefined(); 1.4287 + } 1.4288 + if (shape->hasDefaultGetter()) 1.4289 + return true; 1.4290 + 1.4291 + { 1.4292 + jsbytecode *pc; 1.4293 + JSScript *script = cx->currentScript(&pc); 1.4294 +#ifdef JS_ION 1.4295 + if (script && script->hasBaselineScript()) { 1.4296 + switch (JSOp(*pc)) { 1.4297 + case JSOP_GETPROP: 1.4298 + case JSOP_CALLPROP: 1.4299 + case JSOP_LENGTH: 1.4300 + script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc)); 1.4301 + break; 1.4302 + default: 1.4303 + break; 1.4304 + } 1.4305 + } 1.4306 +#endif 1.4307 + } 1.4308 + 1.4309 + if (!allowGC) 1.4310 + return false; 1.4311 + 1.4312 + if (!shape->get(cx, 1.4313 + MaybeRooted<JSObject*, allowGC>::toHandle(receiver), 1.4314 + MaybeRooted<JSObject*, allowGC>::toHandle(obj), 1.4315 + MaybeRooted<JSObject*, allowGC>::toHandle(pobj), 1.4316 + MaybeRooted<Value, allowGC>::toMutableHandle(vp))) 1.4317 + { 1.4318 + return false; 1.4319 + } 1.4320 + 1.4321 + /* Update slotful shapes according to the value produced by the getter. */ 1.4322 + if (shape->hasSlot() && pobj->nativeContains(cx, shape)) 1.4323 + pobj->nativeSetSlot(shape->slot(), vp); 1.4324 + 1.4325 + return true; 1.4326 +} 1.4327 + 1.4328 +bool 1.4329 +js::NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Handle<Shape*> shape, 1.4330 + MutableHandle<Value> vp) 1.4331 +{ 1.4332 + return NativeGetInline<CanGC>(cx, obj, obj, pobj, shape, vp); 1.4333 +} 1.4334 + 1.4335 +template <ExecutionMode mode> 1.4336 +bool 1.4337 +js::NativeSet(typename ExecutionModeTraits<mode>::ContextType cxArg, 1.4338 + Handle<JSObject*> obj, Handle<JSObject*> receiver, 1.4339 + HandleShape shape, bool strict, MutableHandleValue vp) 1.4340 +{ 1.4341 + JS_ASSERT(cxArg->isThreadLocal(obj)); 1.4342 + JS_ASSERT(obj->isNative()); 1.4343 + 1.4344 + if (shape->hasSlot()) { 1.4345 + /* If shape has a stub setter, just store vp. */ 1.4346 + if (shape->hasDefaultSetter()) { 1.4347 + if (mode == ParallelExecution) { 1.4348 + if (!obj->nativeSetSlotIfHasType(shape, vp)) 1.4349 + return false; 1.4350 + } else { 1.4351 + obj->nativeSetSlotWithType(cxArg->asExclusiveContext(), shape, vp); 1.4352 + } 1.4353 + 1.4354 + return true; 1.4355 + } 1.4356 + } 1.4357 + 1.4358 + if (mode == ParallelExecution) 1.4359 + return false; 1.4360 + JSContext *cx = cxArg->asJSContext(); 1.4361 + 1.4362 + if (!shape->hasSlot()) { 1.4363 + /* 1.4364 + * Allow API consumers to create shared properties with stub setters. 1.4365 + * Such properties effectively function as data descriptors which are 1.4366 + * not writable, so attempting to set such a property should do nothing 1.4367 + * or throw if we're in strict mode. 1.4368 + */ 1.4369 + if (!shape->hasGetterValue() && shape->hasDefaultSetter()) 1.4370 + return js_ReportGetterOnlyAssignment(cx, strict); 1.4371 + } 1.4372 + 1.4373 + RootedValue ovp(cx, vp); 1.4374 + 1.4375 + uint32_t sample = cx->runtime()->propertyRemovals; 1.4376 + if (!shape->set(cx, obj, receiver, strict, vp)) 1.4377 + return false; 1.4378 + 1.4379 + /* 1.4380 + * Update any slot for the shape with the value produced by the setter, 1.4381 + * unless the setter deleted the shape. 1.4382 + */ 1.4383 + if (shape->hasSlot() && 1.4384 + (MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) || 1.4385 + obj->nativeContains(cx, shape))) 1.4386 + { 1.4387 + obj->setSlot(shape->slot(), vp); 1.4388 + } 1.4389 + 1.4390 + return true; 1.4391 +} 1.4392 + 1.4393 +template bool 1.4394 +js::NativeSet<SequentialExecution>(JSContext *cx, 1.4395 + Handle<JSObject*> obj, Handle<JSObject*> receiver, 1.4396 + HandleShape shape, bool strict, MutableHandleValue vp); 1.4397 +template bool 1.4398 +js::NativeSet<ParallelExecution>(ForkJoinContext *cx, 1.4399 + Handle<JSObject*> obj, Handle<JSObject*> receiver, 1.4400 + HandleShape shape, bool strict, MutableHandleValue vp); 1.4401 + 1.4402 +template <AllowGC allowGC> 1.4403 +static MOZ_ALWAYS_INLINE bool 1.4404 +GetPropertyHelperInline(JSContext *cx, 1.4405 + typename MaybeRooted<JSObject*, allowGC>::HandleType obj, 1.4406 + typename MaybeRooted<JSObject*, allowGC>::HandleType receiver, 1.4407 + typename MaybeRooted<jsid, allowGC>::HandleType id, 1.4408 + typename MaybeRooted<Value, allowGC>::MutableHandleType vp) 1.4409 +{ 1.4410 + /* This call site is hot -- use the always-inlined variant of LookupNativeProperty(). */ 1.4411 + typename MaybeRooted<JSObject*, allowGC>::RootType obj2(cx); 1.4412 + typename MaybeRooted<Shape*, allowGC>::RootType shape(cx); 1.4413 + if (!LookupPropertyInline<allowGC>(cx, obj, id, &obj2, &shape)) 1.4414 + return false; 1.4415 + 1.4416 + if (!shape) { 1.4417 + if (!allowGC) 1.4418 + return false; 1.4419 + 1.4420 + vp.setUndefined(); 1.4421 + 1.4422 + if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, 1.4423 + MaybeRooted<JSObject*, allowGC>::toHandle(obj), 1.4424 + MaybeRooted<jsid, allowGC>::toHandle(id), 1.4425 + MaybeRooted<Value, allowGC>::toMutableHandle(vp))) 1.4426 + { 1.4427 + return false; 1.4428 + } 1.4429 + 1.4430 + /* 1.4431 + * Give a strict warning if foo.bar is evaluated by a script for an 1.4432 + * object foo with no property named 'bar'. 1.4433 + */ 1.4434 + if (vp.isUndefined()) { 1.4435 + jsbytecode *pc = nullptr; 1.4436 + RootedScript script(cx, cx->currentScript(&pc)); 1.4437 + if (!pc) 1.4438 + return true; 1.4439 + JSOp op = (JSOp) *pc; 1.4440 + 1.4441 + if (op == JSOP_GETXPROP) { 1.4442 + /* Undefined property during a name lookup, report an error. */ 1.4443 + JSAutoByteString printable; 1.4444 + if (js_ValueToPrintable(cx, IdToValue(id), &printable)) 1.4445 + js_ReportIsNotDefined(cx, printable.ptr()); 1.4446 + return false; 1.4447 + } 1.4448 + 1.4449 + /* Don't warn if extra warnings not enabled or for random getprop operations. */ 1.4450 + if (!cx->options().extraWarnings() || (op != JSOP_GETPROP && op != JSOP_GETELEM)) 1.4451 + return true; 1.4452 + 1.4453 + /* Don't warn repeatedly for the same script. */ 1.4454 + if (!script || script->warnedAboutUndefinedProp()) 1.4455 + return true; 1.4456 + 1.4457 + /* 1.4458 + * Don't warn in self-hosted code (where the further presence of 1.4459 + * JS::ContextOptions::werror() would result in impossible-to-avoid 1.4460 + * errors to entirely-innocent client code). 1.4461 + */ 1.4462 + if (script->selfHosted()) 1.4463 + return true; 1.4464 + 1.4465 + /* We may just be checking if that object has an iterator. */ 1.4466 + if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic)) 1.4467 + return true; 1.4468 + 1.4469 + /* Do not warn about tests like (obj[prop] == undefined). */ 1.4470 + pc += js_CodeSpec[op].length; 1.4471 + if (Detecting(cx, script, pc)) 1.4472 + return true; 1.4473 + 1.4474 + unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT; 1.4475 + script->setWarnedAboutUndefinedProp(); 1.4476 + 1.4477 + /* Ok, bad undefined property reference: whine about it. */ 1.4478 + RootedValue val(cx, IdToValue(id)); 1.4479 + if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, 1.4480 + JSDVG_IGNORE_STACK, val, js::NullPtr(), 1.4481 + nullptr, nullptr)) 1.4482 + { 1.4483 + return false; 1.4484 + } 1.4485 + } 1.4486 + return true; 1.4487 + } 1.4488 + 1.4489 + if (!obj2->isNative()) { 1.4490 + if (!allowGC) 1.4491 + return false; 1.4492 + HandleObject obj2Handle = MaybeRooted<JSObject*, allowGC>::toHandle(obj2); 1.4493 + HandleObject receiverHandle = MaybeRooted<JSObject*, allowGC>::toHandle(receiver); 1.4494 + HandleId idHandle = MaybeRooted<jsid, allowGC>::toHandle(id); 1.4495 + MutableHandleValue vpHandle = MaybeRooted<Value, allowGC>::toMutableHandle(vp); 1.4496 + return obj2->template is<ProxyObject>() 1.4497 + ? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle) 1.4498 + : JSObject::getGeneric(cx, obj2Handle, obj2Handle, idHandle, vpHandle); 1.4499 + } 1.4500 + 1.4501 + if (IsImplicitDenseOrTypedArrayElement(shape)) { 1.4502 + vp.set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id))); 1.4503 + return true; 1.4504 + } 1.4505 + 1.4506 + /* This call site is hot -- use the always-inlined variant of NativeGet(). */ 1.4507 + if (!NativeGetInline<allowGC>(cx, obj, receiver, obj2, shape, vp)) 1.4508 + return false; 1.4509 + 1.4510 + return true; 1.4511 +} 1.4512 + 1.4513 +bool 1.4514 +baseops::GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp) 1.4515 +{ 1.4516 + /* This call site is hot -- use the always-inlined variant of GetPropertyHelper(). */ 1.4517 + return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp); 1.4518 +} 1.4519 + 1.4520 +bool 1.4521 +baseops::GetPropertyNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp) 1.4522 +{ 1.4523 + AutoAssertNoException nogc(cx); 1.4524 + return GetPropertyHelperInline<NoGC>(cx, obj, receiver, id, vp); 1.4525 +} 1.4526 + 1.4527 +static MOZ_ALWAYS_INLINE bool 1.4528 +LookupPropertyPureInline(JSObject *obj, jsid id, JSObject **objp, Shape **propp) 1.4529 +{ 1.4530 + if (!obj->isNative()) 1.4531 + return false; 1.4532 + 1.4533 + JSObject *current = obj; 1.4534 + while (true) { 1.4535 + /* Search for a native dense element, typed array element, or property. */ 1.4536 + 1.4537 + if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) { 1.4538 + *objp = current; 1.4539 + MarkDenseOrTypedArrayElementFound<NoGC>(propp); 1.4540 + return true; 1.4541 + } 1.4542 + 1.4543 + if (current->is<TypedArrayObject>()) { 1.4544 + uint64_t index; 1.4545 + if (IsTypedArrayIndex(id, &index)) { 1.4546 + if (index < obj->as<TypedArrayObject>().length()) { 1.4547 + *objp = current; 1.4548 + MarkDenseOrTypedArrayElementFound<NoGC>(propp); 1.4549 + } else { 1.4550 + *objp = nullptr; 1.4551 + *propp = nullptr; 1.4552 + } 1.4553 + return true; 1.4554 + } 1.4555 + } 1.4556 + 1.4557 + if (Shape *shape = current->nativeLookupPure(id)) { 1.4558 + *objp = current; 1.4559 + *propp = shape; 1.4560 + return true; 1.4561 + } 1.4562 + 1.4563 + /* Fail if there's a resolve hook. */ 1.4564 + if (current->getClass()->resolve != JS_ResolveStub) 1.4565 + return false; 1.4566 + 1.4567 + JSObject *proto = current->getProto(); 1.4568 + 1.4569 + if (!proto) 1.4570 + break; 1.4571 + if (!proto->isNative()) 1.4572 + return false; 1.4573 + 1.4574 + current = proto; 1.4575 + } 1.4576 + 1.4577 + *objp = nullptr; 1.4578 + *propp = nullptr; 1.4579 + return true; 1.4580 +} 1.4581 + 1.4582 +static MOZ_ALWAYS_INLINE bool 1.4583 +NativeGetPureInline(JSObject *pobj, Shape *shape, Value *vp) 1.4584 +{ 1.4585 + JS_ASSERT(pobj->isNative()); 1.4586 + 1.4587 + if (shape->hasSlot()) { 1.4588 + *vp = pobj->nativeGetSlot(shape->slot()); 1.4589 + JS_ASSERT(!vp->isMagic()); 1.4590 + } else { 1.4591 + vp->setUndefined(); 1.4592 + } 1.4593 + 1.4594 + /* Fail if we have a custom getter. */ 1.4595 + return shape->hasDefaultGetter(); 1.4596 +} 1.4597 + 1.4598 +bool 1.4599 +js::LookupPropertyPure(JSObject *obj, jsid id, JSObject **objp, Shape **propp) 1.4600 +{ 1.4601 + return LookupPropertyPureInline(obj, id, objp, propp); 1.4602 +} 1.4603 + 1.4604 +static inline bool 1.4605 +IdIsLength(ThreadSafeContext *cx, jsid id) 1.4606 +{ 1.4607 + return JSID_IS_ATOM(id) && cx->names().length == JSID_TO_ATOM(id); 1.4608 +} 1.4609 + 1.4610 +/* 1.4611 + * A pure version of GetPropertyHelper that can be called from parallel code 1.4612 + * without locking. This code path cannot GC. This variant returns false 1.4613 + * whenever a side-effect might have occured in the effectful version. This 1.4614 + * includes, but is not limited to: 1.4615 + * 1.4616 + * - Any object in the lookup chain has a non-stub resolve hook. 1.4617 + * - Any object in the lookup chain is non-native. 1.4618 + * - The property has a getter. 1.4619 + */ 1.4620 +bool 1.4621 +js::GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp) 1.4622 +{ 1.4623 + /* Deal with native objects. */ 1.4624 + JSObject *obj2; 1.4625 + Shape *shape; 1.4626 + if (!LookupPropertyPureInline(obj, id, &obj2, &shape)) 1.4627 + return false; 1.4628 + 1.4629 + if (!shape) { 1.4630 + /* Fail if we have a non-stub class op hooks. */ 1.4631 + if (obj->getClass()->getProperty && obj->getClass()->getProperty != JS_PropertyStub) 1.4632 + return false; 1.4633 + 1.4634 + if (obj->getOps()->getElement) 1.4635 + return false; 1.4636 + 1.4637 + /* Vanilla native object, return undefined. */ 1.4638 + vp->setUndefined(); 1.4639 + return true; 1.4640 + } 1.4641 + 1.4642 + if (IsImplicitDenseOrTypedArrayElement(shape)) { 1.4643 + *vp = obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)); 1.4644 + return true; 1.4645 + } 1.4646 + 1.4647 + /* Special case 'length' on Array and TypedArray. */ 1.4648 + if (IdIsLength(cx, id)) { 1.4649 + if (obj->is<ArrayObject>()) { 1.4650 + vp->setNumber(obj->as<ArrayObject>().length()); 1.4651 + return true; 1.4652 + } 1.4653 + 1.4654 + if (obj->is<TypedArrayObject>()) { 1.4655 + vp->setNumber(obj->as<TypedArrayObject>().length()); 1.4656 + return true; 1.4657 + } 1.4658 + } 1.4659 + 1.4660 + return NativeGetPureInline(obj2, shape, vp); 1.4661 +} 1.4662 + 1.4663 +static bool 1.4664 +MOZ_ALWAYS_INLINE 1.4665 +GetElementPure(ThreadSafeContext *cx, JSObject *obj, uint32_t index, Value *vp) 1.4666 +{ 1.4667 + if (index <= JSID_INT_MAX) 1.4668 + return GetPropertyPure(cx, obj, INT_TO_JSID(index), vp); 1.4669 + return false; 1.4670 +} 1.4671 + 1.4672 +/* 1.4673 + * A pure version of GetObjectElementOperation that can be called from 1.4674 + * parallel code without locking. This variant returns false whenever a 1.4675 + * side-effect might have occurred. 1.4676 + */ 1.4677 +bool 1.4678 +js::GetObjectElementOperationPure(ThreadSafeContext *cx, JSObject *obj, const Value &prop, 1.4679 + Value *vp) 1.4680 +{ 1.4681 + uint32_t index; 1.4682 + if (IsDefinitelyIndex(prop, &index)) 1.4683 + return GetElementPure(cx, obj, index, vp); 1.4684 + 1.4685 + /* Atomizing the property value is effectful and not threadsafe. */ 1.4686 + if (!prop.isString() || !prop.toString()->isAtom()) 1.4687 + return false; 1.4688 + 1.4689 + JSAtom *name = &prop.toString()->asAtom(); 1.4690 + if (name->isIndex(&index)) 1.4691 + return GetElementPure(cx, obj, index, vp); 1.4692 + 1.4693 + return GetPropertyPure(cx, obj, NameToId(name->asPropertyName()), vp); 1.4694 +} 1.4695 + 1.4696 +bool 1.4697 +baseops::GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, 1.4698 + MutableHandleValue vp) 1.4699 +{ 1.4700 + RootedId id(cx); 1.4701 + if (!IndexToId(cx, index, &id)) 1.4702 + return false; 1.4703 + 1.4704 + /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */ 1.4705 + return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp); 1.4706 +} 1.4707 + 1.4708 +static bool 1.4709 +MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname) 1.4710 +{ 1.4711 + { 1.4712 + JSScript *script = cx->currentScript(nullptr, JSContext::ALLOW_CROSS_COMPARTMENT); 1.4713 + if (!script) 1.4714 + return true; 1.4715 + 1.4716 + // If the code is not strict and extra warnings aren't enabled, then no 1.4717 + // check is needed. 1.4718 + if (!script->strict() && !cx->options().extraWarnings()) 1.4719 + return true; 1.4720 + } 1.4721 + 1.4722 + JSAutoByteString bytes(cx, propname); 1.4723 + return !!bytes && 1.4724 + JS_ReportErrorFlagsAndNumber(cx, 1.4725 + (JSREPORT_WARNING | JSREPORT_STRICT 1.4726 + | JSREPORT_STRICT_MODE_ERROR), 1.4727 + js_GetErrorMessage, nullptr, 1.4728 + JSMSG_UNDECLARED_VAR, bytes.ptr()); 1.4729 +} 1.4730 + 1.4731 +bool 1.4732 +js::ReportIfUndeclaredVarAssignment(JSContext *cx, HandleString propname) 1.4733 +{ 1.4734 + { 1.4735 + jsbytecode *pc; 1.4736 + JSScript *script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT); 1.4737 + if (!script) 1.4738 + return true; 1.4739 + 1.4740 + // If the code is not strict and extra warnings aren't enabled, then no 1.4741 + // check is needed. 1.4742 + if (!script->strict() && !cx->options().extraWarnings()) 1.4743 + return true; 1.4744 + 1.4745 + /* 1.4746 + * We only need to check for bare name mutations: we shouldn't be 1.4747 + * warning, or throwing, or whatever, if we're not doing a variable 1.4748 + * access. 1.4749 + * 1.4750 + * TryConvertToGname in frontend/BytecodeEmitter.cpp checks for rather 1.4751 + * more opcodes when it does, in the normal course of events, what this 1.4752 + * method does in the abnormal course of events. Because we're called 1.4753 + * in narrower circumstances, we only need check two. We don't need to 1.4754 + * check for the increment/decrement opcodes because they're no-ops: 1.4755 + * the actual semantics are implemented by desugaring. And we don't 1.4756 + * need to check name-access because this method is only supposed to be 1.4757 + * called in assignment contexts. 1.4758 + */ 1.4759 + MOZ_ASSERT(*pc != JSOP_NAME); 1.4760 + MOZ_ASSERT(*pc != JSOP_GETGNAME); 1.4761 + if (*pc != JSOP_SETNAME && *pc != JSOP_SETGNAME) 1.4762 + return true; 1.4763 + } 1.4764 + 1.4765 + JSAutoByteString bytes(cx, propname); 1.4766 + return !!bytes && 1.4767 + JS_ReportErrorFlagsAndNumber(cx, 1.4768 + JSREPORT_WARNING | JSREPORT_STRICT | 1.4769 + JSREPORT_STRICT_MODE_ERROR, 1.4770 + js_GetErrorMessage, nullptr, 1.4771 + JSMSG_UNDECLARED_VAR, bytes.ptr()); 1.4772 +} 1.4773 + 1.4774 +bool 1.4775 +JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report) 1.4776 +{ 1.4777 + if (cxArg->isForkJoinContext()) 1.4778 + return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report); 1.4779 + 1.4780 + if (!cxArg->isJSContext()) 1.4781 + return true; 1.4782 + 1.4783 + JSContext *cx = cxArg->asJSContext(); 1.4784 + RootedValue val(cx, IdToValue(id)); 1.4785 + return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY, 1.4786 + JSDVG_IGNORE_STACK, val, js::NullPtr(), 1.4787 + nullptr, nullptr); 1.4788 +} 1.4789 + 1.4790 +bool 1.4791 +JSObject::reportNotConfigurable(ThreadSafeContext *cxArg, jsid id, unsigned report) 1.4792 +{ 1.4793 + if (cxArg->isForkJoinContext()) 1.4794 + return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report); 1.4795 + 1.4796 + if (!cxArg->isJSContext()) 1.4797 + return true; 1.4798 + 1.4799 + JSContext *cx = cxArg->asJSContext(); 1.4800 + RootedValue val(cx, IdToValue(id)); 1.4801 + return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE, 1.4802 + JSDVG_IGNORE_STACK, val, js::NullPtr(), 1.4803 + nullptr, nullptr); 1.4804 +} 1.4805 + 1.4806 +bool 1.4807 +JSObject::reportNotExtensible(ThreadSafeContext *cxArg, unsigned report) 1.4808 +{ 1.4809 + if (cxArg->isForkJoinContext()) 1.4810 + return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report); 1.4811 + 1.4812 + if (!cxArg->isJSContext()) 1.4813 + return true; 1.4814 + 1.4815 + JSContext *cx = cxArg->asJSContext(); 1.4816 + RootedValue val(cx, ObjectValue(*this)); 1.4817 + return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE, 1.4818 + JSDVG_IGNORE_STACK, val, js::NullPtr(), 1.4819 + nullptr, nullptr); 1.4820 +} 1.4821 + 1.4822 +bool 1.4823 +JSObject::callMethod(JSContext *cx, HandleId id, unsigned argc, Value *argv, MutableHandleValue vp) 1.4824 +{ 1.4825 + RootedValue fval(cx); 1.4826 + RootedObject obj(cx, this); 1.4827 + if (!JSObject::getGeneric(cx, obj, obj, id, &fval)) 1.4828 + return false; 1.4829 + return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp); 1.4830 +} 1.4831 + 1.4832 +template <ExecutionMode mode> 1.4833 +bool 1.4834 +baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg, 1.4835 + HandleObject obj, HandleObject receiver, HandleId id, 1.4836 + QualifiedBool qualified, MutableHandleValue vp, bool strict) 1.4837 +{ 1.4838 + JS_ASSERT(cxArg->isThreadLocal(obj)); 1.4839 + 1.4840 + if (MOZ_UNLIKELY(obj->watched())) { 1.4841 + if (mode == ParallelExecution) 1.4842 + return false; 1.4843 + 1.4844 + /* Fire watchpoints, if any. */ 1.4845 + JSContext *cx = cxArg->asJSContext(); 1.4846 + WatchpointMap *wpmap = cx->compartment()->watchpointMap; 1.4847 + if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp)) 1.4848 + return false; 1.4849 + } 1.4850 + 1.4851 + RootedObject pobj(cxArg); 1.4852 + RootedShape shape(cxArg); 1.4853 + if (mode == ParallelExecution) { 1.4854 + if (!LookupPropertyPure(obj, id, pobj.address(), shape.address())) 1.4855 + return false; 1.4856 + } else { 1.4857 + JSContext *cx = cxArg->asJSContext(); 1.4858 + if (!LookupNativeProperty(cx, obj, id, &pobj, &shape)) 1.4859 + return false; 1.4860 + } 1.4861 + if (shape) { 1.4862 + if (!pobj->isNative()) { 1.4863 + if (pobj->is<ProxyObject>()) { 1.4864 + if (mode == ParallelExecution) 1.4865 + return false; 1.4866 + 1.4867 + JSContext *cx = cxArg->asJSContext(); 1.4868 + Rooted<PropertyDescriptor> pd(cx); 1.4869 + if (!Proxy::getPropertyDescriptor(cx, pobj, id, &pd)) 1.4870 + return false; 1.4871 + 1.4872 + if ((pd.attributes() & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) { 1.4873 + return !pd.setter() || 1.4874 + CallSetter(cx, receiver, id, pd.setter(), pd.attributes(), strict, vp); 1.4875 + } 1.4876 + 1.4877 + if (pd.isReadonly()) { 1.4878 + if (strict) 1.4879 + return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR); 1.4880 + if (cx->options().extraWarnings()) 1.4881 + return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING); 1.4882 + return true; 1.4883 + } 1.4884 + } 1.4885 + 1.4886 + shape = nullptr; 1.4887 + } 1.4888 + } else { 1.4889 + /* We should never add properties to lexical blocks. */ 1.4890 + JS_ASSERT(!obj->is<BlockObject>()); 1.4891 + 1.4892 + if (obj->is<GlobalObject>() && !qualified) { 1.4893 + if (mode == ParallelExecution) 1.4894 + return false; 1.4895 + 1.4896 + if (!MaybeReportUndeclaredVarAssignment(cxArg->asJSContext(), JSID_TO_STRING(id))) 1.4897 + return false; 1.4898 + } 1.4899 + } 1.4900 + 1.4901 + /* 1.4902 + * Now either shape is null, meaning id was not found in obj or one of its 1.4903 + * prototypes; or shape is non-null, meaning id was found directly in pobj. 1.4904 + */ 1.4905 + unsigned attrs = JSPROP_ENUMERATE; 1.4906 + const Class *clasp = obj->getClass(); 1.4907 + PropertyOp getter = clasp->getProperty; 1.4908 + StrictPropertyOp setter = clasp->setProperty; 1.4909 + 1.4910 + if (IsImplicitDenseOrTypedArrayElement(shape)) { 1.4911 + /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */ 1.4912 + if (pobj != obj) 1.4913 + shape = nullptr; 1.4914 + } else if (shape) { 1.4915 + /* ES5 8.12.4 [[Put]] step 2. */ 1.4916 + if (shape->isAccessorDescriptor()) { 1.4917 + if (shape->hasDefaultSetter()) { 1.4918 + /* Bail out of parallel execution if we are strict to throw. */ 1.4919 + if (mode == ParallelExecution) 1.4920 + return !strict; 1.4921 + 1.4922 + return js_ReportGetterOnlyAssignment(cxArg->asJSContext(), strict); 1.4923 + } 1.4924 + } else { 1.4925 + JS_ASSERT(shape->isDataDescriptor()); 1.4926 + 1.4927 + if (!shape->writable()) { 1.4928 + /* 1.4929 + * Error in strict mode code, warn with extra warnings 1.4930 + * options, otherwise do nothing. 1.4931 + * 1.4932 + * Bail out of parallel execution if we are strict to throw. 1.4933 + */ 1.4934 + if (mode == ParallelExecution) 1.4935 + return !strict; 1.4936 + 1.4937 + JSContext *cx = cxArg->asJSContext(); 1.4938 + if (strict) 1.4939 + return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR); 1.4940 + if (cx->options().extraWarnings()) 1.4941 + return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING); 1.4942 + return true; 1.4943 + } 1.4944 + } 1.4945 + 1.4946 + attrs = shape->attributes(); 1.4947 + if (pobj != obj) { 1.4948 + /* 1.4949 + * We found id in a prototype object: prepare to share or shadow. 1.4950 + */ 1.4951 + if (!shape->shadowable()) { 1.4952 + if (shape->hasDefaultSetter() && !shape->hasGetterValue()) 1.4953 + return true; 1.4954 + 1.4955 + if (mode == ParallelExecution) 1.4956 + return false; 1.4957 + 1.4958 + return shape->set(cxArg->asJSContext(), obj, receiver, strict, vp); 1.4959 + } 1.4960 + 1.4961 + /* 1.4962 + * Preserve attrs except JSPROP_SHARED, getter, and setter when 1.4963 + * shadowing any property that has no slot (is shared). We must 1.4964 + * clear the shared attribute for the shadowing shape so that the 1.4965 + * property in obj that it defines has a slot to retain the value 1.4966 + * being set, in case the setter simply cannot operate on instances 1.4967 + * of obj's class by storing the value in some class-specific 1.4968 + * location. 1.4969 + */ 1.4970 + if (!shape->hasSlot()) { 1.4971 + attrs &= ~JSPROP_SHARED; 1.4972 + getter = shape->getter(); 1.4973 + setter = shape->setter(); 1.4974 + } else { 1.4975 + /* Restore attrs to the ECMA default for new properties. */ 1.4976 + attrs = JSPROP_ENUMERATE; 1.4977 + } 1.4978 + 1.4979 + /* 1.4980 + * Forget we found the proto-property now that we've copied any 1.4981 + * needed member values. 1.4982 + */ 1.4983 + shape = nullptr; 1.4984 + } 1.4985 + } 1.4986 + 1.4987 + if (IsImplicitDenseOrTypedArrayElement(shape)) { 1.4988 + uint32_t index = JSID_TO_INT(id); 1.4989 + 1.4990 + if (obj->is<TypedArrayObject>()) { 1.4991 + double d; 1.4992 + if (mode == ParallelExecution) { 1.4993 + // Bail if converting the value might invoke user-defined 1.4994 + // conversions. 1.4995 + if (vp.isObject()) 1.4996 + return false; 1.4997 + if (!NonObjectToNumber(cxArg, vp, &d)) 1.4998 + return false; 1.4999 + } else { 1.5000 + if (!ToNumber(cxArg->asJSContext(), vp, &d)) 1.5001 + return false; 1.5002 + } 1.5003 + 1.5004 + // Silently do nothing for out-of-bounds sets, for consistency with 1.5005 + // current behavior. (ES6 currently says to throw for this in 1.5006 + // strict mode code, so we may eventually need to change.) 1.5007 + TypedArrayObject &tarray = obj->as<TypedArrayObject>(); 1.5008 + if (index < tarray.length()) 1.5009 + TypedArrayObject::setElement(tarray, index, d); 1.5010 + return true; 1.5011 + } 1.5012 + 1.5013 + bool definesPast; 1.5014 + if (!WouldDefinePastNonwritableLength(cxArg, obj, index, strict, &definesPast)) 1.5015 + return false; 1.5016 + if (definesPast) { 1.5017 + /* Bail out of parallel execution if we are strict to throw. */ 1.5018 + if (mode == ParallelExecution) 1.5019 + return !strict; 1.5020 + return true; 1.5021 + } 1.5022 + 1.5023 + if (mode == ParallelExecution) 1.5024 + return obj->setDenseElementIfHasType(index, vp); 1.5025 + 1.5026 + obj->setDenseElementWithType(cxArg->asJSContext(), index, vp); 1.5027 + return true; 1.5028 + } 1.5029 + 1.5030 + if (obj->is<ArrayObject>() && id == NameToId(cxArg->names().length)) { 1.5031 + Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>()); 1.5032 + return ArraySetLength<mode>(cxArg, arr, id, attrs, vp, strict); 1.5033 + } 1.5034 + 1.5035 + if (!shape) { 1.5036 + bool extensible; 1.5037 + if (mode == ParallelExecution) { 1.5038 + if (obj->is<ProxyObject>()) 1.5039 + return false; 1.5040 + extensible = obj->nonProxyIsExtensible(); 1.5041 + } else { 1.5042 + if (!JSObject::isExtensible(cxArg->asJSContext(), obj, &extensible)) 1.5043 + return false; 1.5044 + } 1.5045 + 1.5046 + if (!extensible) { 1.5047 + /* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */ 1.5048 + if (strict) 1.5049 + return obj->reportNotExtensible(cxArg); 1.5050 + if (mode == SequentialExecution && cxArg->asJSContext()->options().extraWarnings()) 1.5051 + return obj->reportNotExtensible(cxArg, JSREPORT_STRICT | JSREPORT_WARNING); 1.5052 + return true; 1.5053 + } 1.5054 + 1.5055 + if (mode == ParallelExecution) { 1.5056 + if (obj->isDelegate()) 1.5057 + return false; 1.5058 + 1.5059 + if (getter != JS_PropertyStub || !HasTypePropertyId(obj, id, vp)) 1.5060 + return false; 1.5061 + } else { 1.5062 + JSContext *cx = cxArg->asJSContext(); 1.5063 + 1.5064 + /* Purge the property cache of now-shadowed id in obj's scope chain. */ 1.5065 + if (!PurgeScopeChain(cx, obj, id)) 1.5066 + return false; 1.5067 + } 1.5068 + 1.5069 + return DefinePropertyOrElement<mode>(cxArg, obj, id, getter, setter, 1.5070 + attrs, vp, true, strict); 1.5071 + } 1.5072 + 1.5073 + return NativeSet<mode>(cxArg, obj, receiver, shape, strict, vp); 1.5074 +} 1.5075 + 1.5076 +template bool 1.5077 +baseops::SetPropertyHelper<SequentialExecution>(JSContext *cx, HandleObject obj, 1.5078 + HandleObject receiver, HandleId id, 1.5079 + QualifiedBool qualified, 1.5080 + MutableHandleValue vp, bool strict); 1.5081 +template bool 1.5082 +baseops::SetPropertyHelper<ParallelExecution>(ForkJoinContext *cx, HandleObject obj, 1.5083 + HandleObject receiver, HandleId id, 1.5084 + QualifiedBool qualified, 1.5085 + MutableHandleValue vp, bool strict); 1.5086 + 1.5087 +bool 1.5088 +baseops::SetElementHelper(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, 1.5089 + MutableHandleValue vp, bool strict) 1.5090 +{ 1.5091 + RootedId id(cx); 1.5092 + if (!IndexToId(cx, index, &id)) 1.5093 + return false; 1.5094 + return baseops::SetPropertyHelper<SequentialExecution>(cx, obj, receiver, id, Qualified, vp, 1.5095 + strict); 1.5096 +} 1.5097 + 1.5098 +bool 1.5099 +baseops::GetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) 1.5100 +{ 1.5101 + RootedObject nobj(cx); 1.5102 + RootedShape shape(cx); 1.5103 + if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape)) 1.5104 + return false; 1.5105 + if (!shape) { 1.5106 + *attrsp = 0; 1.5107 + return true; 1.5108 + } 1.5109 + if (!nobj->isNative()) 1.5110 + return JSObject::getGenericAttributes(cx, nobj, id, attrsp); 1.5111 + 1.5112 + *attrsp = GetShapeAttributes(nobj, shape); 1.5113 + return true; 1.5114 +} 1.5115 + 1.5116 +bool 1.5117 +baseops::SetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp) 1.5118 +{ 1.5119 + RootedObject nobj(cx); 1.5120 + RootedShape shape(cx); 1.5121 + if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape)) 1.5122 + return false; 1.5123 + if (!shape) 1.5124 + return true; 1.5125 + if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) { 1.5126 + if (nobj->is<TypedArrayObject>()) { 1.5127 + if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT)) 1.5128 + return true; 1.5129 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS); 1.5130 + return false; 1.5131 + } 1.5132 + if (!JSObject::sparsifyDenseElement(cx, nobj, JSID_TO_INT(id))) 1.5133 + return false; 1.5134 + shape = obj->nativeLookup(cx, id); 1.5135 + } 1.5136 + if (nobj->isNative()) { 1.5137 + if (!JSObject::changePropertyAttributes(cx, nobj, shape, *attrsp)) 1.5138 + return false; 1.5139 + if (*attrsp & JSPROP_READONLY) 1.5140 + MarkTypePropertyNonWritable(cx, obj, id); 1.5141 + return true; 1.5142 + } else { 1.5143 + return JSObject::setGenericAttributes(cx, nobj, id, attrsp); 1.5144 + } 1.5145 +} 1.5146 + 1.5147 +bool 1.5148 +baseops::DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) 1.5149 +{ 1.5150 + RootedObject proto(cx); 1.5151 + RootedShape shape(cx); 1.5152 + if (!baseops::LookupProperty<CanGC>(cx, obj, id, &proto, &shape)) 1.5153 + return false; 1.5154 + if (!shape || proto != obj) { 1.5155 + /* 1.5156 + * If no property, or the property comes from a prototype, call the 1.5157 + * class's delProperty hook, passing succeeded as the result parameter. 1.5158 + */ 1.5159 + return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded); 1.5160 + } 1.5161 + 1.5162 + GCPoke(cx->runtime()); 1.5163 + 1.5164 + if (IsImplicitDenseOrTypedArrayElement(shape)) { 1.5165 + if (obj->is<TypedArrayObject>()) { 1.5166 + // Don't delete elements from typed arrays. 1.5167 + *succeeded = false; 1.5168 + return true; 1.5169 + } 1.5170 + 1.5171 + if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded)) 1.5172 + return false; 1.5173 + if (!succeeded) 1.5174 + return true; 1.5175 + 1.5176 + obj->setDenseElementHole(cx, JSID_TO_INT(id)); 1.5177 + return js_SuppressDeletedProperty(cx, obj, id); 1.5178 + } 1.5179 + 1.5180 + if (!shape->configurable()) { 1.5181 + *succeeded = false; 1.5182 + return true; 1.5183 + } 1.5184 + 1.5185 + RootedId propid(cx, shape->propid()); 1.5186 + if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, propid, succeeded)) 1.5187 + return false; 1.5188 + if (!succeeded) 1.5189 + return true; 1.5190 + 1.5191 + return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id); 1.5192 +} 1.5193 + 1.5194 +bool 1.5195 +baseops::DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, 1.5196 + bool *succeeded) 1.5197 +{ 1.5198 + Rooted<jsid> id(cx, NameToId(name)); 1.5199 + return baseops::DeleteGeneric(cx, obj, id, succeeded); 1.5200 +} 1.5201 + 1.5202 +bool 1.5203 +baseops::DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded) 1.5204 +{ 1.5205 + RootedId id(cx); 1.5206 + if (!IndexToId(cx, index, &id)) 1.5207 + return false; 1.5208 + return baseops::DeleteGeneric(cx, obj, id, succeeded); 1.5209 +} 1.5210 + 1.5211 +bool 1.5212 +js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable) 1.5213 +{ 1.5214 + RootedObject obj(cx, GetInnerObject(cx, origObj)); 1.5215 + if (obj->isNative()) { 1.5216 + // Use sparse indexes for watched objects, as dense elements can be 1.5217 + // written to without checking the watchpoint map. 1.5218 + if (!JSObject::sparsifyDenseElements(cx, obj)) 1.5219 + return false; 1.5220 + 1.5221 + types::MarkTypePropertyNonData(cx, obj, id); 1.5222 + } 1.5223 + 1.5224 + WatchpointMap *wpmap = cx->compartment()->watchpointMap; 1.5225 + if (!wpmap) { 1.5226 + wpmap = cx->runtime()->new_<WatchpointMap>(); 1.5227 + if (!wpmap || !wpmap->init()) { 1.5228 + js_ReportOutOfMemory(cx); 1.5229 + return false; 1.5230 + } 1.5231 + cx->compartment()->watchpointMap = wpmap; 1.5232 + } 1.5233 + 1.5234 + return wpmap->watch(cx, obj, id, js::WatchHandler, callable); 1.5235 +} 1.5236 + 1.5237 +bool 1.5238 +baseops::Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable) 1.5239 +{ 1.5240 + if (!obj->isNative() || obj->is<TypedArrayObject>()) { 1.5241 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH, 1.5242 + obj->getClass()->name); 1.5243 + return false; 1.5244 + } 1.5245 + 1.5246 + return WatchGuts(cx, obj, id, callable); 1.5247 +} 1.5248 + 1.5249 +bool 1.5250 +js::UnwatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id) 1.5251 +{ 1.5252 + // Looking in the map for an unsupported object will never hit, so we don't 1.5253 + // need to check for nativeness or watchable-ness here. 1.5254 + RootedObject obj(cx, GetInnerObject(cx, origObj)); 1.5255 + if (WatchpointMap *wpmap = cx->compartment()->watchpointMap) 1.5256 + wpmap->unwatch(obj, id, nullptr, nullptr); 1.5257 + return true; 1.5258 +} 1.5259 + 1.5260 +bool 1.5261 +baseops::Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id) 1.5262 +{ 1.5263 + return UnwatchGuts(cx, obj, id); 1.5264 +} 1.5265 + 1.5266 +bool 1.5267 +js::HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) 1.5268 +{ 1.5269 + if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) { 1.5270 + *vp = obj->getDenseElement(JSID_TO_INT(id)); 1.5271 + return true; 1.5272 + } 1.5273 + 1.5274 + if (Shape *shape = obj->nativeLookup(cx, id)) { 1.5275 + if (shape->hasDefaultGetter() && shape->hasSlot()) { 1.5276 + *vp = obj->nativeGetSlot(shape->slot()); 1.5277 + return true; 1.5278 + } 1.5279 + } 1.5280 + 1.5281 + return false; 1.5282 +} 1.5283 + 1.5284 +/* 1.5285 + * Gets |obj[id]|. If that value's not callable, returns true and stores a 1.5286 + * non-primitive value in *vp. If it's callable, calls it with no arguments 1.5287 + * and |obj| as |this|, returning the result in *vp. 1.5288 + * 1.5289 + * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2 1.5290 + * or steps 3-4. 1.5291 + */ 1.5292 +static bool 1.5293 +MaybeCallMethod(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) 1.5294 +{ 1.5295 + if (!JSObject::getGeneric(cx, obj, obj, id, vp)) 1.5296 + return false; 1.5297 + if (!js_IsCallable(vp)) { 1.5298 + vp.setObject(*obj); 1.5299 + return true; 1.5300 + } 1.5301 + return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp); 1.5302 +} 1.5303 + 1.5304 +JS_FRIEND_API(bool) 1.5305 +js::DefaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) 1.5306 +{ 1.5307 + JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID); 1.5308 + 1.5309 + Rooted<jsid> id(cx); 1.5310 + 1.5311 + const Class *clasp = obj->getClass(); 1.5312 + if (hint == JSTYPE_STRING) { 1.5313 + id = NameToId(cx->names().toString); 1.5314 + 1.5315 + /* Optimize (new String(...)).toString(). */ 1.5316 + if (clasp == &StringObject::class_) { 1.5317 + if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) { 1.5318 + vp.setString(obj->as<StringObject>().unbox()); 1.5319 + return true; 1.5320 + } 1.5321 + } 1.5322 + 1.5323 + if (!MaybeCallMethod(cx, obj, id, vp)) 1.5324 + return false; 1.5325 + if (vp.isPrimitive()) 1.5326 + return true; 1.5327 + 1.5328 + id = NameToId(cx->names().valueOf); 1.5329 + if (!MaybeCallMethod(cx, obj, id, vp)) 1.5330 + return false; 1.5331 + if (vp.isPrimitive()) 1.5332 + return true; 1.5333 + } else { 1.5334 + 1.5335 + /* Optimize new String(...).valueOf(). */ 1.5336 + if (clasp == &StringObject::class_) { 1.5337 + id = NameToId(cx->names().valueOf); 1.5338 + if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) { 1.5339 + vp.setString(obj->as<StringObject>().unbox()); 1.5340 + return true; 1.5341 + } 1.5342 + } 1.5343 + 1.5344 + /* Optimize new Number(...).valueOf(). */ 1.5345 + if (clasp == &NumberObject::class_) { 1.5346 + id = NameToId(cx->names().valueOf); 1.5347 + if (ClassMethodIsNative(cx, obj, &NumberObject::class_, id, js_num_valueOf)) { 1.5348 + vp.setNumber(obj->as<NumberObject>().unbox()); 1.5349 + return true; 1.5350 + } 1.5351 + } 1.5352 + 1.5353 + id = NameToId(cx->names().valueOf); 1.5354 + if (!MaybeCallMethod(cx, obj, id, vp)) 1.5355 + return false; 1.5356 + if (vp.isPrimitive()) 1.5357 + return true; 1.5358 + 1.5359 + id = NameToId(cx->names().toString); 1.5360 + if (!MaybeCallMethod(cx, obj, id, vp)) 1.5361 + return false; 1.5362 + if (vp.isPrimitive()) 1.5363 + return true; 1.5364 + } 1.5365 + 1.5366 + /* Avoid recursive death when decompiling in js_ReportValueError. */ 1.5367 + RootedString str(cx); 1.5368 + if (hint == JSTYPE_STRING) { 1.5369 + str = JS_InternString(cx, clasp->name); 1.5370 + if (!str) 1.5371 + return false; 1.5372 + } else { 1.5373 + str = nullptr; 1.5374 + } 1.5375 + 1.5376 + RootedValue val(cx, ObjectValue(*obj)); 1.5377 + js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, JSDVG_SEARCH_STACK, val, str, 1.5378 + (hint == JSTYPE_VOID) ? "primitive type" : TypeStrings[hint]); 1.5379 + return false; 1.5380 +} 1.5381 + 1.5382 +JS_FRIEND_API(bool) 1.5383 +JS_EnumerateState(JSContext *cx, HandleObject obj, JSIterateOp enum_op, 1.5384 + MutableHandleValue statep, JS::MutableHandleId idp) 1.5385 +{ 1.5386 + /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */ 1.5387 + const Class *clasp = obj->getClass(); 1.5388 + JSEnumerateOp enumerate = clasp->enumerate; 1.5389 + if (clasp->flags & JSCLASS_NEW_ENUMERATE) { 1.5390 + JS_ASSERT(enumerate != JS_EnumerateStub); 1.5391 + return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); 1.5392 + } 1.5393 + 1.5394 + if (!enumerate(cx, obj)) 1.5395 + return false; 1.5396 + 1.5397 + /* Tell InitNativeIterator to treat us like a native object. */ 1.5398 + JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL); 1.5399 + statep.setMagic(JS_NATIVE_ENUMERATE); 1.5400 + return true; 1.5401 +} 1.5402 + 1.5403 +bool 1.5404 +js::IsDelegate(JSContext *cx, HandleObject obj, const js::Value &v, bool *result) 1.5405 +{ 1.5406 + if (v.isPrimitive()) { 1.5407 + *result = false; 1.5408 + return true; 1.5409 + } 1.5410 + return IsDelegateOfObject(cx, obj, &v.toObject(), result); 1.5411 +} 1.5412 + 1.5413 +bool 1.5414 +js::IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result) 1.5415 +{ 1.5416 + RootedObject obj2(cx, obj); 1.5417 + for (;;) { 1.5418 + if (!JSObject::getProto(cx, obj2, &obj2)) 1.5419 + return false; 1.5420 + if (!obj2) { 1.5421 + *result = false; 1.5422 + return true; 1.5423 + } 1.5424 + if (obj2 == protoObj) { 1.5425 + *result = true; 1.5426 + return true; 1.5427 + } 1.5428 + } 1.5429 +} 1.5430 + 1.5431 +JSObject * 1.5432 +js::GetBuiltinPrototypePure(GlobalObject *global, JSProtoKey protoKey) 1.5433 +{ 1.5434 + JS_ASSERT(JSProto_Null <= protoKey); 1.5435 + JS_ASSERT(protoKey < JSProto_LIMIT); 1.5436 + 1.5437 + if (protoKey != JSProto_Null) { 1.5438 + const Value &v = global->getPrototype(protoKey); 1.5439 + if (v.isObject()) 1.5440 + return &v.toObject(); 1.5441 + } 1.5442 + 1.5443 + return nullptr; 1.5444 +} 1.5445 + 1.5446 +/* 1.5447 + * The first part of this function has been hand-expanded and optimized into 1.5448 + * NewBuiltinClassInstance in jsobjinlines.h. 1.5449 + */ 1.5450 +bool 1.5451 +js::FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp) 1.5452 +{ 1.5453 + protop.set(nullptr); 1.5454 + JSProtoKey protoKey = GetClassProtoKey(clasp); 1.5455 + if (protoKey != JSProto_Null) 1.5456 + return GetBuiltinPrototype(cx, protoKey, protop); 1.5457 + 1.5458 + RootedObject ctor(cx); 1.5459 + if (!FindClassObject(cx, &ctor, clasp)) 1.5460 + return false; 1.5461 + 1.5462 + if (ctor && ctor->is<JSFunction>()) { 1.5463 + RootedValue v(cx); 1.5464 + if (cx->isJSContext()) { 1.5465 + if (!JSObject::getProperty(cx->asJSContext(), 1.5466 + ctor, ctor, cx->names().prototype, &v)) 1.5467 + { 1.5468 + return false; 1.5469 + } 1.5470 + } else { 1.5471 + Shape *shape = ctor->nativeLookup(cx, cx->names().prototype); 1.5472 + if (!shape || !NativeGetPureInline(ctor, shape, v.address())) 1.5473 + return false; 1.5474 + } 1.5475 + if (v.isObject()) 1.5476 + protop.set(&v.toObject()); 1.5477 + } 1.5478 + return true; 1.5479 +} 1.5480 + 1.5481 +JSObject * 1.5482 +js::PrimitiveToObject(JSContext *cx, const Value &v) 1.5483 +{ 1.5484 + if (v.isString()) { 1.5485 + Rooted<JSString*> str(cx, v.toString()); 1.5486 + return StringObject::create(cx, str); 1.5487 + } 1.5488 + if (v.isNumber()) 1.5489 + return NumberObject::create(cx, v.toNumber()); 1.5490 + 1.5491 + JS_ASSERT(v.isBoolean()); 1.5492 + return BooleanObject::create(cx, v.toBoolean()); 1.5493 +} 1.5494 + 1.5495 +/* Callers must handle the already-object case . */ 1.5496 +JSObject * 1.5497 +js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack) 1.5498 +{ 1.5499 + JS_ASSERT(!val.isMagic()); 1.5500 + JS_ASSERT(!val.isObject()); 1.5501 + 1.5502 + if (val.isNullOrUndefined()) { 1.5503 + if (reportScanStack) { 1.5504 + js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val, NullPtr()); 1.5505 + } else { 1.5506 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, 1.5507 + val.isNull() ? "null" : "undefined", "object"); 1.5508 + } 1.5509 + return nullptr; 1.5510 + } 1.5511 + 1.5512 + return PrimitiveToObject(cx, val); 1.5513 +} 1.5514 + 1.5515 +void 1.5516 +js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) 1.5517 +{ 1.5518 + JS_ASSERT(trc->debugPrinter() == js_GetObjectSlotName); 1.5519 + 1.5520 + JSObject *obj = (JSObject *)trc->debugPrintArg(); 1.5521 + uint32_t slot = uint32_t(trc->debugPrintIndex()); 1.5522 + 1.5523 + Shape *shape; 1.5524 + if (obj->isNative()) { 1.5525 + shape = obj->lastProperty(); 1.5526 + while (shape && (!shape->hasSlot() || shape->slot() != slot)) 1.5527 + shape = shape->previous(); 1.5528 + } else { 1.5529 + shape = nullptr; 1.5530 + } 1.5531 + 1.5532 + if (!shape) { 1.5533 + const char *slotname = nullptr; 1.5534 + if (obj->is<GlobalObject>()) { 1.5535 +#define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init,clasp) \ 1.5536 + if ((code) == slot) { slotname = js_##name##_str; goto found; } 1.5537 + JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE) 1.5538 +#undef TEST_SLOT_MATCHES_PROTOTYPE 1.5539 + } 1.5540 + found: 1.5541 + if (slotname) 1.5542 + JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname); 1.5543 + else 1.5544 + JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); 1.5545 + } else { 1.5546 + jsid propid = shape->propid(); 1.5547 + if (JSID_IS_INT(propid)) { 1.5548 + JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid)); 1.5549 + } else if (JSID_IS_ATOM(propid)) { 1.5550 + PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0); 1.5551 + } else { 1.5552 + JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); 1.5553 + } 1.5554 + } 1.5555 +} 1.5556 + 1.5557 +bool 1.5558 +js_ReportGetterOnlyAssignment(JSContext *cx, bool strict) 1.5559 +{ 1.5560 + return JS_ReportErrorFlagsAndNumber(cx, 1.5561 + strict 1.5562 + ? JSREPORT_ERROR 1.5563 + : JSREPORT_WARNING | JSREPORT_STRICT, 1.5564 + js_GetErrorMessage, nullptr, 1.5565 + JSMSG_GETTER_ONLY); 1.5566 +} 1.5567 + 1.5568 +JS_FRIEND_API(bool) 1.5569 +js_GetterOnlyPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict, 1.5570 + MutableHandleValue vp) 1.5571 +{ 1.5572 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY); 1.5573 + return false; 1.5574 +} 1.5575 + 1.5576 +#ifdef DEBUG 1.5577 + 1.5578 +/* 1.5579 + * Routines to print out values during debugging. These are FRIEND_API to help 1.5580 + * the debugger find them and to support temporarily hacking js_Dump* calls 1.5581 + * into other code. 1.5582 + */ 1.5583 + 1.5584 +static void 1.5585 +dumpValue(const Value &v) 1.5586 +{ 1.5587 + if (v.isNull()) 1.5588 + fprintf(stderr, "null"); 1.5589 + else if (v.isUndefined()) 1.5590 + fprintf(stderr, "undefined"); 1.5591 + else if (v.isInt32()) 1.5592 + fprintf(stderr, "%d", v.toInt32()); 1.5593 + else if (v.isDouble()) 1.5594 + fprintf(stderr, "%g", v.toDouble()); 1.5595 + else if (v.isString()) 1.5596 + v.toString()->dump(); 1.5597 + else if (v.isObject() && v.toObject().is<JSFunction>()) { 1.5598 + JSFunction *fun = &v.toObject().as<JSFunction>(); 1.5599 + if (fun->displayAtom()) { 1.5600 + fputs("<function ", stderr); 1.5601 + FileEscapedString(stderr, fun->displayAtom(), 0); 1.5602 + } else { 1.5603 + fputs("<unnamed function", stderr); 1.5604 + } 1.5605 + if (fun->hasScript()) { 1.5606 + JSScript *script = fun->nonLazyScript(); 1.5607 + fprintf(stderr, " (%s:%d)", 1.5608 + script->filename() ? script->filename() : "", (int) script->lineno()); 1.5609 + } 1.5610 + fprintf(stderr, " at %p>", (void *) fun); 1.5611 + } else if (v.isObject()) { 1.5612 + JSObject *obj = &v.toObject(); 1.5613 + const Class *clasp = obj->getClass(); 1.5614 + fprintf(stderr, "<%s%s at %p>", 1.5615 + clasp->name, 1.5616 + (clasp == &JSObject::class_) ? "" : " object", 1.5617 + (void *) obj); 1.5618 + } else if (v.isBoolean()) { 1.5619 + if (v.toBoolean()) 1.5620 + fprintf(stderr, "true"); 1.5621 + else 1.5622 + fprintf(stderr, "false"); 1.5623 + } else if (v.isMagic()) { 1.5624 + fprintf(stderr, "<invalid"); 1.5625 +#ifdef DEBUG 1.5626 + switch (v.whyMagic()) { 1.5627 + case JS_ELEMENTS_HOLE: fprintf(stderr, " elements hole"); break; 1.5628 + case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break; 1.5629 + case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break; 1.5630 + case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break; 1.5631 + case JS_OPTIMIZED_OUT: fprintf(stderr, " optimized out"); break; 1.5632 + default: fprintf(stderr, " ?!"); break; 1.5633 + } 1.5634 +#endif 1.5635 + fprintf(stderr, ">"); 1.5636 + } else { 1.5637 + fprintf(stderr, "unexpected value"); 1.5638 + } 1.5639 +} 1.5640 + 1.5641 +JS_FRIEND_API(void) 1.5642 +js_DumpValue(const Value &val) 1.5643 +{ 1.5644 + dumpValue(val); 1.5645 + fputc('\n', stderr); 1.5646 +} 1.5647 + 1.5648 +JS_FRIEND_API(void) 1.5649 +js_DumpId(jsid id) 1.5650 +{ 1.5651 + fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id)); 1.5652 + dumpValue(IdToValue(id)); 1.5653 + fputc('\n', stderr); 1.5654 +} 1.5655 + 1.5656 +static void 1.5657 +DumpProperty(JSObject *obj, Shape &shape) 1.5658 +{ 1.5659 + jsid id = shape.propid(); 1.5660 + uint8_t attrs = shape.attributes(); 1.5661 + 1.5662 + fprintf(stderr, " ((JSShape *) %p) ", (void *) &shape); 1.5663 + if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate "); 1.5664 + if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly "); 1.5665 + if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent "); 1.5666 + if (attrs & JSPROP_SHARED) fprintf(stderr, "shared "); 1.5667 + 1.5668 + if (shape.hasGetterValue()) 1.5669 + fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject()); 1.5670 + else if (!shape.hasDefaultGetter()) 1.5671 + fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.getterOp())); 1.5672 + 1.5673 + if (shape.hasSetterValue()) 1.5674 + fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject()); 1.5675 + else if (!shape.hasDefaultSetter()) 1.5676 + fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp())); 1.5677 + 1.5678 + if (JSID_IS_ATOM(id)) 1.5679 + JSID_TO_STRING(id)->dump(); 1.5680 + else if (JSID_IS_INT(id)) 1.5681 + fprintf(stderr, "%d", (int) JSID_TO_INT(id)); 1.5682 + else 1.5683 + fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id)); 1.5684 + 1.5685 + uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT; 1.5686 + fprintf(stderr, ": slot %d", slot); 1.5687 + if (shape.hasSlot()) { 1.5688 + fprintf(stderr, " = "); 1.5689 + dumpValue(obj->getSlot(slot)); 1.5690 + } else if (slot != SHAPE_INVALID_SLOT) { 1.5691 + fprintf(stderr, " (INVALID!)"); 1.5692 + } 1.5693 + fprintf(stderr, "\n"); 1.5694 +} 1.5695 + 1.5696 +bool 1.5697 +JSObject::uninlinedIsProxy() const 1.5698 +{ 1.5699 + return is<ProxyObject>(); 1.5700 +} 1.5701 + 1.5702 +void 1.5703 +JSObject::dump() 1.5704 +{ 1.5705 + JSObject *obj = this; 1.5706 + fprintf(stderr, "object %p\n", (void *) obj); 1.5707 + const Class *clasp = obj->getClass(); 1.5708 + fprintf(stderr, "class %p %s\n", (const void *)clasp, clasp->name); 1.5709 + 1.5710 + fprintf(stderr, "flags:"); 1.5711 + if (obj->isDelegate()) fprintf(stderr, " delegate"); 1.5712 + if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) fprintf(stderr, " not_extensible"); 1.5713 + if (obj->isIndexed()) fprintf(stderr, " indexed"); 1.5714 + if (obj->isBoundFunction()) fprintf(stderr, " bound_function"); 1.5715 + if (obj->isVarObj()) fprintf(stderr, " varobj"); 1.5716 + if (obj->watched()) fprintf(stderr, " watched"); 1.5717 + if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton"); 1.5718 + if (obj->isNewTypeUnknown()) fprintf(stderr, " new_type_unknown"); 1.5719 + if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto"); 1.5720 + if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access"); 1.5721 + 1.5722 + if (obj->isNative()) { 1.5723 + if (obj->inDictionaryMode()) 1.5724 + fprintf(stderr, " inDictionaryMode"); 1.5725 + if (obj->hasShapeTable()) 1.5726 + fprintf(stderr, " hasShapeTable"); 1.5727 + } 1.5728 + fprintf(stderr, "\n"); 1.5729 + 1.5730 + if (obj->isNative()) { 1.5731 + uint32_t slots = obj->getDenseInitializedLength(); 1.5732 + if (slots) { 1.5733 + fprintf(stderr, "elements\n"); 1.5734 + for (uint32_t i = 0; i < slots; i++) { 1.5735 + fprintf(stderr, " %3d: ", i); 1.5736 + dumpValue(obj->getDenseElement(i)); 1.5737 + fprintf(stderr, "\n"); 1.5738 + fflush(stderr); 1.5739 + } 1.5740 + } 1.5741 + } 1.5742 + 1.5743 + fprintf(stderr, "proto "); 1.5744 + TaggedProto proto = obj->getTaggedProto(); 1.5745 + if (proto.isLazy()) 1.5746 + fprintf(stderr, "<lazy>"); 1.5747 + else 1.5748 + dumpValue(ObjectOrNullValue(proto.toObjectOrNull())); 1.5749 + fputc('\n', stderr); 1.5750 + 1.5751 + fprintf(stderr, "parent "); 1.5752 + dumpValue(ObjectOrNullValue(obj->getParent())); 1.5753 + fputc('\n', stderr); 1.5754 + 1.5755 + if (clasp->flags & JSCLASS_HAS_PRIVATE) 1.5756 + fprintf(stderr, "private %p\n", obj->getPrivate()); 1.5757 + 1.5758 + if (!obj->isNative()) 1.5759 + fprintf(stderr, "not native\n"); 1.5760 + 1.5761 + uint32_t reservedEnd = JSCLASS_RESERVED_SLOTS(clasp); 1.5762 + uint32_t slots = obj->slotSpan(); 1.5763 + uint32_t stop = obj->isNative() ? reservedEnd : slots; 1.5764 + if (stop > 0) 1.5765 + fprintf(stderr, obj->isNative() ? "reserved slots:\n" : "slots:\n"); 1.5766 + for (uint32_t i = 0; i < stop; i++) { 1.5767 + fprintf(stderr, " %3d ", i); 1.5768 + if (i < reservedEnd) 1.5769 + fprintf(stderr, "(reserved) "); 1.5770 + fprintf(stderr, "= "); 1.5771 + dumpValue(obj->getSlot(i)); 1.5772 + fputc('\n', stderr); 1.5773 + } 1.5774 + 1.5775 + if (obj->isNative()) { 1.5776 + fprintf(stderr, "properties:\n"); 1.5777 + Vector<Shape *, 8, SystemAllocPolicy> props; 1.5778 + for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) 1.5779 + props.append(&r.front()); 1.5780 + for (size_t i = props.length(); i-- != 0;) 1.5781 + DumpProperty(obj, *props[i]); 1.5782 + } 1.5783 + fputc('\n', stderr); 1.5784 +} 1.5785 + 1.5786 +static void 1.5787 +MaybeDumpObject(const char *name, JSObject *obj) 1.5788 +{ 1.5789 + if (obj) { 1.5790 + fprintf(stderr, " %s: ", name); 1.5791 + dumpValue(ObjectValue(*obj)); 1.5792 + fputc('\n', stderr); 1.5793 + } 1.5794 +} 1.5795 + 1.5796 +static void 1.5797 +MaybeDumpValue(const char *name, const Value &v) 1.5798 +{ 1.5799 + if (!v.isNull()) { 1.5800 + fprintf(stderr, " %s: ", name); 1.5801 + dumpValue(v); 1.5802 + fputc('\n', stderr); 1.5803 + } 1.5804 +} 1.5805 + 1.5806 +JS_FRIEND_API(void) 1.5807 +js_DumpInterpreterFrame(JSContext *cx, InterpreterFrame *start) 1.5808 +{ 1.5809 + /* This should only called during live debugging. */ 1.5810 + ScriptFrameIter i(cx, ScriptFrameIter::GO_THROUGH_SAVED); 1.5811 + if (!start) { 1.5812 + if (i.done()) { 1.5813 + fprintf(stderr, "no stack for cx = %p\n", (void*) cx); 1.5814 + return; 1.5815 + } 1.5816 + } else { 1.5817 + while (!i.done() && !i.isJit() && i.interpFrame() != start) 1.5818 + ++i; 1.5819 + 1.5820 + if (i.done()) { 1.5821 + fprintf(stderr, "fp = %p not found in cx = %p\n", 1.5822 + (void *)start, (void *)cx); 1.5823 + return; 1.5824 + } 1.5825 + } 1.5826 + 1.5827 + for (; !i.done(); ++i) { 1.5828 + if (i.isJit()) 1.5829 + fprintf(stderr, "JIT frame\n"); 1.5830 + else 1.5831 + fprintf(stderr, "InterpreterFrame at %p\n", (void *) i.interpFrame()); 1.5832 + 1.5833 + if (i.isFunctionFrame()) { 1.5834 + fprintf(stderr, "callee fun: "); 1.5835 + dumpValue(i.calleev()); 1.5836 + } else { 1.5837 + fprintf(stderr, "global frame, no callee"); 1.5838 + } 1.5839 + fputc('\n', stderr); 1.5840 + 1.5841 + fprintf(stderr, "file %s line %u\n", 1.5842 + i.script()->filename(), (unsigned) i.script()->lineno()); 1.5843 + 1.5844 + if (jsbytecode *pc = i.pc()) { 1.5845 + fprintf(stderr, " pc = %p\n", pc); 1.5846 + fprintf(stderr, " current op: %s\n", js_CodeName[*pc]); 1.5847 + MaybeDumpObject("staticScope", i.script()->getStaticScope(pc)); 1.5848 + } 1.5849 + MaybeDumpValue("this", i.thisv()); 1.5850 + if (!i.isJit()) { 1.5851 + fprintf(stderr, " rval: "); 1.5852 + dumpValue(i.interpFrame()->returnValue()); 1.5853 + fputc('\n', stderr); 1.5854 + } 1.5855 + 1.5856 + fprintf(stderr, " flags:"); 1.5857 + if (i.isConstructing()) 1.5858 + fprintf(stderr, " constructing"); 1.5859 + if (!i.isJit() && i.interpFrame()->isDebuggerFrame()) 1.5860 + fprintf(stderr, " debugger"); 1.5861 + if (i.isEvalFrame()) 1.5862 + fprintf(stderr, " eval"); 1.5863 + if (!i.isJit() && i.interpFrame()->isYielding()) 1.5864 + fprintf(stderr, " yielding"); 1.5865 + if (!i.isJit() && i.interpFrame()->isGeneratorFrame()) 1.5866 + fprintf(stderr, " generator"); 1.5867 + fputc('\n', stderr); 1.5868 + 1.5869 + fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) i.scopeChain()); 1.5870 + 1.5871 + fputc('\n', stderr); 1.5872 + } 1.5873 +} 1.5874 + 1.5875 +#endif /* DEBUG */ 1.5876 + 1.5877 +JS_FRIEND_API(void) 1.5878 +js_DumpBacktrace(JSContext *cx) 1.5879 +{ 1.5880 + Sprinter sprinter(cx); 1.5881 + sprinter.init(); 1.5882 + size_t depth = 0; 1.5883 + for (ScriptFrameIter i(cx); !i.done(); ++i, ++depth) { 1.5884 + const char *filename = JS_GetScriptFilename(i.script()); 1.5885 + unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc()); 1.5886 + JSScript *script = i.script(); 1.5887 + sprinter.printf("#%d %14p %s:%d (%p @ %d)\n", 1.5888 + depth, (i.isJit() ? 0 : i.interpFrame()), filename, line, 1.5889 + script, script->pcToOffset(i.pc())); 1.5890 + } 1.5891 + fprintf(stdout, "%s", sprinter.string()); 1.5892 +} 1.5893 +void 1.5894 +JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes) 1.5895 +{ 1.5896 + if (hasDynamicSlots()) 1.5897 + sizes->mallocHeapSlots += mallocSizeOf(slots); 1.5898 + 1.5899 + if (hasDynamicElements()) { 1.5900 + js::ObjectElements *elements = getElementsHeader(); 1.5901 + sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements); 1.5902 + } 1.5903 + 1.5904 + // Other things may be measured in the future if DMD indicates it is worthwhile. 1.5905 + if (is<JSFunction>() || 1.5906 + is<JSObject>() || 1.5907 + is<ArrayObject>() || 1.5908 + is<CallObject>() || 1.5909 + is<RegExpObject>() || 1.5910 + is<ProxyObject>()) 1.5911 + { 1.5912 + // Do nothing. But this function is hot, and we win by getting the 1.5913 + // common cases out of the way early. Some stats on the most common 1.5914 + // classes, as measured during a vanilla browser session: 1.5915 + // - (53.7%, 53.7%): Function 1.5916 + // - (18.0%, 71.7%): Object 1.5917 + // - (16.9%, 88.6%): Array 1.5918 + // - ( 3.9%, 92.5%): Call 1.5919 + // - ( 2.8%, 95.3%): RegExp 1.5920 + // - ( 1.0%, 96.4%): Proxy 1.5921 + 1.5922 + } else if (is<ArgumentsObject>()) { 1.5923 + sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf); 1.5924 + } else if (is<RegExpStaticsObject>()) { 1.5925 + sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf); 1.5926 + } else if (is<PropertyIteratorObject>()) { 1.5927 + sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf); 1.5928 + } else if (is<ArrayBufferObject>() || is<SharedArrayBufferObject>()) { 1.5929 + ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, sizes); 1.5930 +#ifdef JS_ION 1.5931 + } else if (is<AsmJSModuleObject>()) { 1.5932 + as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS, 1.5933 + &sizes->mallocHeapAsmJSModuleData); 1.5934 +#endif 1.5935 +#ifdef JS_HAS_CTYPES 1.5936 + } else { 1.5937 + // This must be the last case. 1.5938 + sizes->mallocHeapCtypesData += 1.5939 + js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this)); 1.5940 +#endif 1.5941 + } 1.5942 +}