1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsinfer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,4596 @@ 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 +#include "jsinferinlines.h" 1.11 + 1.12 +#include "mozilla/DebugOnly.h" 1.13 +#include "mozilla/MemoryReporting.h" 1.14 +#include "mozilla/PodOperations.h" 1.15 + 1.16 +#include "jsapi.h" 1.17 +#include "jscntxt.h" 1.18 +#include "jsgc.h" 1.19 +#include "jshashutil.h" 1.20 +#include "jsobj.h" 1.21 +#include "jsprf.h" 1.22 +#include "jsscript.h" 1.23 +#include "jsstr.h" 1.24 +#include "jsworkers.h" 1.25 +#include "prmjtime.h" 1.26 + 1.27 +#include "gc/Marking.h" 1.28 +#ifdef JS_ION 1.29 +#include "jit/BaselineJIT.h" 1.30 +#include "jit/Ion.h" 1.31 +#include "jit/IonAnalysis.h" 1.32 +#include "jit/JitCompartment.h" 1.33 +#endif 1.34 +#include "js/MemoryMetrics.h" 1.35 +#include "vm/Opcodes.h" 1.36 +#include "vm/Shape.h" 1.37 + 1.38 +#include "jsatominlines.h" 1.39 +#include "jsgcinlines.h" 1.40 +#include "jsobjinlines.h" 1.41 +#include "jsscriptinlines.h" 1.42 + 1.43 +#include "jit/ExecutionMode-inl.h" 1.44 + 1.45 +using namespace js; 1.46 +using namespace js::gc; 1.47 +using namespace js::types; 1.48 +using namespace js::analyze; 1.49 + 1.50 +using mozilla::DebugOnly; 1.51 +using mozilla::Maybe; 1.52 +using mozilla::PodArrayZero; 1.53 +using mozilla::PodCopy; 1.54 +using mozilla::PodZero; 1.55 + 1.56 +static inline jsid 1.57 +id_prototype(JSContext *cx) { 1.58 + return NameToId(cx->names().prototype); 1.59 +} 1.60 + 1.61 +static inline jsid 1.62 +id___proto__(JSContext *cx) { 1.63 + return NameToId(cx->names().proto); 1.64 +} 1.65 + 1.66 +static inline jsid 1.67 +id_constructor(JSContext *cx) { 1.68 + return NameToId(cx->names().constructor); 1.69 +} 1.70 + 1.71 +static inline jsid 1.72 +id_caller(JSContext *cx) { 1.73 + return NameToId(cx->names().caller); 1.74 +} 1.75 + 1.76 +#ifdef DEBUG 1.77 +const char * 1.78 +types::TypeIdStringImpl(jsid id) 1.79 +{ 1.80 + if (JSID_IS_VOID(id)) 1.81 + return "(index)"; 1.82 + if (JSID_IS_EMPTY(id)) 1.83 + return "(new)"; 1.84 + static char bufs[4][100]; 1.85 + static unsigned which = 0; 1.86 + which = (which + 1) & 3; 1.87 + PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0); 1.88 + return bufs[which]; 1.89 +} 1.90 +#endif 1.91 + 1.92 +///////////////////////////////////////////////////////////////////// 1.93 +// Logging 1.94 +///////////////////////////////////////////////////////////////////// 1.95 + 1.96 +#ifdef DEBUG 1.97 + 1.98 +static bool InferSpewActive(SpewChannel channel) 1.99 +{ 1.100 + static bool active[SPEW_COUNT]; 1.101 + static bool checked = false; 1.102 + if (!checked) { 1.103 + checked = true; 1.104 + PodArrayZero(active); 1.105 + const char *env = getenv("INFERFLAGS"); 1.106 + if (!env) 1.107 + return false; 1.108 + if (strstr(env, "ops")) 1.109 + active[ISpewOps] = true; 1.110 + if (strstr(env, "result")) 1.111 + active[ISpewResult] = true; 1.112 + if (strstr(env, "full")) { 1.113 + for (unsigned i = 0; i < SPEW_COUNT; i++) 1.114 + active[i] = true; 1.115 + } 1.116 + } 1.117 + return active[channel]; 1.118 +} 1.119 + 1.120 +static bool InferSpewColorable() 1.121 +{ 1.122 + /* Only spew colors on xterm-color to not screw up emacs. */ 1.123 + static bool colorable = false; 1.124 + static bool checked = false; 1.125 + if (!checked) { 1.126 + checked = true; 1.127 + const char *env = getenv("TERM"); 1.128 + if (!env) 1.129 + return false; 1.130 + if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0) 1.131 + colorable = true; 1.132 + } 1.133 + return colorable; 1.134 +} 1.135 + 1.136 +const char * 1.137 +types::InferSpewColorReset() 1.138 +{ 1.139 + if (!InferSpewColorable()) 1.140 + return ""; 1.141 + return "\x1b[0m"; 1.142 +} 1.143 + 1.144 +const char * 1.145 +types::InferSpewColor(TypeConstraint *constraint) 1.146 +{ 1.147 + /* Type constraints are printed out using foreground colors. */ 1.148 + static const char * const colors[] = { "\x1b[31m", "\x1b[32m", "\x1b[33m", 1.149 + "\x1b[34m", "\x1b[35m", "\x1b[36m", 1.150 + "\x1b[37m" }; 1.151 + if (!InferSpewColorable()) 1.152 + return ""; 1.153 + return colors[DefaultHasher<TypeConstraint *>::hash(constraint) % 7]; 1.154 +} 1.155 + 1.156 +const char * 1.157 +types::InferSpewColor(TypeSet *types) 1.158 +{ 1.159 + /* Type sets are printed out using bold colors. */ 1.160 + static const char * const colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m", 1.161 + "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m", 1.162 + "\x1b[1;37m" }; 1.163 + if (!InferSpewColorable()) 1.164 + return ""; 1.165 + return colors[DefaultHasher<TypeSet *>::hash(types) % 7]; 1.166 +} 1.167 + 1.168 +const char * 1.169 +types::TypeString(Type type) 1.170 +{ 1.171 + if (type.isPrimitive()) { 1.172 + switch (type.primitive()) { 1.173 + case JSVAL_TYPE_UNDEFINED: 1.174 + return "void"; 1.175 + case JSVAL_TYPE_NULL: 1.176 + return "null"; 1.177 + case JSVAL_TYPE_BOOLEAN: 1.178 + return "bool"; 1.179 + case JSVAL_TYPE_INT32: 1.180 + return "int"; 1.181 + case JSVAL_TYPE_DOUBLE: 1.182 + return "float"; 1.183 + case JSVAL_TYPE_STRING: 1.184 + return "string"; 1.185 + case JSVAL_TYPE_MAGIC: 1.186 + return "lazyargs"; 1.187 + default: 1.188 + MOZ_ASSUME_UNREACHABLE("Bad type"); 1.189 + } 1.190 + } 1.191 + if (type.isUnknown()) 1.192 + return "unknown"; 1.193 + if (type.isAnyObject()) 1.194 + return " object"; 1.195 + 1.196 + static char bufs[4][40]; 1.197 + static unsigned which = 0; 1.198 + which = (which + 1) & 3; 1.199 + 1.200 + if (type.isSingleObject()) 1.201 + JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleObject()); 1.202 + else 1.203 + JS_snprintf(bufs[which], 40, "[0x%p]", (void *) type.typeObject()); 1.204 + 1.205 + return bufs[which]; 1.206 +} 1.207 + 1.208 +const char * 1.209 +types::TypeObjectString(TypeObject *type) 1.210 +{ 1.211 + return TypeString(Type::ObjectType(type)); 1.212 +} 1.213 + 1.214 +unsigned JSScript::id() { 1.215 + if (!id_) { 1.216 + id_ = ++compartment()->types.scriptCount; 1.217 + InferSpew(ISpewOps, "script #%u: %p %s:%d", 1.218 + id_, this, filename() ? filename() : "<null>", lineno()); 1.219 + } 1.220 + return id_; 1.221 +} 1.222 + 1.223 +void 1.224 +types::InferSpew(SpewChannel channel, const char *fmt, ...) 1.225 +{ 1.226 + if (!InferSpewActive(channel)) 1.227 + return; 1.228 + 1.229 + va_list ap; 1.230 + va_start(ap, fmt); 1.231 + fprintf(stderr, "[infer] "); 1.232 + vfprintf(stderr, fmt, ap); 1.233 + fprintf(stderr, "\n"); 1.234 + va_end(ap); 1.235 +} 1.236 + 1.237 +bool 1.238 +types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value) 1.239 +{ 1.240 + /* 1.241 + * Check the correctness of the type information in the object's property 1.242 + * against an actual value. 1.243 + */ 1.244 + if (!obj->unknownProperties() && !value.isUndefined()) { 1.245 + id = IdToTypeId(id); 1.246 + 1.247 + /* Watch for properties which inference does not monitor. */ 1.248 + if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) 1.249 + return true; 1.250 + 1.251 + Type type = GetValueType(value); 1.252 + 1.253 + AutoEnterAnalysis enter(cx); 1.254 + 1.255 + /* 1.256 + * We don't track types for properties inherited from prototypes which 1.257 + * haven't yet been accessed during analysis of the inheriting object. 1.258 + * Don't do the property instantiation now. 1.259 + */ 1.260 + TypeSet *types = obj->maybeGetProperty(id); 1.261 + if (!types) 1.262 + return true; 1.263 + 1.264 + if (!types->hasType(type)) { 1.265 + TypeFailure(cx, "Missing type in object %s %s: %s", 1.266 + TypeObjectString(obj), TypeIdString(id), TypeString(type)); 1.267 + } 1.268 + } 1.269 + return true; 1.270 +} 1.271 + 1.272 +#endif 1.273 + 1.274 +void 1.275 +types::TypeFailure(JSContext *cx, const char *fmt, ...) 1.276 +{ 1.277 + char msgbuf[1024]; /* Larger error messages will be truncated */ 1.278 + char errbuf[1024]; 1.279 + 1.280 + va_list ap; 1.281 + va_start(ap, fmt); 1.282 + JS_vsnprintf(errbuf, sizeof(errbuf), fmt, ap); 1.283 + va_end(ap); 1.284 + 1.285 + JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf); 1.286 + 1.287 + /* Dump type state, even if INFERFLAGS is unset. */ 1.288 + cx->compartment()->types.print(cx, true); 1.289 + 1.290 + MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__); 1.291 + MOZ_CRASH(); 1.292 +} 1.293 + 1.294 +///////////////////////////////////////////////////////////////////// 1.295 +// TypeSet 1.296 +///////////////////////////////////////////////////////////////////// 1.297 + 1.298 +TemporaryTypeSet::TemporaryTypeSet(Type type) 1.299 +{ 1.300 + if (type.isUnknown()) { 1.301 + flags |= TYPE_FLAG_BASE_MASK; 1.302 + } else if (type.isPrimitive()) { 1.303 + flags = PrimitiveTypeFlag(type.primitive()); 1.304 + if (flags == TYPE_FLAG_DOUBLE) 1.305 + flags |= TYPE_FLAG_INT32; 1.306 + } else if (type.isAnyObject()) { 1.307 + flags |= TYPE_FLAG_ANYOBJECT; 1.308 + } else if (type.isTypeObject() && type.typeObject()->unknownProperties()) { 1.309 + flags |= TYPE_FLAG_ANYOBJECT; 1.310 + } else { 1.311 + setBaseObjectCount(1); 1.312 + objectSet = reinterpret_cast<TypeObjectKey**>(type.objectKey()); 1.313 + } 1.314 +} 1.315 + 1.316 +bool 1.317 +TypeSet::mightBeMIRType(jit::MIRType type) 1.318 +{ 1.319 + if (unknown()) 1.320 + return true; 1.321 + 1.322 + if (type == jit::MIRType_Object) 1.323 + return unknownObject() || baseObjectCount() != 0; 1.324 + 1.325 + switch (type) { 1.326 + case jit::MIRType_Undefined: 1.327 + return baseFlags() & TYPE_FLAG_UNDEFINED; 1.328 + case jit::MIRType_Null: 1.329 + return baseFlags() & TYPE_FLAG_NULL; 1.330 + case jit::MIRType_Boolean: 1.331 + return baseFlags() & TYPE_FLAG_BOOLEAN; 1.332 + case jit::MIRType_Int32: 1.333 + return baseFlags() & TYPE_FLAG_INT32; 1.334 + case jit::MIRType_Float32: // Fall through, there's no JSVAL for Float32. 1.335 + case jit::MIRType_Double: 1.336 + return baseFlags() & TYPE_FLAG_DOUBLE; 1.337 + case jit::MIRType_String: 1.338 + return baseFlags() & TYPE_FLAG_STRING; 1.339 + case jit::MIRType_MagicOptimizedArguments: 1.340 + return baseFlags() & TYPE_FLAG_LAZYARGS; 1.341 + case jit::MIRType_MagicHole: 1.342 + case jit::MIRType_MagicIsConstructing: 1.343 + // These magic constants do not escape to script and are not observed 1.344 + // in the type sets. 1.345 + // 1.346 + // The reason we can return false here is subtle: if Ion is asking the 1.347 + // type set if it has seen such a magic constant, then the MIR in 1.348 + // question is the most generic type, MIRType_Value. A magic constant 1.349 + // could only be emitted by a MIR of MIRType_Value if that MIR is a 1.350 + // phi, and we check that different magic constants do not flow to the 1.351 + // same join point in GuessPhiType. 1.352 + return false; 1.353 + default: 1.354 + MOZ_ASSUME_UNREACHABLE("Bad MIR type"); 1.355 + } 1.356 +} 1.357 + 1.358 +bool 1.359 +TypeSet::isSubset(TypeSet *other) 1.360 +{ 1.361 + if ((baseFlags() & other->baseFlags()) != baseFlags()) 1.362 + return false; 1.363 + 1.364 + if (unknownObject()) { 1.365 + JS_ASSERT(other->unknownObject()); 1.366 + } else { 1.367 + for (unsigned i = 0; i < getObjectCount(); i++) { 1.368 + TypeObjectKey *obj = getObject(i); 1.369 + if (!obj) 1.370 + continue; 1.371 + if (!other->hasType(Type::ObjectType(obj))) 1.372 + return false; 1.373 + } 1.374 + } 1.375 + 1.376 + return true; 1.377 +} 1.378 + 1.379 +bool 1.380 +TypeSet::enumerateTypes(TypeList *list) 1.381 +{ 1.382 + /* If any type is possible, there's no need to worry about specifics. */ 1.383 + if (flags & TYPE_FLAG_UNKNOWN) 1.384 + return list->append(Type::UnknownType()); 1.385 + 1.386 + /* Enqueue type set members stored as bits. */ 1.387 + for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) { 1.388 + if (flags & flag) { 1.389 + Type type = Type::PrimitiveType(TypeFlagPrimitive(flag)); 1.390 + if (!list->append(type)) 1.391 + return false; 1.392 + } 1.393 + } 1.394 + 1.395 + /* If any object is possible, skip specifics. */ 1.396 + if (flags & TYPE_FLAG_ANYOBJECT) 1.397 + return list->append(Type::AnyObjectType()); 1.398 + 1.399 + /* Enqueue specific object types. */ 1.400 + unsigned count = getObjectCount(); 1.401 + for (unsigned i = 0; i < count; i++) { 1.402 + TypeObjectKey *object = getObject(i); 1.403 + if (object) { 1.404 + if (!list->append(Type::ObjectType(object))) 1.405 + return false; 1.406 + } 1.407 + } 1.408 + 1.409 + return true; 1.410 +} 1.411 + 1.412 +inline bool 1.413 +TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint) 1.414 +{ 1.415 + /* 1.416 + * Build all types in the set into a vector before triggering the 1.417 + * constraint, as doing so may modify this type set. 1.418 + */ 1.419 + TypeList types; 1.420 + if (!enumerateTypes(&types)) 1.421 + return false; 1.422 + 1.423 + for (unsigned i = 0; i < types.length(); i++) 1.424 + constraint->newType(cx, this, types[i]); 1.425 + 1.426 + return true; 1.427 +} 1.428 + 1.429 +bool 1.430 +ConstraintTypeSet::addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting) 1.431 +{ 1.432 + if (!constraint) { 1.433 + /* OOM failure while constructing the constraint. */ 1.434 + return false; 1.435 + } 1.436 + 1.437 + JS_ASSERT(cx->compartment()->activeAnalysis); 1.438 + 1.439 + InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s", 1.440 + InferSpewColor(this), this, InferSpewColorReset(), 1.441 + InferSpewColor(constraint), constraint, InferSpewColorReset(), 1.442 + constraint->kind()); 1.443 + 1.444 + JS_ASSERT(constraint->next == nullptr); 1.445 + constraint->next = constraintList; 1.446 + constraintList = constraint; 1.447 + 1.448 + if (callExisting) 1.449 + return addTypesToConstraint(cx, constraint); 1.450 + return true; 1.451 +} 1.452 + 1.453 +void 1.454 +TypeSet::clearObjects() 1.455 +{ 1.456 + setBaseObjectCount(0); 1.457 + objectSet = nullptr; 1.458 +} 1.459 + 1.460 +void 1.461 +TypeSet::addType(Type type, LifoAlloc *alloc) 1.462 +{ 1.463 + if (unknown()) 1.464 + return; 1.465 + 1.466 + if (type.isUnknown()) { 1.467 + flags |= TYPE_FLAG_BASE_MASK; 1.468 + clearObjects(); 1.469 + JS_ASSERT(unknown()); 1.470 + return; 1.471 + } 1.472 + 1.473 + if (type.isPrimitive()) { 1.474 + TypeFlags flag = PrimitiveTypeFlag(type.primitive()); 1.475 + if (flags & flag) 1.476 + return; 1.477 + 1.478 + /* If we add float to a type set it is also considered to contain int. */ 1.479 + if (flag == TYPE_FLAG_DOUBLE) 1.480 + flag |= TYPE_FLAG_INT32; 1.481 + 1.482 + flags |= flag; 1.483 + return; 1.484 + } 1.485 + 1.486 + if (flags & TYPE_FLAG_ANYOBJECT) 1.487 + return; 1.488 + if (type.isAnyObject()) 1.489 + goto unknownObject; 1.490 + 1.491 + { 1.492 + uint32_t objectCount = baseObjectCount(); 1.493 + TypeObjectKey *object = type.objectKey(); 1.494 + TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey> 1.495 + (*alloc, objectSet, objectCount, object); 1.496 + if (!pentry) 1.497 + goto unknownObject; 1.498 + if (*pentry) 1.499 + return; 1.500 + *pentry = object; 1.501 + 1.502 + setBaseObjectCount(objectCount); 1.503 + 1.504 + if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT) 1.505 + goto unknownObject; 1.506 + } 1.507 + 1.508 + if (type.isTypeObject()) { 1.509 + TypeObject *nobject = type.typeObject(); 1.510 + JS_ASSERT(!nobject->singleton()); 1.511 + if (nobject->unknownProperties()) 1.512 + goto unknownObject; 1.513 + } 1.514 + 1.515 + if (false) { 1.516 + unknownObject: 1.517 + flags |= TYPE_FLAG_ANYOBJECT; 1.518 + clearObjects(); 1.519 + } 1.520 +} 1.521 + 1.522 +void 1.523 +ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type) 1.524 +{ 1.525 + JS_ASSERT(cxArg->compartment()->activeAnalysis); 1.526 + 1.527 + if (hasType(type)) 1.528 + return; 1.529 + 1.530 + TypeSet::addType(type, &cxArg->typeLifoAlloc()); 1.531 + 1.532 + if (type.isObjectUnchecked() && unknownObject()) 1.533 + type = Type::AnyObjectType(); 1.534 + 1.535 + InferSpew(ISpewOps, "addType: %sT%p%s %s", 1.536 + InferSpewColor(this), this, InferSpewColorReset(), 1.537 + TypeString(type)); 1.538 + 1.539 + /* Propagate the type to all constraints. */ 1.540 + if (JSContext *cx = cxArg->maybeJSContext()) { 1.541 + TypeConstraint *constraint = constraintList; 1.542 + while (constraint) { 1.543 + constraint->newType(cx, this, type); 1.544 + constraint = constraint->next; 1.545 + } 1.546 + } else { 1.547 + JS_ASSERT(!constraintList); 1.548 + } 1.549 +} 1.550 + 1.551 +void 1.552 +TypeSet::print() 1.553 +{ 1.554 + if (flags & TYPE_FLAG_NON_DATA_PROPERTY) 1.555 + fprintf(stderr, " [non-data]"); 1.556 + 1.557 + if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY) 1.558 + fprintf(stderr, " [non-writable]"); 1.559 + 1.560 + if (definiteProperty()) 1.561 + fprintf(stderr, " [definite:%d]", definiteSlot()); 1.562 + 1.563 + if (baseFlags() == 0 && !baseObjectCount()) { 1.564 + fprintf(stderr, " missing"); 1.565 + return; 1.566 + } 1.567 + 1.568 + if (flags & TYPE_FLAG_UNKNOWN) 1.569 + fprintf(stderr, " unknown"); 1.570 + if (flags & TYPE_FLAG_ANYOBJECT) 1.571 + fprintf(stderr, " object"); 1.572 + 1.573 + if (flags & TYPE_FLAG_UNDEFINED) 1.574 + fprintf(stderr, " void"); 1.575 + if (flags & TYPE_FLAG_NULL) 1.576 + fprintf(stderr, " null"); 1.577 + if (flags & TYPE_FLAG_BOOLEAN) 1.578 + fprintf(stderr, " bool"); 1.579 + if (flags & TYPE_FLAG_INT32) 1.580 + fprintf(stderr, " int"); 1.581 + if (flags & TYPE_FLAG_DOUBLE) 1.582 + fprintf(stderr, " float"); 1.583 + if (flags & TYPE_FLAG_STRING) 1.584 + fprintf(stderr, " string"); 1.585 + if (flags & TYPE_FLAG_LAZYARGS) 1.586 + fprintf(stderr, " lazyargs"); 1.587 + 1.588 + uint32_t objectCount = baseObjectCount(); 1.589 + if (objectCount) { 1.590 + fprintf(stderr, " object[%u]", objectCount); 1.591 + 1.592 + unsigned count = getObjectCount(); 1.593 + for (unsigned i = 0; i < count; i++) { 1.594 + TypeObjectKey *object = getObject(i); 1.595 + if (object) 1.596 + fprintf(stderr, " %s", TypeString(Type::ObjectType(object))); 1.597 + } 1.598 + } 1.599 +} 1.600 + 1.601 +bool 1.602 +TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const 1.603 +{ 1.604 + JS_ASSERT(result->empty()); 1.605 + 1.606 + unsigned objectCount = baseObjectCount(); 1.607 + unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0; 1.608 + 1.609 + TypeObjectKey **newSet; 1.610 + if (capacity) { 1.611 + newSet = alloc->newArray<TypeObjectKey*>(capacity); 1.612 + if (!newSet) 1.613 + return false; 1.614 + PodCopy(newSet, objectSet, capacity); 1.615 + } 1.616 + 1.617 + new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet); 1.618 + return true; 1.619 +} 1.620 + 1.621 +TemporaryTypeSet * 1.622 +TypeSet::clone(LifoAlloc *alloc) const 1.623 +{ 1.624 + TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>(); 1.625 + if (!res || !clone(alloc, res)) 1.626 + return nullptr; 1.627 + return res; 1.628 +} 1.629 + 1.630 +TemporaryTypeSet * 1.631 +TypeSet::filter(LifoAlloc *alloc, bool filterUndefined, bool filterNull) const 1.632 +{ 1.633 + TemporaryTypeSet *res = clone(alloc); 1.634 + if (!res) 1.635 + return nullptr; 1.636 + 1.637 + if (filterUndefined) 1.638 + res->flags = res->flags & ~TYPE_FLAG_UNDEFINED; 1.639 + 1.640 + if (filterNull) 1.641 + res->flags = res->flags & ~TYPE_FLAG_NULL; 1.642 + 1.643 + return res; 1.644 +} 1.645 + 1.646 +/* static */ TemporaryTypeSet * 1.647 +TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc) 1.648 +{ 1.649 + TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>(a->baseFlags() | b->baseFlags(), 1.650 + static_cast<TypeObjectKey**>(nullptr)); 1.651 + if (!res) 1.652 + return nullptr; 1.653 + 1.654 + if (!res->unknownObject()) { 1.655 + for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) { 1.656 + if (TypeObjectKey *key = a->getObject(i)) 1.657 + res->addType(Type::ObjectType(key), alloc); 1.658 + } 1.659 + for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) { 1.660 + if (TypeObjectKey *key = b->getObject(i)) 1.661 + res->addType(Type::ObjectType(key), alloc); 1.662 + } 1.663 + } 1.664 + 1.665 + return res; 1.666 +} 1.667 + 1.668 +///////////////////////////////////////////////////////////////////// 1.669 +// Compiler constraints 1.670 +///////////////////////////////////////////////////////////////////// 1.671 + 1.672 +// Compiler constraints overview 1.673 +// 1.674 +// Constraints generated during Ion compilation capture assumptions made about 1.675 +// heap properties that will trigger invalidation of the resulting Ion code if 1.676 +// the constraint is violated. Constraints can only be attached to type sets on 1.677 +// the main thread, so to allow compilation to occur almost entirely off thread 1.678 +// the generation is split into two phases. 1.679 +// 1.680 +// During compilation, CompilerConstraint values are constructed in a list, 1.681 +// recording the heap property type set which was read from and its expected 1.682 +// contents, along with the assumption made about those contents. 1.683 +// 1.684 +// At the end of compilation, when linking the result on the main thread, the 1.685 +// list of compiler constraints are read and converted to type constraints and 1.686 +// attached to the type sets. If the property type sets have changed so that the 1.687 +// assumptions no longer hold then the compilation is aborted and its result 1.688 +// discarded. 1.689 + 1.690 +// Superclass of all constraints generated during Ion compilation. These may 1.691 +// be allocated off the main thread, using the current Ion context's allocator. 1.692 +class CompilerConstraint 1.693 +{ 1.694 + public: 1.695 + // Property being queried by the compiler. 1.696 + HeapTypeSetKey property; 1.697 + 1.698 + // Contents of the property at the point when the query was performed. This 1.699 + // may differ from the actual property types later in compilation as the 1.700 + // main thread performs side effects. 1.701 + TemporaryTypeSet *expected; 1.702 + 1.703 + CompilerConstraint(LifoAlloc *alloc, const HeapTypeSetKey &property) 1.704 + : property(property), 1.705 + expected(property.maybeTypes() ? property.maybeTypes()->clone(alloc) : nullptr) 1.706 + {} 1.707 + 1.708 + // Generate the type constraint recording the assumption made by this 1.709 + // compilation. Returns true if the assumption originally made still holds. 1.710 + virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0; 1.711 +}; 1.712 + 1.713 +class types::CompilerConstraintList 1.714 +{ 1.715 + public: 1.716 + struct FrozenScript 1.717 + { 1.718 + JSScript *script; 1.719 + TemporaryTypeSet *thisTypes; 1.720 + TemporaryTypeSet *argTypes; 1.721 + TemporaryTypeSet *bytecodeTypes; 1.722 + }; 1.723 + 1.724 + private: 1.725 + 1.726 + // OOM during generation of some constraint. 1.727 + bool failed_; 1.728 + 1.729 +#ifdef JS_ION 1.730 + // Allocator used for constraints. 1.731 + LifoAlloc *alloc_; 1.732 + 1.733 + // Constraints generated on heap properties. 1.734 + Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints; 1.735 + 1.736 + // Scripts whose stack type sets were frozen for the compilation. 1.737 + Vector<FrozenScript, 1, jit::IonAllocPolicy> frozenScripts; 1.738 +#endif 1.739 + 1.740 + public: 1.741 + CompilerConstraintList(jit::TempAllocator &alloc) 1.742 + : failed_(false) 1.743 +#ifdef JS_ION 1.744 + , alloc_(alloc.lifoAlloc()) 1.745 + , constraints(alloc) 1.746 + , frozenScripts(alloc) 1.747 +#endif 1.748 + {} 1.749 + 1.750 + void add(CompilerConstraint *constraint) { 1.751 +#ifdef JS_ION 1.752 + if (!constraint || !constraints.append(constraint)) 1.753 + setFailed(); 1.754 +#else 1.755 + MOZ_CRASH(); 1.756 +#endif 1.757 + } 1.758 + 1.759 + void freezeScript(JSScript *script, 1.760 + TemporaryTypeSet *thisTypes, 1.761 + TemporaryTypeSet *argTypes, 1.762 + TemporaryTypeSet *bytecodeTypes) 1.763 + { 1.764 +#ifdef JS_ION 1.765 + FrozenScript entry; 1.766 + entry.script = script; 1.767 + entry.thisTypes = thisTypes; 1.768 + entry.argTypes = argTypes; 1.769 + entry.bytecodeTypes = bytecodeTypes; 1.770 + if (!frozenScripts.append(entry)) 1.771 + setFailed(); 1.772 +#else 1.773 + MOZ_CRASH(); 1.774 +#endif 1.775 + } 1.776 + 1.777 + size_t length() { 1.778 +#ifdef JS_ION 1.779 + return constraints.length(); 1.780 +#else 1.781 + MOZ_CRASH(); 1.782 +#endif 1.783 + } 1.784 + 1.785 + CompilerConstraint *get(size_t i) { 1.786 +#ifdef JS_ION 1.787 + return constraints[i]; 1.788 +#else 1.789 + MOZ_CRASH(); 1.790 +#endif 1.791 + } 1.792 + 1.793 + size_t numFrozenScripts() { 1.794 +#ifdef JS_ION 1.795 + return frozenScripts.length(); 1.796 +#else 1.797 + MOZ_CRASH(); 1.798 +#endif 1.799 + } 1.800 + 1.801 + const FrozenScript &frozenScript(size_t i) { 1.802 +#ifdef JS_ION 1.803 + return frozenScripts[i]; 1.804 +#else 1.805 + MOZ_CRASH(); 1.806 +#endif 1.807 + } 1.808 + 1.809 + bool failed() { 1.810 + return failed_; 1.811 + } 1.812 + void setFailed() { 1.813 + failed_ = true; 1.814 + } 1.815 + LifoAlloc *alloc() const { 1.816 +#ifdef JS_ION 1.817 + return alloc_; 1.818 +#else 1.819 + MOZ_CRASH(); 1.820 +#endif 1.821 + } 1.822 +}; 1.823 + 1.824 +CompilerConstraintList * 1.825 +types::NewCompilerConstraintList(jit::TempAllocator &alloc) 1.826 +{ 1.827 +#ifdef JS_ION 1.828 + return alloc.lifoAlloc()->new_<CompilerConstraintList>(alloc); 1.829 +#else 1.830 + MOZ_CRASH(); 1.831 +#endif 1.832 +} 1.833 + 1.834 +/* static */ bool 1.835 +TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script, 1.836 + TemporaryTypeSet **pThisTypes, 1.837 + TemporaryTypeSet **pArgTypes, 1.838 + TemporaryTypeSet **pBytecodeTypes) 1.839 +{ 1.840 + LifoAlloc *alloc = constraints->alloc(); 1.841 + StackTypeSet *existing = script->types->typeArray(); 1.842 + 1.843 + size_t count = NumTypeSets(script); 1.844 + TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count); 1.845 + if (!types) 1.846 + return false; 1.847 + PodZero(types, count); 1.848 + 1.849 + for (size_t i = 0; i < count; i++) { 1.850 + if (!existing[i].clone(alloc, &types[i])) 1.851 + return false; 1.852 + } 1.853 + 1.854 + *pThisTypes = types + (ThisTypes(script) - existing); 1.855 + *pArgTypes = (script->functionNonDelazifying() && script->functionNonDelazifying()->nargs()) 1.856 + ? (types + (ArgTypes(script, 0) - existing)) 1.857 + : nullptr; 1.858 + *pBytecodeTypes = types; 1.859 + 1.860 + constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes); 1.861 + return true; 1.862 +} 1.863 + 1.864 +namespace { 1.865 + 1.866 +template <typename T> 1.867 +class CompilerConstraintInstance : public CompilerConstraint 1.868 +{ 1.869 + T data; 1.870 + 1.871 + public: 1.872 + CompilerConstraintInstance<T>(LifoAlloc *alloc, const HeapTypeSetKey &property, const T &data) 1.873 + : CompilerConstraint(alloc, property), data(data) 1.874 + {} 1.875 + 1.876 + bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo); 1.877 +}; 1.878 + 1.879 +// Constraint generated from a CompilerConstraint when linking the compilation. 1.880 +template <typename T> 1.881 +class TypeCompilerConstraint : public TypeConstraint 1.882 +{ 1.883 + // Compilation which this constraint may invalidate. 1.884 + RecompileInfo compilation; 1.885 + 1.886 + T data; 1.887 + 1.888 + public: 1.889 + TypeCompilerConstraint<T>(RecompileInfo compilation, const T &data) 1.890 + : compilation(compilation), data(data) 1.891 + {} 1.892 + 1.893 + const char *kind() { return data.kind(); } 1.894 + 1.895 + void newType(JSContext *cx, TypeSet *source, Type type) { 1.896 + if (data.invalidateOnNewType(type)) 1.897 + cx->zone()->types.addPendingRecompile(cx, compilation); 1.898 + } 1.899 + 1.900 + void newPropertyState(JSContext *cx, TypeSet *source) { 1.901 + if (data.invalidateOnNewPropertyState(source)) 1.902 + cx->zone()->types.addPendingRecompile(cx, compilation); 1.903 + } 1.904 + 1.905 + void newObjectState(JSContext *cx, TypeObject *object) { 1.906 + // Note: Once the object has unknown properties, no more notifications 1.907 + // will be sent on changes to its state, so always invalidate any 1.908 + // associated compilations. 1.909 + if (object->unknownProperties() || data.invalidateOnNewObjectState(object)) 1.910 + cx->zone()->types.addPendingRecompile(cx, compilation); 1.911 + } 1.912 + 1.913 + bool sweep(TypeZone &zone, TypeConstraint **res) { 1.914 + if (data.shouldSweep() || compilation.shouldSweep(zone)) 1.915 + return false; 1.916 + *res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data); 1.917 + return true; 1.918 + } 1.919 +}; 1.920 + 1.921 +template <typename T> 1.922 +bool 1.923 +CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) 1.924 +{ 1.925 + if (property.object()->unknownProperties()) 1.926 + return false; 1.927 + 1.928 + if (!property.instantiate(cx)) 1.929 + return false; 1.930 + 1.931 + if (!data.constraintHolds(cx, property, expected)) 1.932 + return false; 1.933 + 1.934 + return property.maybeTypes()->addConstraint(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data), 1.935 + /* callExisting = */ false); 1.936 +} 1.937 + 1.938 +} /* anonymous namespace */ 1.939 + 1.940 +const Class * 1.941 +TypeObjectKey::clasp() 1.942 +{ 1.943 + return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass(); 1.944 +} 1.945 + 1.946 +TaggedProto 1.947 +TypeObjectKey::proto() 1.948 +{ 1.949 + JS_ASSERT(hasTenuredProto()); 1.950 + return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto(); 1.951 +} 1.952 + 1.953 +bool 1.954 +ObjectImpl::hasTenuredProto() const 1.955 +{ 1.956 + return type_->hasTenuredProto(); 1.957 +} 1.958 + 1.959 +bool 1.960 +TypeObjectKey::hasTenuredProto() 1.961 +{ 1.962 + return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto(); 1.963 +} 1.964 + 1.965 +JSObject * 1.966 +TypeObjectKey::singleton() 1.967 +{ 1.968 + return isTypeObject() ? asTypeObject()->singleton() : asSingleObject(); 1.969 +} 1.970 + 1.971 +TypeNewScript * 1.972 +TypeObjectKey::newScript() 1.973 +{ 1.974 + if (isTypeObject() && asTypeObject()->hasNewScript()) 1.975 + return asTypeObject()->newScript(); 1.976 + return nullptr; 1.977 +} 1.978 + 1.979 +TypeObject * 1.980 +TypeObjectKey::maybeType() 1.981 +{ 1.982 + if (isTypeObject()) 1.983 + return asTypeObject(); 1.984 + if (asSingleObject()->hasLazyType()) 1.985 + return nullptr; 1.986 + return asSingleObject()->type(); 1.987 +} 1.988 + 1.989 +bool 1.990 +TypeObjectKey::unknownProperties() 1.991 +{ 1.992 + if (TypeObject *type = maybeType()) 1.993 + return type->unknownProperties(); 1.994 + return false; 1.995 +} 1.996 + 1.997 +HeapTypeSetKey 1.998 +TypeObjectKey::property(jsid id) 1.999 +{ 1.1000 + JS_ASSERT(!unknownProperties()); 1.1001 + 1.1002 + HeapTypeSetKey property; 1.1003 + property.object_ = this; 1.1004 + property.id_ = id; 1.1005 + if (TypeObject *type = maybeType()) 1.1006 + property.maybeTypes_ = type->maybeGetProperty(id); 1.1007 + 1.1008 + return property; 1.1009 +} 1.1010 + 1.1011 +void 1.1012 +TypeObjectKey::ensureTrackedProperty(JSContext *cx, jsid id) 1.1013 +{ 1.1014 +#ifdef JS_ION 1.1015 + // If we are accessing a lazily defined property which actually exists in 1.1016 + // the VM and has not been instantiated yet, instantiate it now if we are 1.1017 + // on the main thread and able to do so. 1.1018 + if (!JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) { 1.1019 + JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); 1.1020 + if (JSObject *obj = singleton()) { 1.1021 + if (obj->isNative() && obj->nativeLookupPure(id)) 1.1022 + EnsureTrackPropertyTypes(cx, obj, id); 1.1023 + } 1.1024 + } 1.1025 +#endif // JS_ION 1.1026 +} 1.1027 + 1.1028 +bool 1.1029 +HeapTypeSetKey::instantiate(JSContext *cx) 1.1030 +{ 1.1031 + if (maybeTypes()) 1.1032 + return true; 1.1033 + if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx)) { 1.1034 + cx->clearPendingException(); 1.1035 + return false; 1.1036 + } 1.1037 + maybeTypes_ = object()->maybeType()->getProperty(cx, id()); 1.1038 + return maybeTypes_ != nullptr; 1.1039 +} 1.1040 + 1.1041 +static bool 1.1042 +CheckFrozenTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual) 1.1043 +{ 1.1044 + // Return whether the types frozen for a script during compilation are 1.1045 + // still valid. Also check for any new types added to the frozen set during 1.1046 + // compilation, and add them to the actual stack type sets. These new types 1.1047 + // indicate places where the compiler relaxed its possible inputs to be 1.1048 + // more tolerant of potential new types. 1.1049 + 1.1050 + if (!actual->isSubset(frozen)) 1.1051 + return false; 1.1052 + 1.1053 + if (!frozen->isSubset(actual)) { 1.1054 + TypeSet::TypeList list; 1.1055 + frozen->enumerateTypes(&list); 1.1056 + 1.1057 + for (size_t i = 0; i < list.length(); i++) 1.1058 + actual->addType(cx, list[i]); 1.1059 + } 1.1060 + 1.1061 + return true; 1.1062 +} 1.1063 + 1.1064 +namespace { 1.1065 + 1.1066 +/* 1.1067 + * As for TypeConstraintFreeze, but describes an implicit freeze constraint 1.1068 + * added for stack types within a script. Applies to all compilations of the 1.1069 + * script, not just a single one. 1.1070 + */ 1.1071 +class TypeConstraintFreezeStack : public TypeConstraint 1.1072 +{ 1.1073 + JSScript *script_; 1.1074 + 1.1075 + public: 1.1076 + TypeConstraintFreezeStack(JSScript *script) 1.1077 + : script_(script) 1.1078 + {} 1.1079 + 1.1080 + const char *kind() { return "freezeStack"; } 1.1081 + 1.1082 + void newType(JSContext *cx, TypeSet *source, Type type) { 1.1083 + /* 1.1084 + * Unlike TypeConstraintFreeze, triggering this constraint once does 1.1085 + * not disable it on future changes to the type set. 1.1086 + */ 1.1087 + cx->zone()->types.addPendingRecompile(cx, script_); 1.1088 + } 1.1089 + 1.1090 + bool sweep(TypeZone &zone, TypeConstraint **res) { 1.1091 + if (IsScriptAboutToBeFinalized(&script_)) 1.1092 + return false; 1.1093 + *res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_); 1.1094 + return true; 1.1095 + } 1.1096 +}; 1.1097 + 1.1098 +} /* anonymous namespace */ 1.1099 + 1.1100 +bool 1.1101 +types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode executionMode, 1.1102 + CompilerConstraintList *constraints, RecompileInfo *precompileInfo) 1.1103 +{ 1.1104 + if (constraints->failed()) 1.1105 + return false; 1.1106 + 1.1107 + CompilerOutput co(script, executionMode); 1.1108 + 1.1109 + TypeZone &types = cx->zone()->types; 1.1110 + if (!types.compilerOutputs) { 1.1111 + types.compilerOutputs = cx->new_< Vector<CompilerOutput> >(cx); 1.1112 + if (!types.compilerOutputs) 1.1113 + return false; 1.1114 + } 1.1115 + 1.1116 +#ifdef DEBUG 1.1117 + for (size_t i = 0; i < types.compilerOutputs->length(); i++) { 1.1118 + const CompilerOutput &co = (*types.compilerOutputs)[i]; 1.1119 + JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode); 1.1120 + } 1.1121 +#endif 1.1122 + 1.1123 + uint32_t index = types.compilerOutputs->length(); 1.1124 + if (!types.compilerOutputs->append(co)) 1.1125 + return false; 1.1126 + 1.1127 + *precompileInfo = RecompileInfo(index); 1.1128 + 1.1129 + bool succeeded = true; 1.1130 + 1.1131 + for (size_t i = 0; i < constraints->length(); i++) { 1.1132 + CompilerConstraint *constraint = constraints->get(i); 1.1133 + if (!constraint->generateTypeConstraint(cx, *precompileInfo)) 1.1134 + succeeded = false; 1.1135 + } 1.1136 + 1.1137 + for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { 1.1138 + const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i); 1.1139 + JS_ASSERT(entry.script->types); 1.1140 + 1.1141 + if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script))) 1.1142 + succeeded = false; 1.1143 + unsigned nargs = entry.script->functionNonDelazifying() 1.1144 + ? entry.script->functionNonDelazifying()->nargs() 1.1145 + : 0; 1.1146 + for (size_t i = 0; i < nargs; i++) { 1.1147 + if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], types::TypeScript::ArgTypes(entry.script, i))) 1.1148 + succeeded = false; 1.1149 + } 1.1150 + for (size_t i = 0; i < entry.script->nTypeSets(); i++) { 1.1151 + if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types->typeArray()[i])) 1.1152 + succeeded = false; 1.1153 + } 1.1154 + 1.1155 + // If necessary, add constraints to trigger invalidation on the script 1.1156 + // after any future changes to the stack type sets. 1.1157 + if (entry.script->hasFreezeConstraints()) 1.1158 + continue; 1.1159 + entry.script->setHasFreezeConstraints(); 1.1160 + 1.1161 + size_t count = TypeScript::NumTypeSets(entry.script); 1.1162 + 1.1163 + StackTypeSet *array = entry.script->types->typeArray(); 1.1164 + for (size_t i = 0; i < count; i++) { 1.1165 + if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false)) 1.1166 + succeeded = false; 1.1167 + } 1.1168 + } 1.1169 + 1.1170 + if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) { 1.1171 + types.compilerOutputs->back().invalidate(); 1.1172 + script->resetUseCount(); 1.1173 + return false; 1.1174 + } 1.1175 + 1.1176 + return true; 1.1177 +} 1.1178 + 1.1179 +static void 1.1180 +CheckDefinitePropertiesTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual) 1.1181 +{ 1.1182 + // The definite properties analysis happens on the main thread, so no new 1.1183 + // types can have been added to actual. The analysis may have updated the 1.1184 + // contents of |frozen| though with new speculative types, and these need 1.1185 + // to be reflected in |actual| for AddClearDefiniteFunctionUsesInScript 1.1186 + // to work. 1.1187 + if (!frozen->isSubset(actual)) { 1.1188 + TypeSet::TypeList list; 1.1189 + frozen->enumerateTypes(&list); 1.1190 + 1.1191 + for (size_t i = 0; i < list.length(); i++) 1.1192 + actual->addType(cx, list[i]); 1.1193 + } 1.1194 +} 1.1195 + 1.1196 +void 1.1197 +types::FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *constraints) 1.1198 +{ 1.1199 +#ifdef DEBUG 1.1200 + // Assert no new types have been added to the StackTypeSets. Do this before 1.1201 + // calling CheckDefinitePropertiesTypeSet, as it may add new types to the 1.1202 + // StackTypeSets and break these invariants if a script is inlined more 1.1203 + // than once. See also CheckDefinitePropertiesTypeSet. 1.1204 + for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { 1.1205 + const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i); 1.1206 + JSScript *script = entry.script; 1.1207 + JS_ASSERT(script->types); 1.1208 + 1.1209 + JS_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes)); 1.1210 + 1.1211 + unsigned nargs = entry.script->functionNonDelazifying() 1.1212 + ? entry.script->functionNonDelazifying()->nargs() 1.1213 + : 0; 1.1214 + for (size_t j = 0; j < nargs; j++) 1.1215 + JS_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j])); 1.1216 + 1.1217 + for (size_t j = 0; j < script->nTypeSets(); j++) 1.1218 + JS_ASSERT(script->types->typeArray()[j].isSubset(&entry.bytecodeTypes[j])); 1.1219 + } 1.1220 +#endif 1.1221 + 1.1222 + for (size_t i = 0; i < constraints->numFrozenScripts(); i++) { 1.1223 + const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i); 1.1224 + JSScript *script = entry.script; 1.1225 + JS_ASSERT(script->types); 1.1226 + 1.1227 + if (!script->types) 1.1228 + MOZ_CRASH(); 1.1229 + 1.1230 + CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script)); 1.1231 + 1.1232 + unsigned nargs = script->functionNonDelazifying() 1.1233 + ? script->functionNonDelazifying()->nargs() 1.1234 + : 0; 1.1235 + for (size_t j = 0; j < nargs; j++) 1.1236 + CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j)); 1.1237 + 1.1238 + for (size_t j = 0; j < script->nTypeSets(); j++) 1.1239 + CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types->typeArray()[j]); 1.1240 + } 1.1241 +} 1.1242 + 1.1243 +namespace { 1.1244 + 1.1245 +// Constraint which triggers recompilation of a script if any type is added to a type set. */ 1.1246 +class ConstraintDataFreeze 1.1247 +{ 1.1248 + public: 1.1249 + ConstraintDataFreeze() {} 1.1250 + 1.1251 + const char *kind() { return "freeze"; } 1.1252 + 1.1253 + bool invalidateOnNewType(Type type) { return true; } 1.1254 + bool invalidateOnNewPropertyState(TypeSet *property) { return true; } 1.1255 + bool invalidateOnNewObjectState(TypeObject *object) { return false; } 1.1256 + 1.1257 + bool constraintHolds(JSContext *cx, 1.1258 + const HeapTypeSetKey &property, TemporaryTypeSet *expected) 1.1259 + { 1.1260 + return expected 1.1261 + ? property.maybeTypes()->isSubset(expected) 1.1262 + : property.maybeTypes()->empty(); 1.1263 + } 1.1264 + 1.1265 + bool shouldSweep() { return false; } 1.1266 +}; 1.1267 + 1.1268 +} /* anonymous namespace */ 1.1269 + 1.1270 +void 1.1271 +HeapTypeSetKey::freeze(CompilerConstraintList *constraints) 1.1272 +{ 1.1273 + LifoAlloc *alloc = constraints->alloc(); 1.1274 + 1.1275 + typedef CompilerConstraintInstance<ConstraintDataFreeze> T; 1.1276 + constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataFreeze())); 1.1277 +} 1.1278 + 1.1279 +static inline jit::MIRType 1.1280 +GetMIRTypeFromTypeFlags(TypeFlags flags) 1.1281 +{ 1.1282 + switch (flags) { 1.1283 + case TYPE_FLAG_UNDEFINED: 1.1284 + return jit::MIRType_Undefined; 1.1285 + case TYPE_FLAG_NULL: 1.1286 + return jit::MIRType_Null; 1.1287 + case TYPE_FLAG_BOOLEAN: 1.1288 + return jit::MIRType_Boolean; 1.1289 + case TYPE_FLAG_INT32: 1.1290 + return jit::MIRType_Int32; 1.1291 + case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE): 1.1292 + return jit::MIRType_Double; 1.1293 + case TYPE_FLAG_STRING: 1.1294 + return jit::MIRType_String; 1.1295 + case TYPE_FLAG_LAZYARGS: 1.1296 + return jit::MIRType_MagicOptimizedArguments; 1.1297 + case TYPE_FLAG_ANYOBJECT: 1.1298 + return jit::MIRType_Object; 1.1299 + default: 1.1300 + return jit::MIRType_Value; 1.1301 + } 1.1302 +} 1.1303 + 1.1304 +jit::MIRType 1.1305 +TemporaryTypeSet::getKnownMIRType() 1.1306 +{ 1.1307 + TypeFlags flags = baseFlags(); 1.1308 + jit::MIRType type; 1.1309 + 1.1310 + if (baseObjectCount()) 1.1311 + type = flags ? jit::MIRType_Value : jit::MIRType_Object; 1.1312 + else 1.1313 + type = GetMIRTypeFromTypeFlags(flags); 1.1314 + 1.1315 + /* 1.1316 + * If the type set is totally empty then it will be treated as unknown, 1.1317 + * but we still need to record the dependency as adding a new type can give 1.1318 + * it a definite type tag. This is not needed if there are enough types 1.1319 + * that the exact tag is unknown, as it will stay unknown as more types are 1.1320 + * added to the set. 1.1321 + */ 1.1322 + DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0; 1.1323 + JS_ASSERT_IF(empty, type == jit::MIRType_Value); 1.1324 + 1.1325 + return type; 1.1326 +} 1.1327 + 1.1328 +jit::MIRType 1.1329 +HeapTypeSetKey::knownMIRType(CompilerConstraintList *constraints) 1.1330 +{ 1.1331 + TypeSet *types = maybeTypes(); 1.1332 + 1.1333 + if (!types || types->unknown()) 1.1334 + return jit::MIRType_Value; 1.1335 + 1.1336 + TypeFlags flags = types->baseFlags() & ~TYPE_FLAG_ANYOBJECT; 1.1337 + jit::MIRType type; 1.1338 + 1.1339 + if (types->unknownObject() || types->getObjectCount()) 1.1340 + type = flags ? jit::MIRType_Value : jit::MIRType_Object; 1.1341 + else 1.1342 + type = GetMIRTypeFromTypeFlags(flags); 1.1343 + 1.1344 + if (type != jit::MIRType_Value) 1.1345 + freeze(constraints); 1.1346 + 1.1347 + /* 1.1348 + * If the type set is totally empty then it will be treated as unknown, 1.1349 + * but we still need to record the dependency as adding a new type can give 1.1350 + * it a definite type tag. This is not needed if there are enough types 1.1351 + * that the exact tag is unknown, as it will stay unknown as more types are 1.1352 + * added to the set. 1.1353 + */ 1.1354 + JS_ASSERT_IF(types->empty(), type == jit::MIRType_Value); 1.1355 + 1.1356 + return type; 1.1357 +} 1.1358 + 1.1359 +bool 1.1360 +HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints) 1.1361 +{ 1.1362 + if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty())) 1.1363 + return true; 1.1364 + if (JSObject *obj = object()->singleton()) { 1.1365 + if (CanHaveEmptyPropertyTypesForOwnProperty(obj)) 1.1366 + return true; 1.1367 + } 1.1368 + freeze(constraints); 1.1369 + return false; 1.1370 +} 1.1371 + 1.1372 +bool 1.1373 +HeapTypeSetKey::knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other) 1.1374 +{ 1.1375 + if (!maybeTypes() || maybeTypes()->empty()) { 1.1376 + freeze(constraints); 1.1377 + return true; 1.1378 + } 1.1379 + if (!other.maybeTypes() || !maybeTypes()->isSubset(other.maybeTypes())) 1.1380 + return false; 1.1381 + freeze(constraints); 1.1382 + return true; 1.1383 +} 1.1384 + 1.1385 +JSObject * 1.1386 +TemporaryTypeSet::getSingleton() 1.1387 +{ 1.1388 + if (baseFlags() != 0 || baseObjectCount() != 1) 1.1389 + return nullptr; 1.1390 + 1.1391 + return getSingleObject(0); 1.1392 +} 1.1393 + 1.1394 +JSObject * 1.1395 +HeapTypeSetKey::singleton(CompilerConstraintList *constraints) 1.1396 +{ 1.1397 + HeapTypeSet *types = maybeTypes(); 1.1398 + 1.1399 + if (!types || types->nonDataProperty() || types->baseFlags() != 0 || types->getObjectCount() != 1) 1.1400 + return nullptr; 1.1401 + 1.1402 + JSObject *obj = types->getSingleObject(0); 1.1403 + 1.1404 + if (obj) 1.1405 + freeze(constraints); 1.1406 + 1.1407 + return obj; 1.1408 +} 1.1409 + 1.1410 +bool 1.1411 +HeapTypeSetKey::needsBarrier(CompilerConstraintList *constraints) 1.1412 +{ 1.1413 + TypeSet *types = maybeTypes(); 1.1414 + if (!types) 1.1415 + return false; 1.1416 + bool result = types->unknownObject() 1.1417 + || types->getObjectCount() > 0 1.1418 + || types->hasAnyFlag(TYPE_FLAG_STRING); 1.1419 + if (!result) 1.1420 + freeze(constraints); 1.1421 + return result; 1.1422 +} 1.1423 + 1.1424 +namespace { 1.1425 + 1.1426 +// Constraint which triggers recompilation if an object acquires particular flags. 1.1427 +class ConstraintDataFreezeObjectFlags 1.1428 +{ 1.1429 + public: 1.1430 + // Flags we are watching for on this object. 1.1431 + TypeObjectFlags flags; 1.1432 + 1.1433 + ConstraintDataFreezeObjectFlags(TypeObjectFlags flags) 1.1434 + : flags(flags) 1.1435 + { 1.1436 + JS_ASSERT(flags); 1.1437 + } 1.1438 + 1.1439 + const char *kind() { return "freezeObjectFlags"; } 1.1440 + 1.1441 + bool invalidateOnNewType(Type type) { return false; } 1.1442 + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } 1.1443 + bool invalidateOnNewObjectState(TypeObject *object) { 1.1444 + return object->hasAnyFlags(flags); 1.1445 + } 1.1446 + 1.1447 + bool constraintHolds(JSContext *cx, 1.1448 + const HeapTypeSetKey &property, TemporaryTypeSet *expected) 1.1449 + { 1.1450 + return !invalidateOnNewObjectState(property.object()->maybeType()); 1.1451 + } 1.1452 + 1.1453 + bool shouldSweep() { return false; } 1.1454 +}; 1.1455 + 1.1456 +} /* anonymous namespace */ 1.1457 + 1.1458 +bool 1.1459 +TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags) 1.1460 +{ 1.1461 + JS_ASSERT(flags); 1.1462 + 1.1463 + if (TypeObject *type = maybeType()) { 1.1464 + if (type->hasAnyFlags(flags)) 1.1465 + return true; 1.1466 + } 1.1467 + 1.1468 + HeapTypeSetKey objectProperty = property(JSID_EMPTY); 1.1469 + LifoAlloc *alloc = constraints->alloc(); 1.1470 + 1.1471 + typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T; 1.1472 + constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(flags))); 1.1473 + return false; 1.1474 +} 1.1475 + 1.1476 +bool 1.1477 +TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags) 1.1478 +{ 1.1479 + if (unknownObject()) 1.1480 + return true; 1.1481 + 1.1482 + /* 1.1483 + * Treat type sets containing no objects as having all object flags, 1.1484 + * to spare callers from having to check this. 1.1485 + */ 1.1486 + if (baseObjectCount() == 0) 1.1487 + return true; 1.1488 + 1.1489 + unsigned count = getObjectCount(); 1.1490 + for (unsigned i = 0; i < count; i++) { 1.1491 + TypeObjectKey *object = getObject(i); 1.1492 + if (object && object->hasFlags(constraints, flags)) 1.1493 + return true; 1.1494 + } 1.1495 + 1.1496 + return false; 1.1497 +} 1.1498 + 1.1499 +gc::InitialHeap 1.1500 +TypeObject::initialHeap(CompilerConstraintList *constraints) 1.1501 +{ 1.1502 + // If this object is not required to be pretenured but could be in the 1.1503 + // future, add a constraint to trigger recompilation if the requirement 1.1504 + // changes. 1.1505 + 1.1506 + if (shouldPreTenure()) 1.1507 + return gc::TenuredHeap; 1.1508 + 1.1509 + if (!canPreTenure()) 1.1510 + return gc::DefaultHeap; 1.1511 + 1.1512 + HeapTypeSetKey objectProperty = TypeObjectKey::get(this)->property(JSID_EMPTY); 1.1513 + LifoAlloc *alloc = constraints->alloc(); 1.1514 + 1.1515 + typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T; 1.1516 + constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(OBJECT_FLAG_PRE_TENURE))); 1.1517 + 1.1518 + return gc::DefaultHeap; 1.1519 +} 1.1520 + 1.1521 +namespace { 1.1522 + 1.1523 +// Constraint which triggers recompilation on any type change in an inlined 1.1524 +// script. The freeze constraints added to stack type sets will only directly 1.1525 +// invalidate the script containing those stack type sets. To invalidate code 1.1526 +// for scripts into which the base script was inlined, ObjectStateChange is used. 1.1527 +class ConstraintDataFreezeObjectForInlinedCall 1.1528 +{ 1.1529 + public: 1.1530 + ConstraintDataFreezeObjectForInlinedCall() 1.1531 + {} 1.1532 + 1.1533 + const char *kind() { return "freezeObjectForInlinedCall"; } 1.1534 + 1.1535 + bool invalidateOnNewType(Type type) { return false; } 1.1536 + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } 1.1537 + bool invalidateOnNewObjectState(TypeObject *object) { 1.1538 + // We don't keep track of the exact dependencies the caller has on its 1.1539 + // inlined scripts' type sets, so always invalidate the caller. 1.1540 + return true; 1.1541 + } 1.1542 + 1.1543 + bool constraintHolds(JSContext *cx, 1.1544 + const HeapTypeSetKey &property, TemporaryTypeSet *expected) 1.1545 + { 1.1546 + return true; 1.1547 + } 1.1548 + 1.1549 + bool shouldSweep() { return false; } 1.1550 +}; 1.1551 + 1.1552 +// Constraint which triggers recompilation when the template object for a 1.1553 +// type's new script changes. 1.1554 +class ConstraintDataFreezeObjectForNewScriptTemplate 1.1555 +{ 1.1556 + JSObject *templateObject; 1.1557 + 1.1558 + public: 1.1559 + ConstraintDataFreezeObjectForNewScriptTemplate(JSObject *templateObject) 1.1560 + : templateObject(templateObject) 1.1561 + {} 1.1562 + 1.1563 + const char *kind() { return "freezeObjectForNewScriptTemplate"; } 1.1564 + 1.1565 + bool invalidateOnNewType(Type type) { return false; } 1.1566 + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } 1.1567 + bool invalidateOnNewObjectState(TypeObject *object) { 1.1568 + return !object->hasNewScript() || object->newScript()->templateObject != templateObject; 1.1569 + } 1.1570 + 1.1571 + bool constraintHolds(JSContext *cx, 1.1572 + const HeapTypeSetKey &property, TemporaryTypeSet *expected) 1.1573 + { 1.1574 + return !invalidateOnNewObjectState(property.object()->maybeType()); 1.1575 + } 1.1576 + 1.1577 + bool shouldSweep() { 1.1578 + // Note: |templateObject| is only used for equality testing. 1.1579 + return false; 1.1580 + } 1.1581 +}; 1.1582 + 1.1583 +// Constraint which triggers recompilation when a typed array's data becomes 1.1584 +// invalid. 1.1585 +class ConstraintDataFreezeObjectForTypedArrayData 1.1586 +{ 1.1587 + void *viewData; 1.1588 + uint32_t length; 1.1589 + 1.1590 + public: 1.1591 + ConstraintDataFreezeObjectForTypedArrayData(TypedArrayObject &tarray) 1.1592 + : viewData(tarray.viewData()), 1.1593 + length(tarray.length()) 1.1594 + {} 1.1595 + 1.1596 + const char *kind() { return "freezeObjectForTypedArrayData"; } 1.1597 + 1.1598 + bool invalidateOnNewType(Type type) { return false; } 1.1599 + bool invalidateOnNewPropertyState(TypeSet *property) { return false; } 1.1600 + bool invalidateOnNewObjectState(TypeObject *object) { 1.1601 + TypedArrayObject &tarray = object->singleton()->as<TypedArrayObject>(); 1.1602 + return tarray.viewData() != viewData || tarray.length() != length; 1.1603 + } 1.1604 + 1.1605 + bool constraintHolds(JSContext *cx, 1.1606 + const HeapTypeSetKey &property, TemporaryTypeSet *expected) 1.1607 + { 1.1608 + return !invalidateOnNewObjectState(property.object()->maybeType()); 1.1609 + } 1.1610 + 1.1611 + bool shouldSweep() { 1.1612 + // Note: |viewData| is only used for equality testing. 1.1613 + return false; 1.1614 + } 1.1615 +}; 1.1616 + 1.1617 +} /* anonymous namespace */ 1.1618 + 1.1619 +void 1.1620 +TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints) 1.1621 +{ 1.1622 + HeapTypeSetKey objectProperty = property(JSID_EMPTY); 1.1623 + LifoAlloc *alloc = constraints->alloc(); 1.1624 + 1.1625 + typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForInlinedCall> T; 1.1626 + constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectForInlinedCall())); 1.1627 +} 1.1628 + 1.1629 +void 1.1630 +TypeObjectKey::watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints) 1.1631 +{ 1.1632 + JSObject *templateObject = asTypeObject()->newScript()->templateObject; 1.1633 + HeapTypeSetKey objectProperty = property(JSID_EMPTY); 1.1634 + LifoAlloc *alloc = constraints->alloc(); 1.1635 + 1.1636 + typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForNewScriptTemplate> T; 1.1637 + constraints->add(alloc->new_<T>(alloc, objectProperty, 1.1638 + ConstraintDataFreezeObjectForNewScriptTemplate(templateObject))); 1.1639 +} 1.1640 + 1.1641 +void 1.1642 +TypeObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList *constraints) 1.1643 +{ 1.1644 + TypedArrayObject &tarray = asSingleObject()->as<TypedArrayObject>(); 1.1645 + HeapTypeSetKey objectProperty = property(JSID_EMPTY); 1.1646 + LifoAlloc *alloc = constraints->alloc(); 1.1647 + 1.1648 + typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForTypedArrayData> T; 1.1649 + constraints->add(alloc->new_<T>(alloc, objectProperty, 1.1650 + ConstraintDataFreezeObjectForTypedArrayData(tarray))); 1.1651 +} 1.1652 + 1.1653 +static void 1.1654 +ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown) 1.1655 +{ 1.1656 + if (object->unknownProperties()) 1.1657 + return; 1.1658 + 1.1659 + /* All constraints listening to state changes are on the empty id. */ 1.1660 + HeapTypeSet *types = object->maybeGetProperty(JSID_EMPTY); 1.1661 + 1.1662 + /* Mark as unknown after getting the types, to avoid assertion. */ 1.1663 + if (markingUnknown) 1.1664 + object->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); 1.1665 + 1.1666 + if (types) { 1.1667 + if (JSContext *cx = cxArg->maybeJSContext()) { 1.1668 + TypeConstraint *constraint = types->constraintList; 1.1669 + while (constraint) { 1.1670 + constraint->newObjectState(cx, object); 1.1671 + constraint = constraint->next; 1.1672 + } 1.1673 + } else { 1.1674 + JS_ASSERT(!types->constraintList); 1.1675 + } 1.1676 + } 1.1677 +} 1.1678 + 1.1679 +static void 1.1680 +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun); 1.1681 + 1.1682 +namespace { 1.1683 + 1.1684 +class ConstraintDataFreezePropertyState 1.1685 +{ 1.1686 + public: 1.1687 + enum Which { 1.1688 + NON_DATA, 1.1689 + NON_WRITABLE 1.1690 + } which; 1.1691 + 1.1692 + ConstraintDataFreezePropertyState(Which which) 1.1693 + : which(which) 1.1694 + {} 1.1695 + 1.1696 + const char *kind() { return (which == NON_DATA) ? "freezeNonDataProperty" : "freezeNonWritableProperty"; } 1.1697 + 1.1698 + bool invalidateOnNewType(Type type) { return false; } 1.1699 + bool invalidateOnNewPropertyState(TypeSet *property) { 1.1700 + return (which == NON_DATA) 1.1701 + ? property->nonDataProperty() 1.1702 + : property->nonWritableProperty(); 1.1703 + } 1.1704 + bool invalidateOnNewObjectState(TypeObject *object) { return false; } 1.1705 + 1.1706 + bool constraintHolds(JSContext *cx, 1.1707 + const HeapTypeSetKey &property, TemporaryTypeSet *expected) 1.1708 + { 1.1709 + return !invalidateOnNewPropertyState(property.maybeTypes()); 1.1710 + } 1.1711 + 1.1712 + bool shouldSweep() { return false; } 1.1713 +}; 1.1714 + 1.1715 +} /* anonymous namespace */ 1.1716 + 1.1717 +bool 1.1718 +HeapTypeSetKey::nonData(CompilerConstraintList *constraints) 1.1719 +{ 1.1720 + if (maybeTypes() && maybeTypes()->nonDataProperty()) 1.1721 + return true; 1.1722 + 1.1723 + LifoAlloc *alloc = constraints->alloc(); 1.1724 + 1.1725 + typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T; 1.1726 + constraints->add(alloc->new_<T>(alloc, *this, 1.1727 + ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_DATA))); 1.1728 + return false; 1.1729 +} 1.1730 + 1.1731 +bool 1.1732 +HeapTypeSetKey::nonWritable(CompilerConstraintList *constraints) 1.1733 +{ 1.1734 + if (maybeTypes() && maybeTypes()->nonWritableProperty()) 1.1735 + return true; 1.1736 + 1.1737 + LifoAlloc *alloc = constraints->alloc(); 1.1738 + 1.1739 + typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T; 1.1740 + constraints->add(alloc->new_<T>(alloc, *this, 1.1741 + ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_WRITABLE))); 1.1742 + return false; 1.1743 +} 1.1744 + 1.1745 +bool 1.1746 +TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const 1.1747 +{ 1.1748 + if (other->unknown()) 1.1749 + return unknown(); 1.1750 + 1.1751 + for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) { 1.1752 + Type type = Type::PrimitiveType(TypeFlagPrimitive(flag)); 1.1753 + if (type != filteredType && other->hasType(type) && !hasType(type)) 1.1754 + return false; 1.1755 + } 1.1756 + 1.1757 + if (other->unknownObject()) 1.1758 + return unknownObject(); 1.1759 + 1.1760 + for (size_t i = 0; i < other->getObjectCount(); i++) { 1.1761 + TypeObjectKey *key = other->getObject(i); 1.1762 + if (key) { 1.1763 + Type type = Type::ObjectType(key); 1.1764 + if (type != filteredType && !hasType(type)) 1.1765 + return false; 1.1766 + } 1.1767 + } 1.1768 + 1.1769 + return true; 1.1770 +} 1.1771 + 1.1772 +TemporaryTypeSet::DoubleConversion 1.1773 +TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints) 1.1774 +{ 1.1775 + if (unknownObject() || !getObjectCount()) 1.1776 + return AmbiguousDoubleConversion; 1.1777 + 1.1778 + bool alwaysConvert = true; 1.1779 + bool maybeConvert = false; 1.1780 + bool dontConvert = false; 1.1781 + 1.1782 + for (unsigned i = 0; i < getObjectCount(); i++) { 1.1783 + TypeObjectKey *type = getObject(i); 1.1784 + if (!type) 1.1785 + continue; 1.1786 + 1.1787 + if (type->unknownProperties()) { 1.1788 + alwaysConvert = false; 1.1789 + continue; 1.1790 + } 1.1791 + 1.1792 + HeapTypeSetKey property = type->property(JSID_VOID); 1.1793 + property.freeze(constraints); 1.1794 + 1.1795 + // We can't convert to double elements for objects which do not have 1.1796 + // double in their element types (as the conversion may render the type 1.1797 + // information incorrect), nor for non-array objects (as their elements 1.1798 + // may point to emptyObjectElements, which cannot be converted). 1.1799 + if (!property.maybeTypes() || 1.1800 + !property.maybeTypes()->hasType(Type::DoubleType()) || 1.1801 + type->clasp() != &ArrayObject::class_) 1.1802 + { 1.1803 + dontConvert = true; 1.1804 + alwaysConvert = false; 1.1805 + continue; 1.1806 + } 1.1807 + 1.1808 + // Only bother with converting known packed arrays whose possible 1.1809 + // element types are int or double. Other arrays require type tests 1.1810 + // when elements are accessed regardless of the conversion. 1.1811 + if (property.knownMIRType(constraints) == jit::MIRType_Double && 1.1812 + !type->hasFlags(constraints, OBJECT_FLAG_NON_PACKED)) 1.1813 + { 1.1814 + maybeConvert = true; 1.1815 + } else { 1.1816 + alwaysConvert = false; 1.1817 + } 1.1818 + } 1.1819 + 1.1820 + JS_ASSERT_IF(alwaysConvert, maybeConvert); 1.1821 + 1.1822 + if (maybeConvert && dontConvert) 1.1823 + return AmbiguousDoubleConversion; 1.1824 + if (alwaysConvert) 1.1825 + return AlwaysConvertToDoubles; 1.1826 + if (maybeConvert) 1.1827 + return MaybeConvertToDoubles; 1.1828 + return DontConvertToDoubles; 1.1829 +} 1.1830 + 1.1831 +const Class * 1.1832 +TemporaryTypeSet::getKnownClass() 1.1833 +{ 1.1834 + if (unknownObject()) 1.1835 + return nullptr; 1.1836 + 1.1837 + const Class *clasp = nullptr; 1.1838 + unsigned count = getObjectCount(); 1.1839 + 1.1840 + for (unsigned i = 0; i < count; i++) { 1.1841 + const Class *nclasp = getObjectClass(i); 1.1842 + if (!nclasp) 1.1843 + continue; 1.1844 + 1.1845 + if (clasp && clasp != nclasp) 1.1846 + return nullptr; 1.1847 + clasp = nclasp; 1.1848 + } 1.1849 + 1.1850 + return clasp; 1.1851 +} 1.1852 + 1.1853 +TemporaryTypeSet::ForAllResult 1.1854 +TemporaryTypeSet::forAllClasses(bool (*func)(const Class* clasp)) 1.1855 +{ 1.1856 + if (unknownObject()) 1.1857 + return ForAllResult::MIXED; 1.1858 + 1.1859 + unsigned count = getObjectCount(); 1.1860 + if (count == 0) 1.1861 + return ForAllResult::EMPTY; 1.1862 + 1.1863 + bool true_results = false; 1.1864 + bool false_results = false; 1.1865 + for (unsigned i = 0; i < count; i++) { 1.1866 + const Class *clasp = getObjectClass(i); 1.1867 + if (!clasp) 1.1868 + return ForAllResult::MIXED; 1.1869 + if (func(clasp)) { 1.1870 + true_results = true; 1.1871 + if (false_results) return ForAllResult::MIXED; 1.1872 + } 1.1873 + else { 1.1874 + false_results = true; 1.1875 + if (true_results) return ForAllResult::MIXED; 1.1876 + } 1.1877 + } 1.1878 + 1.1879 + JS_ASSERT(true_results != false_results); 1.1880 + 1.1881 + return true_results ? ForAllResult::ALL_TRUE : ForAllResult::ALL_FALSE; 1.1882 +} 1.1883 + 1.1884 +int 1.1885 +TemporaryTypeSet::getTypedArrayType() 1.1886 +{ 1.1887 + const Class *clasp = getKnownClass(); 1.1888 + 1.1889 + if (clasp && IsTypedArrayClass(clasp)) 1.1890 + return clasp - &TypedArrayObject::classes[0]; 1.1891 + return ScalarTypeDescr::TYPE_MAX; 1.1892 +} 1.1893 + 1.1894 +bool 1.1895 +TemporaryTypeSet::isDOMClass() 1.1896 +{ 1.1897 + if (unknownObject()) 1.1898 + return false; 1.1899 + 1.1900 + unsigned count = getObjectCount(); 1.1901 + for (unsigned i = 0; i < count; i++) { 1.1902 + const Class *clasp = getObjectClass(i); 1.1903 + if (clasp && !clasp->isDOMClass()) 1.1904 + return false; 1.1905 + } 1.1906 + 1.1907 + return count > 0; 1.1908 +} 1.1909 + 1.1910 +bool 1.1911 +TemporaryTypeSet::maybeCallable() 1.1912 +{ 1.1913 + if (!maybeObject()) 1.1914 + return false; 1.1915 + 1.1916 + if (unknownObject()) 1.1917 + return true; 1.1918 + 1.1919 + unsigned count = getObjectCount(); 1.1920 + for (unsigned i = 0; i < count; i++) { 1.1921 + const Class *clasp = getObjectClass(i); 1.1922 + if (clasp && clasp->isCallable()) 1.1923 + return true; 1.1924 + } 1.1925 + 1.1926 + return false; 1.1927 +} 1.1928 + 1.1929 +bool 1.1930 +TemporaryTypeSet::maybeEmulatesUndefined() 1.1931 +{ 1.1932 + if (!maybeObject()) 1.1933 + return false; 1.1934 + 1.1935 + if (unknownObject()) 1.1936 + return true; 1.1937 + 1.1938 + unsigned count = getObjectCount(); 1.1939 + for (unsigned i = 0; i < count; i++) { 1.1940 + // The object emulates undefined if clasp->emulatesUndefined() or if 1.1941 + // it's a WrapperObject, see EmulatesUndefined. Since all wrappers are 1.1942 + // proxies, we can just check for that. 1.1943 + const Class *clasp = getObjectClass(i); 1.1944 + if (clasp && (clasp->emulatesUndefined() || clasp->isProxy())) 1.1945 + return true; 1.1946 + } 1.1947 + 1.1948 + return false; 1.1949 +} 1.1950 + 1.1951 +JSObject * 1.1952 +TemporaryTypeSet::getCommonPrototype() 1.1953 +{ 1.1954 + if (unknownObject()) 1.1955 + return nullptr; 1.1956 + 1.1957 + JSObject *proto = nullptr; 1.1958 + unsigned count = getObjectCount(); 1.1959 + 1.1960 + for (unsigned i = 0; i < count; i++) { 1.1961 + TypeObjectKey *object = getObject(i); 1.1962 + if (!object) 1.1963 + continue; 1.1964 + 1.1965 + if (!object->hasTenuredProto()) 1.1966 + return nullptr; 1.1967 + 1.1968 + TaggedProto nproto = object->proto(); 1.1969 + if (proto) { 1.1970 + if (nproto != proto) 1.1971 + return nullptr; 1.1972 + } else { 1.1973 + if (!nproto.isObject()) 1.1974 + return nullptr; 1.1975 + proto = nproto.toObject(); 1.1976 + } 1.1977 + } 1.1978 + 1.1979 + return proto; 1.1980 +} 1.1981 + 1.1982 +bool 1.1983 +TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id) 1.1984 +{ 1.1985 + if (unknownObject()) 1.1986 + return true; 1.1987 + 1.1988 + for (unsigned i = 0; i < getObjectCount(); i++) { 1.1989 + TypeObjectKey *type = getObject(i); 1.1990 + if (!type) 1.1991 + continue; 1.1992 + 1.1993 + if (type->unknownProperties()) 1.1994 + return true; 1.1995 + 1.1996 + HeapTypeSetKey property = type->property(id); 1.1997 + if (property.needsBarrier(constraints)) 1.1998 + return true; 1.1999 + } 1.2000 + 1.2001 + return false; 1.2002 +} 1.2003 + 1.2004 +///////////////////////////////////////////////////////////////////// 1.2005 +// TypeCompartment 1.2006 +///////////////////////////////////////////////////////////////////// 1.2007 + 1.2008 +TypeCompartment::TypeCompartment() 1.2009 +{ 1.2010 + PodZero(this); 1.2011 +} 1.2012 + 1.2013 +TypeObject * 1.2014 +TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto, 1.2015 + TypeObjectFlags initialFlags) 1.2016 +{ 1.2017 + JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); 1.2018 + 1.2019 + if (cx->isJSContext()) { 1.2020 + if (proto.isObject() && IsInsideNursery(cx->asJSContext()->runtime(), proto.toObject())) 1.2021 + initialFlags |= OBJECT_FLAG_NURSERY_PROTO; 1.2022 + } 1.2023 + 1.2024 + TypeObject *object = js::NewTypeObject(cx); 1.2025 + if (!object) 1.2026 + return nullptr; 1.2027 + new(object) TypeObject(clasp, proto, initialFlags); 1.2028 + 1.2029 + return object; 1.2030 +} 1.2031 + 1.2032 +TypeObject * 1.2033 +TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key) 1.2034 +{ 1.2035 + AutoEnterAnalysis enter(cx); 1.2036 + 1.2037 + if (!allocationSiteTable) { 1.2038 + allocationSiteTable = cx->new_<AllocationSiteTable>(); 1.2039 + if (!allocationSiteTable || !allocationSiteTable->init()) { 1.2040 + js_delete(allocationSiteTable); 1.2041 + return nullptr; 1.2042 + } 1.2043 + } 1.2044 + 1.2045 + AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key); 1.2046 + JS_ASSERT(!p); 1.2047 + 1.2048 + TypeObject *res = nullptr; 1.2049 + 1.2050 + jsbytecode *pc = key.script->offsetToPC(key.offset); 1.2051 + RootedScript keyScript(cx, key.script); 1.2052 + 1.2053 + if (!res) { 1.2054 + RootedObject proto(cx); 1.2055 + if (!GetBuiltinPrototype(cx, key.kind, &proto)) 1.2056 + return nullptr; 1.2057 + 1.2058 + Rooted<TaggedProto> tagged(cx, TaggedProto(proto)); 1.2059 + res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE); 1.2060 + if (!res) 1.2061 + return nullptr; 1.2062 + key.script = keyScript; 1.2063 + } 1.2064 + 1.2065 + if (JSOp(*pc) == JSOP_NEWOBJECT) { 1.2066 + /* 1.2067 + * This object is always constructed the same way and will not be 1.2068 + * observed by other code before all properties have been added. Mark 1.2069 + * all the properties as definite properties of the object. 1.2070 + */ 1.2071 + RootedObject baseobj(cx, key.script->getObject(GET_UINT32_INDEX(pc))); 1.2072 + 1.2073 + if (!res->addDefiniteProperties(cx, baseobj)) 1.2074 + return nullptr; 1.2075 + } 1.2076 + 1.2077 + if (!allocationSiteTable->add(p, key, res)) 1.2078 + return nullptr; 1.2079 + 1.2080 + return res; 1.2081 +} 1.2082 + 1.2083 +static inline jsid 1.2084 +GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset) 1.2085 +{ 1.2086 + PropertyName *name = script->getName(GET_UINT32_INDEX(pc + offset)); 1.2087 + return IdToTypeId(NameToId(name)); 1.2088 +} 1.2089 + 1.2090 +bool 1.2091 +types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc) 1.2092 +{ 1.2093 + /* 1.2094 + * Make a heuristic guess at a use of JSOP_NEW that the constructed object 1.2095 + * should have a fresh type object. We do this when the NEW is immediately 1.2096 + * followed by a simple assignment to an object's .prototype field. 1.2097 + * This is designed to catch common patterns for subclassing in JS: 1.2098 + * 1.2099 + * function Super() { ... } 1.2100 + * function Sub1() { ... } 1.2101 + * function Sub2() { ... } 1.2102 + * 1.2103 + * Sub1.prototype = new Super(); 1.2104 + * Sub2.prototype = new Super(); 1.2105 + * 1.2106 + * Using distinct type objects for the particular prototypes of Sub1 and 1.2107 + * Sub2 lets us continue to distinguish the two subclasses and any extra 1.2108 + * properties added to those prototype objects. 1.2109 + */ 1.2110 + if (JSOp(*pc) == JSOP_NEW) 1.2111 + pc += JSOP_NEW_LENGTH; 1.2112 + else if (JSOp(*pc) == JSOP_SPREADNEW) 1.2113 + pc += JSOP_SPREADNEW_LENGTH; 1.2114 + else 1.2115 + return false; 1.2116 + if (JSOp(*pc) == JSOP_SETPROP) { 1.2117 + jsid id = GetAtomId(cx, script, pc, 0); 1.2118 + if (id == id_prototype(cx)) 1.2119 + return true; 1.2120 + } 1.2121 + 1.2122 + return false; 1.2123 +} 1.2124 + 1.2125 +NewObjectKind 1.2126 +types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key) 1.2127 +{ 1.2128 + /* 1.2129 + * Objects created outside loops in global and eval scripts should have 1.2130 + * singleton types. For now this is only done for plain objects and typed 1.2131 + * arrays, but not normal arrays. 1.2132 + */ 1.2133 + 1.2134 + if (script->functionNonDelazifying() && !script->treatAsRunOnce()) 1.2135 + return GenericObject; 1.2136 + 1.2137 + if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray)) 1.2138 + return GenericObject; 1.2139 + 1.2140 + /* 1.2141 + * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note 1.2142 + * indicating their boundary. 1.2143 + */ 1.2144 + 1.2145 + if (!script->hasTrynotes()) 1.2146 + return SingletonObject; 1.2147 + 1.2148 + unsigned offset = script->pcToOffset(pc); 1.2149 + 1.2150 + JSTryNote *tn = script->trynotes()->vector; 1.2151 + JSTryNote *tnlimit = tn + script->trynotes()->length; 1.2152 + for (; tn < tnlimit; tn++) { 1.2153 + if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP) 1.2154 + continue; 1.2155 + 1.2156 + unsigned startOffset = script->mainOffset() + tn->start; 1.2157 + unsigned endOffset = startOffset + tn->length; 1.2158 + 1.2159 + if (offset >= startOffset && offset < endOffset) 1.2160 + return GenericObject; 1.2161 + } 1.2162 + 1.2163 + return SingletonObject; 1.2164 +} 1.2165 + 1.2166 +NewObjectKind 1.2167 +types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp) 1.2168 +{ 1.2169 + return UseNewTypeForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp)); 1.2170 +} 1.2171 + 1.2172 +static inline bool 1.2173 +ClassCanHaveExtraProperties(const Class *clasp) 1.2174 +{ 1.2175 + JS_ASSERT(clasp->resolve); 1.2176 + return clasp->resolve != JS_ResolveStub 1.2177 + || clasp->ops.lookupGeneric 1.2178 + || clasp->ops.getGeneric 1.2179 + || IsTypedArrayClass(clasp); 1.2180 +} 1.2181 + 1.2182 +static inline bool 1.2183 +PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj) 1.2184 +{ 1.2185 + do { 1.2186 + TypeObjectKey *type = TypeObjectKey::get(obj); 1.2187 + if (ClassCanHaveExtraProperties(type->clasp())) 1.2188 + return true; 1.2189 + if (type->unknownProperties()) 1.2190 + return true; 1.2191 + HeapTypeSetKey index = type->property(JSID_VOID); 1.2192 + if (index.nonData(constraints) || index.isOwnProperty(constraints)) 1.2193 + return true; 1.2194 + if (!obj->hasTenuredProto()) 1.2195 + return true; 1.2196 + obj = obj->getProto(); 1.2197 + } while (obj); 1.2198 + 1.2199 + return false; 1.2200 +} 1.2201 + 1.2202 +bool 1.2203 +types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSScript *script) 1.2204 +{ 1.2205 + if (JSObject *proto = script->global().maybeGetArrayPrototype()) 1.2206 + return PrototypeHasIndexedProperty(constraints, proto); 1.2207 + return true; 1.2208 +} 1.2209 + 1.2210 +bool 1.2211 +types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, 1.2212 + TemporaryTypeSet *types) 1.2213 +{ 1.2214 + const Class *clasp = types->getKnownClass(); 1.2215 + 1.2216 + // Note: typed arrays have indexed properties not accounted for by type 1.2217 + // information, though these are all in bounds and will be accounted for 1.2218 + // by JIT paths. 1.2219 + if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp))) 1.2220 + return true; 1.2221 + 1.2222 + if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES)) 1.2223 + return true; 1.2224 + 1.2225 + JSObject *proto = types->getCommonPrototype(); 1.2226 + if (!proto) 1.2227 + return true; 1.2228 + 1.2229 + return PrototypeHasIndexedProperty(constraints, proto); 1.2230 +} 1.2231 + 1.2232 +void 1.2233 +TypeZone::processPendingRecompiles(FreeOp *fop) 1.2234 +{ 1.2235 + if (!pendingRecompiles) 1.2236 + return; 1.2237 + 1.2238 + /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */ 1.2239 + Vector<RecompileInfo> *pending = pendingRecompiles; 1.2240 + pendingRecompiles = nullptr; 1.2241 + 1.2242 + JS_ASSERT(!pending->empty()); 1.2243 + 1.2244 +#ifdef JS_ION 1.2245 + jit::Invalidate(*this, fop, *pending); 1.2246 +#endif 1.2247 + 1.2248 + fop->delete_(pending); 1.2249 +} 1.2250 + 1.2251 +void 1.2252 +TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info) 1.2253 +{ 1.2254 + CompilerOutput *co = info.compilerOutput(cx); 1.2255 + if (!co || !co->isValid() || co->pendingInvalidation()) 1.2256 + return; 1.2257 + 1.2258 + InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d", 1.2259 + co->script(), co->script()->filename(), co->script()->lineno()); 1.2260 + 1.2261 + co->setPendingInvalidation(); 1.2262 + 1.2263 + if (!pendingRecompiles) { 1.2264 + pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx); 1.2265 + if (!pendingRecompiles) 1.2266 + CrashAtUnhandlableOOM("Could not update pendingRecompiles"); 1.2267 + } 1.2268 + 1.2269 + if (!pendingRecompiles->append(info)) 1.2270 + CrashAtUnhandlableOOM("Could not update pendingRecompiles"); 1.2271 +} 1.2272 + 1.2273 +void 1.2274 +TypeZone::addPendingRecompile(JSContext *cx, JSScript *script) 1.2275 +{ 1.2276 + JS_ASSERT(script); 1.2277 + 1.2278 +#ifdef JS_ION 1.2279 + CancelOffThreadIonCompile(cx->compartment(), script); 1.2280 + 1.2281 + // Let the script warm up again before attempting another compile. 1.2282 + if (jit::IsBaselineEnabled(cx)) 1.2283 + script->resetUseCount(); 1.2284 + 1.2285 + if (script->hasIonScript()) 1.2286 + addPendingRecompile(cx, script->ionScript()->recompileInfo()); 1.2287 + 1.2288 + if (script->hasParallelIonScript()) 1.2289 + addPendingRecompile(cx, script->parallelIonScript()->recompileInfo()); 1.2290 +#endif 1.2291 + 1.2292 + // When one script is inlined into another the caller listens to state 1.2293 + // changes on the callee's script, so trigger these to force recompilation 1.2294 + // of any such callers. 1.2295 + if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyType()) 1.2296 + ObjectStateChange(cx, script->functionNonDelazifying()->type(), false); 1.2297 +} 1.2298 + 1.2299 +void 1.2300 +TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target) 1.2301 +{ 1.2302 + JS_ASSERT(this == &cx->compartment()->types); 1.2303 + JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN)); 1.2304 + JS_ASSERT(!target->singleton()); 1.2305 + JS_ASSERT(target->unknownProperties()); 1.2306 + 1.2307 + AutoEnterAnalysis enter(cx); 1.2308 + 1.2309 + /* Mark type sets which contain obj as having a generic object types. */ 1.2310 + 1.2311 + for (gc::CellIter i(cx->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { 1.2312 + TypeObject *object = i.get<TypeObject>(); 1.2313 + unsigned count = object->getPropertyCount(); 1.2314 + for (unsigned i = 0; i < count; i++) { 1.2315 + Property *prop = object->getProperty(i); 1.2316 + if (prop && prop->types.hasType(Type::ObjectType(target))) 1.2317 + prop->types.addType(cx, Type::AnyObjectType()); 1.2318 + } 1.2319 + } 1.2320 + 1.2321 + for (gc::CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) { 1.2322 + RootedScript script(cx, i.get<JSScript>()); 1.2323 + if (script->types) { 1.2324 + unsigned count = TypeScript::NumTypeSets(script); 1.2325 + StackTypeSet *typeArray = script->types->typeArray(); 1.2326 + for (unsigned i = 0; i < count; i++) { 1.2327 + if (typeArray[i].hasType(Type::ObjectType(target))) 1.2328 + typeArray[i].addType(cx, Type::AnyObjectType()); 1.2329 + } 1.2330 + } 1.2331 + } 1.2332 + 1.2333 + target->addFlags(OBJECT_FLAG_SETS_MARKED_UNKNOWN); 1.2334 +} 1.2335 + 1.2336 +void 1.2337 +TypeCompartment::print(JSContext *cx, bool force) 1.2338 +{ 1.2339 +#ifdef DEBUG 1.2340 + gc::AutoSuppressGC suppressGC(cx); 1.2341 + 1.2342 + JSCompartment *compartment = this->compartment(); 1.2343 + AutoEnterAnalysis enter(nullptr, compartment); 1.2344 + 1.2345 + if (!force && !InferSpewActive(ISpewResult)) 1.2346 + return; 1.2347 + 1.2348 + for (gc::CellIter i(compartment->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) { 1.2349 + // Note: use cx->runtime() instead of cx to work around IsInRequest(cx) 1.2350 + // assertion failures when we're called from DestroyContext. 1.2351 + RootedScript script(cx->runtime(), i.get<JSScript>()); 1.2352 + if (script->types) 1.2353 + script->types->printTypes(cx, script); 1.2354 + } 1.2355 + 1.2356 + for (gc::CellIter i(compartment->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { 1.2357 + TypeObject *object = i.get<TypeObject>(); 1.2358 + object->print(); 1.2359 + } 1.2360 +#endif 1.2361 +} 1.2362 + 1.2363 +///////////////////////////////////////////////////////////////////// 1.2364 +// TypeCompartment tables 1.2365 +///////////////////////////////////////////////////////////////////// 1.2366 + 1.2367 +/* 1.2368 + * The arrayTypeTable and objectTypeTable are per-compartment tables for making 1.2369 + * common type objects to model the contents of large script singletons and 1.2370 + * JSON objects. These are vanilla Arrays and native Objects, so we distinguish 1.2371 + * the types of different ones by looking at the types of their properties. 1.2372 + * 1.2373 + * All singleton/JSON arrays which have the same prototype, are homogenous and 1.2374 + * of the same element type will share a type object. All singleton/JSON 1.2375 + * objects which have the same shape and property types will also share a type 1.2376 + * object. We don't try to collate arrays or objects that have type mismatches. 1.2377 + */ 1.2378 + 1.2379 +static inline bool 1.2380 +NumberTypes(Type a, Type b) 1.2381 +{ 1.2382 + return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE)) 1.2383 + && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE)); 1.2384 +} 1.2385 + 1.2386 +/* 1.2387 + * As for GetValueType, but requires object types to be non-singletons with 1.2388 + * their default prototype. These are the only values that should appear in 1.2389 + * arrays and objects whose type can be fixed. 1.2390 + */ 1.2391 +static inline Type 1.2392 +GetValueTypeForTable(const Value &v) 1.2393 +{ 1.2394 + Type type = GetValueType(v); 1.2395 + JS_ASSERT(!type.isSingleObject()); 1.2396 + return type; 1.2397 +} 1.2398 + 1.2399 +struct types::ArrayTableKey : public DefaultHasher<types::ArrayTableKey> 1.2400 +{ 1.2401 + Type type; 1.2402 + JSObject *proto; 1.2403 + 1.2404 + ArrayTableKey() 1.2405 + : type(Type::UndefinedType()), proto(nullptr) 1.2406 + {} 1.2407 + 1.2408 + ArrayTableKey(Type type, JSObject *proto) 1.2409 + : type(type), proto(proto) 1.2410 + {} 1.2411 + 1.2412 + static inline uint32_t hash(const ArrayTableKey &v) { 1.2413 + return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2)); 1.2414 + } 1.2415 + 1.2416 + static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) { 1.2417 + return v1.type == v2.type && v1.proto == v2.proto; 1.2418 + } 1.2419 +}; 1.2420 + 1.2421 +void 1.2422 +TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx, 1.2423 + JSObject *obj, Type elementType) 1.2424 +{ 1.2425 + JS_ASSERT(cx->compartment()->activeAnalysis); 1.2426 + 1.2427 + if (!arrayTypeTable) { 1.2428 + arrayTypeTable = cx->new_<ArrayTypeTable>(); 1.2429 + if (!arrayTypeTable || !arrayTypeTable->init()) { 1.2430 + arrayTypeTable = nullptr; 1.2431 + return; 1.2432 + } 1.2433 + } 1.2434 + 1.2435 + ArrayTableKey key(elementType, obj->getProto()); 1.2436 + DependentAddPtr<ArrayTypeTable> p(cx, *arrayTypeTable, key); 1.2437 + if (p) { 1.2438 + obj->setType(p->value()); 1.2439 + } else { 1.2440 + /* Make a new type to use for future arrays with the same elements. */ 1.2441 + RootedObject objProto(cx, obj->getProto()); 1.2442 + TypeObject *objType = newTypeObject(cx, &ArrayObject::class_, objProto); 1.2443 + if (!objType) 1.2444 + return; 1.2445 + obj->setType(objType); 1.2446 + 1.2447 + if (!objType->unknownProperties()) 1.2448 + objType->addPropertyType(cx, JSID_VOID, elementType); 1.2449 + 1.2450 + key.proto = objProto; 1.2451 + (void) p.add(cx, *arrayTypeTable, key, objType); 1.2452 + } 1.2453 +} 1.2454 + 1.2455 +void 1.2456 +TypeCompartment::fixArrayType(ExclusiveContext *cx, JSObject *obj) 1.2457 +{ 1.2458 + AutoEnterAnalysis enter(cx); 1.2459 + 1.2460 + /* 1.2461 + * If the array is of homogenous type, pick a type object which will be 1.2462 + * shared with all other singleton/JSON arrays of the same type. 1.2463 + * If the array is heterogenous, keep the existing type object, which has 1.2464 + * unknown properties. 1.2465 + */ 1.2466 + JS_ASSERT(obj->is<ArrayObject>()); 1.2467 + 1.2468 + unsigned len = obj->getDenseInitializedLength(); 1.2469 + if (len == 0) 1.2470 + return; 1.2471 + 1.2472 + Type type = GetValueTypeForTable(obj->getDenseElement(0)); 1.2473 + 1.2474 + for (unsigned i = 1; i < len; i++) { 1.2475 + Type ntype = GetValueTypeForTable(obj->getDenseElement(i)); 1.2476 + if (ntype != type) { 1.2477 + if (NumberTypes(type, ntype)) 1.2478 + type = Type::DoubleType(); 1.2479 + else 1.2480 + return; 1.2481 + } 1.2482 + } 1.2483 + 1.2484 + setTypeToHomogenousArray(cx, obj, type); 1.2485 +} 1.2486 + 1.2487 +void 1.2488 +types::FixRestArgumentsType(ExclusiveContext *cx, JSObject *obj) 1.2489 +{ 1.2490 + cx->compartment()->types.fixRestArgumentsType(cx, obj); 1.2491 +} 1.2492 + 1.2493 +void 1.2494 +TypeCompartment::fixRestArgumentsType(ExclusiveContext *cx, JSObject *obj) 1.2495 +{ 1.2496 + AutoEnterAnalysis enter(cx); 1.2497 + 1.2498 + /* 1.2499 + * Tracking element types for rest argument arrays is not worth it, but we 1.2500 + * still want it to be known that it's a dense array. 1.2501 + */ 1.2502 + JS_ASSERT(obj->is<ArrayObject>()); 1.2503 + 1.2504 + setTypeToHomogenousArray(cx, obj, Type::UnknownType()); 1.2505 +} 1.2506 + 1.2507 +/* 1.2508 + * N.B. We could also use the initial shape of the object (before its type is 1.2509 + * fixed) as the key in the object table, but since all references in the table 1.2510 + * are weak the hash entries would usually be collected on GC even if objects 1.2511 + * with the new type/shape are still live. 1.2512 + */ 1.2513 +struct types::ObjectTableKey 1.2514 +{ 1.2515 + jsid *properties; 1.2516 + uint32_t nproperties; 1.2517 + uint32_t nfixed; 1.2518 + 1.2519 + struct Lookup { 1.2520 + IdValuePair *properties; 1.2521 + uint32_t nproperties; 1.2522 + uint32_t nfixed; 1.2523 + 1.2524 + Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed) 1.2525 + : properties(properties), nproperties(nproperties), nfixed(nfixed) 1.2526 + {} 1.2527 + }; 1.2528 + 1.2529 + static inline HashNumber hash(const Lookup &lookup) { 1.2530 + return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^ 1.2531 + lookup.nproperties ^ 1.2532 + lookup.nfixed); 1.2533 + } 1.2534 + 1.2535 + static inline bool match(const ObjectTableKey &v, const Lookup &lookup) { 1.2536 + if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed) 1.2537 + return false; 1.2538 + for (size_t i = 0; i < lookup.nproperties; i++) { 1.2539 + if (lookup.properties[i].id != v.properties[i]) 1.2540 + return false; 1.2541 + } 1.2542 + return true; 1.2543 + } 1.2544 +}; 1.2545 + 1.2546 +struct types::ObjectTableEntry 1.2547 +{ 1.2548 + ReadBarriered<TypeObject> object; 1.2549 + ReadBarriered<Shape> shape; 1.2550 + Type *types; 1.2551 +}; 1.2552 + 1.2553 +static inline void 1.2554 +UpdateObjectTableEntryTypes(ExclusiveContext *cx, ObjectTableEntry &entry, 1.2555 + IdValuePair *properties, size_t nproperties) 1.2556 +{ 1.2557 + if (entry.object->unknownProperties()) 1.2558 + return; 1.2559 + for (size_t i = 0; i < nproperties; i++) { 1.2560 + Type type = entry.types[i]; 1.2561 + Type ntype = GetValueTypeForTable(properties[i].value); 1.2562 + if (ntype == type) 1.2563 + continue; 1.2564 + if (ntype.isPrimitive(JSVAL_TYPE_INT32) && 1.2565 + type.isPrimitive(JSVAL_TYPE_DOUBLE)) 1.2566 + { 1.2567 + /* The property types already reflect 'int32'. */ 1.2568 + } else { 1.2569 + if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) && 1.2570 + type.isPrimitive(JSVAL_TYPE_INT32)) 1.2571 + { 1.2572 + /* Include 'double' in the property types to avoid the update below later. */ 1.2573 + entry.types[i] = Type::DoubleType(); 1.2574 + } 1.2575 + entry.object->addPropertyType(cx, IdToTypeId(properties[i].id), ntype); 1.2576 + } 1.2577 + } 1.2578 +} 1.2579 + 1.2580 +void 1.2581 +TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj) 1.2582 +{ 1.2583 + AutoEnterAnalysis enter(cx); 1.2584 + 1.2585 + if (!objectTypeTable) { 1.2586 + objectTypeTable = cx->new_<ObjectTypeTable>(); 1.2587 + if (!objectTypeTable || !objectTypeTable->init()) { 1.2588 + js_delete(objectTypeTable); 1.2589 + objectTypeTable = nullptr; 1.2590 + return; 1.2591 + } 1.2592 + } 1.2593 + 1.2594 + /* 1.2595 + * Use the same type object for all singleton/JSON objects with the same 1.2596 + * base shape, i.e. the same fields written in the same order. 1.2597 + */ 1.2598 + JS_ASSERT(obj->is<JSObject>()); 1.2599 + 1.2600 + /* 1.2601 + * Exclude some objects we can't readily associate common types for based on their 1.2602 + * shape. Objects with metadata are excluded so that the metadata does not need to 1.2603 + * be included in the table lookup (the metadata object might be in the nursery). 1.2604 + */ 1.2605 + if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata()) 1.2606 + return; 1.2607 + 1.2608 + Vector<IdValuePair> properties(cx); 1.2609 + if (!properties.resize(obj->slotSpan())) 1.2610 + return; 1.2611 + 1.2612 + Shape *shape = obj->lastProperty(); 1.2613 + while (!shape->isEmptyShape()) { 1.2614 + IdValuePair &entry = properties[shape->slot()]; 1.2615 + entry.id = shape->propid(); 1.2616 + entry.value = obj->getSlot(shape->slot()); 1.2617 + shape = shape->previous(); 1.2618 + } 1.2619 + 1.2620 + ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots()); 1.2621 + ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup); 1.2622 + 1.2623 + if (p) { 1.2624 + JS_ASSERT(obj->getProto() == p->value().object->proto().toObject()); 1.2625 + JS_ASSERT(obj->lastProperty() == p->value().shape); 1.2626 + 1.2627 + UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length()); 1.2628 + obj->setType(p->value().object); 1.2629 + return; 1.2630 + } 1.2631 + 1.2632 + /* Make a new type to use for the object and similar future ones. */ 1.2633 + Rooted<TaggedProto> objProto(cx, obj->getTaggedProto()); 1.2634 + TypeObject *objType = newTypeObject(cx, &JSObject::class_, objProto); 1.2635 + if (!objType || !objType->addDefiniteProperties(cx, obj)) 1.2636 + return; 1.2637 + 1.2638 + if (obj->isIndexed()) 1.2639 + objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES); 1.2640 + 1.2641 + ScopedJSFreePtr<jsid> ids(cx->pod_calloc<jsid>(properties.length())); 1.2642 + if (!ids) 1.2643 + return; 1.2644 + 1.2645 + ScopedJSFreePtr<Type> types(cx->pod_calloc<Type>(properties.length())); 1.2646 + if (!types) 1.2647 + return; 1.2648 + 1.2649 + for (size_t i = 0; i < properties.length(); i++) { 1.2650 + ids[i] = properties[i].id; 1.2651 + types[i] = GetValueTypeForTable(obj->getSlot(i)); 1.2652 + if (!objType->unknownProperties()) 1.2653 + objType->addPropertyType(cx, IdToTypeId(ids[i]), types[i]); 1.2654 + } 1.2655 + 1.2656 + ObjectTableKey key; 1.2657 + key.properties = ids; 1.2658 + key.nproperties = properties.length(); 1.2659 + key.nfixed = obj->numFixedSlots(); 1.2660 + JS_ASSERT(ObjectTableKey::match(key, lookup)); 1.2661 + 1.2662 + ObjectTableEntry entry; 1.2663 + entry.object = objType; 1.2664 + entry.shape = obj->lastProperty(); 1.2665 + entry.types = types; 1.2666 + 1.2667 + obj->setType(objType); 1.2668 + 1.2669 + p = objectTypeTable->lookupForAdd(lookup); 1.2670 + if (objectTypeTable->add(p, key, entry)) { 1.2671 + ids.forget(); 1.2672 + types.forget(); 1.2673 + } 1.2674 +} 1.2675 + 1.2676 +JSObject * 1.2677 +TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties) 1.2678 +{ 1.2679 + AutoEnterAnalysis enter(cx); 1.2680 + 1.2681 + if (!objectTypeTable) { 1.2682 + objectTypeTable = cx->new_<ObjectTypeTable>(); 1.2683 + if (!objectTypeTable || !objectTypeTable->init()) { 1.2684 + js_delete(objectTypeTable); 1.2685 + objectTypeTable = nullptr; 1.2686 + return nullptr; 1.2687 + } 1.2688 + } 1.2689 + 1.2690 + /* 1.2691 + * Use the object type table to allocate an object with the specified 1.2692 + * properties, filling in its final type and shape and failing if no cache 1.2693 + * entry could be found for the properties. 1.2694 + */ 1.2695 + 1.2696 + /* 1.2697 + * Filter out a few cases where we don't want to use the object type table. 1.2698 + * Note that if the properties contain any duplicates or dense indexes, 1.2699 + * the lookup below will fail as such arrays of properties cannot be stored 1.2700 + * in the object type table --- fixObjectType populates the table with 1.2701 + * properties read off its input object, which cannot be duplicates, and 1.2702 + * ignores objects with dense indexes. 1.2703 + */ 1.2704 + if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT) 1.2705 + return nullptr; 1.2706 + 1.2707 + gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties); 1.2708 + size_t nfixed = gc::GetGCKindSlots(allocKind, &JSObject::class_); 1.2709 + 1.2710 + ObjectTableKey::Lookup lookup(properties, nproperties, nfixed); 1.2711 + ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup); 1.2712 + 1.2713 + if (!p) 1.2714 + return nullptr; 1.2715 + 1.2716 + RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind)); 1.2717 + if (!obj) { 1.2718 + cx->clearPendingException(); 1.2719 + return nullptr; 1.2720 + } 1.2721 + JS_ASSERT(obj->getProto() == p->value().object->proto().toObject()); 1.2722 + 1.2723 + RootedShape shape(cx, p->value().shape); 1.2724 + if (!JSObject::setLastProperty(cx, obj, shape)) { 1.2725 + cx->clearPendingException(); 1.2726 + return nullptr; 1.2727 + } 1.2728 + 1.2729 + UpdateObjectTableEntryTypes(cx, p->value(), properties, nproperties); 1.2730 + 1.2731 + for (size_t i = 0; i < nproperties; i++) 1.2732 + obj->setSlot(i, properties[i].value); 1.2733 + 1.2734 + obj->setType(p->value().object); 1.2735 + return obj; 1.2736 +} 1.2737 + 1.2738 +///////////////////////////////////////////////////////////////////// 1.2739 +// TypeObject 1.2740 +///////////////////////////////////////////////////////////////////// 1.2741 + 1.2742 +void 1.2743 +TypeObject::setProto(JSContext *cx, TaggedProto proto) 1.2744 +{ 1.2745 + JS_ASSERT(singleton()); 1.2746 + 1.2747 + if (proto.isObject() && IsInsideNursery(cx->runtime(), proto.toObject())) 1.2748 + addFlags(OBJECT_FLAG_NURSERY_PROTO); 1.2749 + 1.2750 + setProtoUnchecked(proto); 1.2751 +} 1.2752 + 1.2753 +static inline void 1.2754 +UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shape *shape, 1.2755 + bool indexed) 1.2756 +{ 1.2757 + if (!shape->writable()) 1.2758 + types->setNonWritableProperty(cx); 1.2759 + 1.2760 + if (shape->hasGetterValue() || shape->hasSetterValue()) { 1.2761 + types->setNonDataProperty(cx); 1.2762 + types->TypeSet::addType(Type::UnknownType(), &cx->typeLifoAlloc()); 1.2763 + } else if (shape->hasDefaultGetter() && shape->hasSlot()) { 1.2764 + if (!indexed && types->canSetDefinite(shape->slot())) 1.2765 + types->setDefinite(shape->slot()); 1.2766 + 1.2767 + const Value &value = obj->nativeGetSlot(shape->slot()); 1.2768 + 1.2769 + /* 1.2770 + * Don't add initial undefined types for properties of global objects 1.2771 + * that are not collated into the JSID_VOID property (see propertySet 1.2772 + * comment). 1.2773 + */ 1.2774 + if (indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) { 1.2775 + Type type = GetValueType(value); 1.2776 + types->TypeSet::addType(type, &cx->typeLifoAlloc()); 1.2777 + } 1.2778 + } 1.2779 +} 1.2780 + 1.2781 +void 1.2782 +TypeObject::updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types) 1.2783 +{ 1.2784 + InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s", 1.2785 + InferSpewColor(types), types, InferSpewColorReset(), 1.2786 + TypeObjectString(this), TypeIdString(id)); 1.2787 + 1.2788 + if (!singleton() || !singleton()->isNative()) 1.2789 + return; 1.2790 + 1.2791 + /* 1.2792 + * Fill the property in with any type the object already has in an own 1.2793 + * property. We are only interested in plain native properties and 1.2794 + * dense elements which don't go through a barrier when read by the VM 1.2795 + * or jitcode. 1.2796 + */ 1.2797 + 1.2798 + if (JSID_IS_VOID(id)) { 1.2799 + /* Go through all shapes on the object to get integer-valued properties. */ 1.2800 + RootedShape shape(cx, singleton()->lastProperty()); 1.2801 + while (!shape->isEmptyShape()) { 1.2802 + if (JSID_IS_VOID(IdToTypeId(shape->propid()))) 1.2803 + UpdatePropertyType(cx, types, singleton(), shape, true); 1.2804 + shape = shape->previous(); 1.2805 + } 1.2806 + 1.2807 + /* Also get values of any dense elements in the object. */ 1.2808 + for (size_t i = 0; i < singleton()->getDenseInitializedLength(); i++) { 1.2809 + const Value &value = singleton()->getDenseElement(i); 1.2810 + if (!value.isMagic(JS_ELEMENTS_HOLE)) { 1.2811 + Type type = GetValueType(value); 1.2812 + types->TypeSet::addType(type, &cx->typeLifoAlloc()); 1.2813 + } 1.2814 + } 1.2815 + } else if (!JSID_IS_EMPTY(id)) { 1.2816 + RootedId rootedId(cx, id); 1.2817 + Shape *shape = singleton()->nativeLookup(cx, rootedId); 1.2818 + if (shape) 1.2819 + UpdatePropertyType(cx, types, singleton(), shape, false); 1.2820 + } 1.2821 + 1.2822 + if (singleton()->watched()) { 1.2823 + /* 1.2824 + * Mark the property as non-data, to inhibit optimizations on it 1.2825 + * and avoid bypassing the watchpoint handler. 1.2826 + */ 1.2827 + types->setNonDataProperty(cx); 1.2828 + } 1.2829 +} 1.2830 + 1.2831 +bool 1.2832 +TypeObject::addDefiniteProperties(ExclusiveContext *cx, JSObject *obj) 1.2833 +{ 1.2834 + if (unknownProperties()) 1.2835 + return true; 1.2836 + 1.2837 + /* Mark all properties of obj as definite properties of this type. */ 1.2838 + AutoEnterAnalysis enter(cx); 1.2839 + 1.2840 + RootedShape shape(cx, obj->lastProperty()); 1.2841 + while (!shape->isEmptyShape()) { 1.2842 + jsid id = IdToTypeId(shape->propid()); 1.2843 + if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot())) { 1.2844 + TypeSet *types = getProperty(cx, id); 1.2845 + if (!types) 1.2846 + return false; 1.2847 + types->setDefinite(shape->slot()); 1.2848 + } 1.2849 + shape = shape->previous(); 1.2850 + } 1.2851 + 1.2852 + return true; 1.2853 +} 1.2854 + 1.2855 +bool 1.2856 +TypeObject::matchDefiniteProperties(HandleObject obj) 1.2857 +{ 1.2858 + unsigned count = getPropertyCount(); 1.2859 + for (unsigned i = 0; i < count; i++) { 1.2860 + Property *prop = getProperty(i); 1.2861 + if (!prop) 1.2862 + continue; 1.2863 + if (prop->types.definiteProperty()) { 1.2864 + unsigned slot = prop->types.definiteSlot(); 1.2865 + 1.2866 + bool found = false; 1.2867 + Shape *shape = obj->lastProperty(); 1.2868 + while (!shape->isEmptyShape()) { 1.2869 + if (shape->slot() == slot && shape->propid() == prop->id) { 1.2870 + found = true; 1.2871 + break; 1.2872 + } 1.2873 + shape = shape->previous(); 1.2874 + } 1.2875 + if (!found) 1.2876 + return false; 1.2877 + } 1.2878 + } 1.2879 + 1.2880 + return true; 1.2881 +} 1.2882 + 1.2883 +static inline void 1.2884 +InlineAddTypeProperty(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type) 1.2885 +{ 1.2886 + JS_ASSERT(id == IdToTypeId(id)); 1.2887 + 1.2888 + AutoEnterAnalysis enter(cx); 1.2889 + 1.2890 + HeapTypeSet *types = obj->getProperty(cx, id); 1.2891 + if (!types || types->hasType(type)) 1.2892 + return; 1.2893 + 1.2894 + InferSpew(ISpewOps, "externalType: property %s %s: %s", 1.2895 + TypeObjectString(obj), TypeIdString(id), TypeString(type)); 1.2896 + types->addType(cx, type); 1.2897 +} 1.2898 + 1.2899 +void 1.2900 +TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, Type type) 1.2901 +{ 1.2902 + InlineAddTypeProperty(cx, this, id, type); 1.2903 +} 1.2904 + 1.2905 +void 1.2906 +TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, const Value &value) 1.2907 +{ 1.2908 + InlineAddTypeProperty(cx, this, id, GetValueType(value)); 1.2909 +} 1.2910 + 1.2911 +void 1.2912 +TypeObject::markPropertyNonData(ExclusiveContext *cx, jsid id) 1.2913 +{ 1.2914 + AutoEnterAnalysis enter(cx); 1.2915 + 1.2916 + id = IdToTypeId(id); 1.2917 + 1.2918 + HeapTypeSet *types = getProperty(cx, id); 1.2919 + if (types) 1.2920 + types->setNonDataProperty(cx); 1.2921 +} 1.2922 + 1.2923 +void 1.2924 +TypeObject::markPropertyNonWritable(ExclusiveContext *cx, jsid id) 1.2925 +{ 1.2926 + AutoEnterAnalysis enter(cx); 1.2927 + 1.2928 + id = IdToTypeId(id); 1.2929 + 1.2930 + HeapTypeSet *types = getProperty(cx, id); 1.2931 + if (types) 1.2932 + types->setNonWritableProperty(cx); 1.2933 +} 1.2934 + 1.2935 +bool 1.2936 +TypeObject::isPropertyNonData(jsid id) 1.2937 +{ 1.2938 + TypeSet *types = maybeGetProperty(id); 1.2939 + if (types) 1.2940 + return types->nonDataProperty(); 1.2941 + return false; 1.2942 +} 1.2943 + 1.2944 +bool 1.2945 +TypeObject::isPropertyNonWritable(jsid id) 1.2946 +{ 1.2947 + TypeSet *types = maybeGetProperty(id); 1.2948 + if (types) 1.2949 + return types->nonWritableProperty(); 1.2950 + return false; 1.2951 +} 1.2952 + 1.2953 +void 1.2954 +TypeObject::markStateChange(ExclusiveContext *cxArg) 1.2955 +{ 1.2956 + if (unknownProperties()) 1.2957 + return; 1.2958 + 1.2959 + AutoEnterAnalysis enter(cxArg); 1.2960 + HeapTypeSet *types = maybeGetProperty(JSID_EMPTY); 1.2961 + if (types) { 1.2962 + if (JSContext *cx = cxArg->maybeJSContext()) { 1.2963 + TypeConstraint *constraint = types->constraintList; 1.2964 + while (constraint) { 1.2965 + constraint->newObjectState(cx, this); 1.2966 + constraint = constraint->next; 1.2967 + } 1.2968 + } else { 1.2969 + JS_ASSERT(!types->constraintList); 1.2970 + } 1.2971 + } 1.2972 +} 1.2973 + 1.2974 +void 1.2975 +TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags) 1.2976 +{ 1.2977 + if (hasAllFlags(flags)) 1.2978 + return; 1.2979 + 1.2980 + AutoEnterAnalysis enter(cx); 1.2981 + 1.2982 + if (singleton()) { 1.2983 + /* Make sure flags are consistent with persistent object state. */ 1.2984 + JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, 1.2985 + singleton()->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)); 1.2986 + } 1.2987 + 1.2988 + addFlags(flags); 1.2989 + 1.2990 + InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags); 1.2991 + 1.2992 + ObjectStateChange(cx, this, false); 1.2993 +} 1.2994 + 1.2995 +void 1.2996 +TypeObject::markUnknown(ExclusiveContext *cx) 1.2997 +{ 1.2998 + AutoEnterAnalysis enter(cx); 1.2999 + 1.3000 + JS_ASSERT(cx->compartment()->activeAnalysis); 1.3001 + JS_ASSERT(!unknownProperties()); 1.3002 + 1.3003 + if (!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED)) 1.3004 + clearAddendum(cx); 1.3005 + 1.3006 + InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this)); 1.3007 + 1.3008 + ObjectStateChange(cx, this, true); 1.3009 + 1.3010 + /* 1.3011 + * Existing constraints may have already been added to this object, which we need 1.3012 + * to do the right thing for. We can't ensure that we will mark all unknown 1.3013 + * objects before they have been accessed, as the __proto__ of a known object 1.3014 + * could be dynamically set to an unknown object, and we can decide to ignore 1.3015 + * properties of an object during analysis (i.e. hashmaps). Adding unknown for 1.3016 + * any properties accessed already accounts for possible values read from them. 1.3017 + */ 1.3018 + 1.3019 + unsigned count = getPropertyCount(); 1.3020 + for (unsigned i = 0; i < count; i++) { 1.3021 + Property *prop = getProperty(i); 1.3022 + if (prop) { 1.3023 + prop->types.addType(cx, Type::UnknownType()); 1.3024 + prop->types.setNonDataProperty(cx); 1.3025 + } 1.3026 + } 1.3027 +} 1.3028 + 1.3029 +void 1.3030 +TypeObject::clearAddendum(ExclusiveContext *cx) 1.3031 +{ 1.3032 + JS_ASSERT(!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED)); 1.3033 + 1.3034 + addFlags(OBJECT_FLAG_ADDENDUM_CLEARED); 1.3035 + 1.3036 + /* 1.3037 + * It is possible for the object to not have a new script or other 1.3038 + * addendum yet, but to have one added in the future. When 1.3039 + * analyzing properties of new scripts we mix in adding 1.3040 + * constraints to trigger clearNewScript with changes to the type 1.3041 + * sets themselves (from breakTypeBarriers). It is possible that 1.3042 + * we could trigger one of these constraints before 1.3043 + * AnalyzeNewScriptProperties has finished, in which case we want 1.3044 + * to make sure that call fails. 1.3045 + */ 1.3046 + if (!addendum) 1.3047 + return; 1.3048 + 1.3049 + switch (addendum->kind) { 1.3050 + case TypeObjectAddendum::NewScript: 1.3051 + clearNewScriptAddendum(cx); 1.3052 + break; 1.3053 + 1.3054 + case TypeObjectAddendum::TypedObject: 1.3055 + clearTypedObjectAddendum(cx); 1.3056 + break; 1.3057 + } 1.3058 + 1.3059 + /* We nullptr out addendum *before* freeing it so the write barrier works. */ 1.3060 + TypeObjectAddendum *savedAddendum = addendum; 1.3061 + addendum = nullptr; 1.3062 + js_free(savedAddendum); 1.3063 + 1.3064 + markStateChange(cx); 1.3065 +} 1.3066 + 1.3067 +void 1.3068 +TypeObject::clearNewScriptAddendum(ExclusiveContext *cx) 1.3069 +{ 1.3070 + AutoEnterAnalysis enter(cx); 1.3071 + 1.3072 + /* 1.3073 + * Any definite properties we added due to analysis of the new script when 1.3074 + * the type object was created are now invalid: objects with the same type 1.3075 + * can be created by using 'new' on a different script or through some 1.3076 + * other mechanism (e.g. Object.create). Rather than clear out the definite 1.3077 + * bits on the object's properties, just mark such properties as having 1.3078 + * been deleted/reconfigured, which will have the same effect on JITs 1.3079 + * wanting to use the definite bits to optimize property accesses. 1.3080 + */ 1.3081 + for (unsigned i = 0; i < getPropertyCount(); i++) { 1.3082 + Property *prop = getProperty(i); 1.3083 + if (!prop) 1.3084 + continue; 1.3085 + if (prop->types.definiteProperty()) 1.3086 + prop->types.setNonDataProperty(cx); 1.3087 + } 1.3088 + 1.3089 + /* 1.3090 + * If we cleared the new script while in the middle of initializing an 1.3091 + * object, it will still have the new script's shape and reflect the no 1.3092 + * longer correct state of the object once its initialization is completed. 1.3093 + * We can't really detect the possibility of this statically, but the new 1.3094 + * script keeps track of where each property is initialized so we can walk 1.3095 + * the stack and fix up any such objects. 1.3096 + */ 1.3097 + if (cx->isJSContext()) { 1.3098 + Vector<uint32_t, 32> pcOffsets(cx); 1.3099 + for (ScriptFrameIter iter(cx->asJSContext()); !iter.done(); ++iter) { 1.3100 + pcOffsets.append(iter.script()->pcToOffset(iter.pc())); 1.3101 + if (!iter.isConstructing() || 1.3102 + iter.callee() != newScript()->fun || 1.3103 + !iter.thisv().isObject() || 1.3104 + iter.thisv().toObject().hasLazyType() || 1.3105 + iter.thisv().toObject().type() != this) 1.3106 + { 1.3107 + continue; 1.3108 + } 1.3109 + 1.3110 + // Found a matching frame. 1.3111 + RootedObject obj(cx, &iter.thisv().toObject()); 1.3112 + 1.3113 + // Whether all identified 'new' properties have been initialized. 1.3114 + bool finished = false; 1.3115 + 1.3116 + // If not finished, number of properties that have been added. 1.3117 + uint32_t numProperties = 0; 1.3118 + 1.3119 + // Whether the current SETPROP is within an inner frame which has 1.3120 + // finished entirely. 1.3121 + bool pastProperty = false; 1.3122 + 1.3123 + // Index in pcOffsets of the outermost frame. 1.3124 + int callDepth = pcOffsets.length() - 1; 1.3125 + 1.3126 + // Index in pcOffsets of the frame currently being checked for a SETPROP. 1.3127 + int setpropDepth = callDepth; 1.3128 + 1.3129 + for (TypeNewScript::Initializer *init = newScript()->initializerList;; init++) { 1.3130 + if (init->kind == TypeNewScript::Initializer::SETPROP) { 1.3131 + if (!pastProperty && pcOffsets[setpropDepth] < init->offset) { 1.3132 + // Have not yet reached this setprop. 1.3133 + break; 1.3134 + } 1.3135 + // This setprop has executed, reset state for the next one. 1.3136 + numProperties++; 1.3137 + pastProperty = false; 1.3138 + setpropDepth = callDepth; 1.3139 + } else if (init->kind == TypeNewScript::Initializer::SETPROP_FRAME) { 1.3140 + if (!pastProperty) { 1.3141 + if (pcOffsets[setpropDepth] < init->offset) { 1.3142 + // Have not yet reached this inner call. 1.3143 + break; 1.3144 + } else if (pcOffsets[setpropDepth] > init->offset) { 1.3145 + // Have advanced past this inner call. 1.3146 + pastProperty = true; 1.3147 + } else if (setpropDepth == 0) { 1.3148 + // Have reached this call but not yet in it. 1.3149 + break; 1.3150 + } else { 1.3151 + // Somewhere inside this inner call. 1.3152 + setpropDepth--; 1.3153 + } 1.3154 + } 1.3155 + } else { 1.3156 + JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE); 1.3157 + finished = true; 1.3158 + break; 1.3159 + } 1.3160 + } 1.3161 + 1.3162 + if (!finished) 1.3163 + (void) JSObject::rollbackProperties(cx, obj, numProperties); 1.3164 + } 1.3165 + } else { 1.3166 + // Threads with an ExclusiveContext are not allowed to run scripts. 1.3167 + JS_ASSERT(!cx->perThreadData->activation()); 1.3168 + } 1.3169 +} 1.3170 + 1.3171 +void 1.3172 +TypeObject::maybeClearNewScriptAddendumOnOOM() 1.3173 +{ 1.3174 + if (!isMarked()) 1.3175 + return; 1.3176 + 1.3177 + if (!addendum || addendum->kind != TypeObjectAddendum::NewScript) 1.3178 + return; 1.3179 + 1.3180 + for (unsigned i = 0; i < getPropertyCount(); i++) { 1.3181 + Property *prop = getProperty(i); 1.3182 + if (!prop) 1.3183 + continue; 1.3184 + if (prop->types.definiteProperty()) 1.3185 + prop->types.setNonDataPropertyIgnoringConstraints(); 1.3186 + } 1.3187 + 1.3188 + // This method is called during GC sweeping, so there is no write barrier 1.3189 + // that needs to be triggered. 1.3190 + js_free(addendum); 1.3191 + addendum.unsafeSet(nullptr); 1.3192 +} 1.3193 + 1.3194 +void 1.3195 +TypeObject::clearTypedObjectAddendum(ExclusiveContext *cx) 1.3196 +{ 1.3197 +} 1.3198 + 1.3199 +void 1.3200 +TypeObject::print() 1.3201 +{ 1.3202 + TaggedProto tagged(proto()); 1.3203 + fprintf(stderr, "%s : %s", 1.3204 + TypeObjectString(this), 1.3205 + tagged.isObject() ? TypeString(Type::ObjectType(tagged.toObject())) 1.3206 + : (tagged.isLazy() ? "(lazy)" : "(null)")); 1.3207 + 1.3208 + if (unknownProperties()) { 1.3209 + fprintf(stderr, " unknown"); 1.3210 + } else { 1.3211 + if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES)) 1.3212 + fprintf(stderr, " dense"); 1.3213 + if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED)) 1.3214 + fprintf(stderr, " packed"); 1.3215 + if (!hasAnyFlags(OBJECT_FLAG_LENGTH_OVERFLOW)) 1.3216 + fprintf(stderr, " noLengthOverflow"); 1.3217 + if (hasAnyFlags(OBJECT_FLAG_ITERATED)) 1.3218 + fprintf(stderr, " iterated"); 1.3219 + if (interpretedFunction) 1.3220 + fprintf(stderr, " ifun"); 1.3221 + } 1.3222 + 1.3223 + unsigned count = getPropertyCount(); 1.3224 + 1.3225 + if (count == 0) { 1.3226 + fprintf(stderr, " {}\n"); 1.3227 + return; 1.3228 + } 1.3229 + 1.3230 + fprintf(stderr, " {"); 1.3231 + 1.3232 + for (unsigned i = 0; i < count; i++) { 1.3233 + Property *prop = getProperty(i); 1.3234 + if (prop) { 1.3235 + fprintf(stderr, "\n %s:", TypeIdString(prop->id)); 1.3236 + prop->types.print(); 1.3237 + } 1.3238 + } 1.3239 + 1.3240 + fprintf(stderr, "\n}\n"); 1.3241 +} 1.3242 + 1.3243 +///////////////////////////////////////////////////////////////////// 1.3244 +// Type Analysis 1.3245 +///////////////////////////////////////////////////////////////////// 1.3246 + 1.3247 +/* 1.3248 + * Persistent constraint clearing out newScript and definite properties from 1.3249 + * an object should a property on another object get a getter or setter. 1.3250 + */ 1.3251 +class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint 1.3252 +{ 1.3253 + public: 1.3254 + TypeObject *object; 1.3255 + 1.3256 + TypeConstraintClearDefiniteGetterSetter(TypeObject *object) 1.3257 + : object(object) 1.3258 + {} 1.3259 + 1.3260 + const char *kind() { return "clearDefiniteGetterSetter"; } 1.3261 + 1.3262 + void newPropertyState(JSContext *cx, TypeSet *source) 1.3263 + { 1.3264 + if (!object->hasNewScript()) 1.3265 + return; 1.3266 + /* 1.3267 + * Clear out the newScript shape and definite property information from 1.3268 + * an object if the source type set could be a setter or could be 1.3269 + * non-writable. 1.3270 + */ 1.3271 + if (!(object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED) && 1.3272 + (source->nonDataProperty() || source->nonWritableProperty())) 1.3273 + { 1.3274 + object->clearAddendum(cx); 1.3275 + } 1.3276 + } 1.3277 + 1.3278 + void newType(JSContext *cx, TypeSet *source, Type type) {} 1.3279 + 1.3280 + bool sweep(TypeZone &zone, TypeConstraint **res) { 1.3281 + if (IsTypeObjectAboutToBeFinalized(&object)) 1.3282 + return false; 1.3283 + *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object); 1.3284 + return true; 1.3285 + } 1.3286 +}; 1.3287 + 1.3288 +bool 1.3289 +types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, HandleId id) 1.3290 +{ 1.3291 + /* 1.3292 + * Ensure that if the properties named here could have a getter, setter or 1.3293 + * a permanent property in any transitive prototype, the definite 1.3294 + * properties get cleared from the type. 1.3295 + */ 1.3296 + RootedObject parent(cx, type->proto().toObjectOrNull()); 1.3297 + while (parent) { 1.3298 + TypeObject *parentObject = parent->getType(cx); 1.3299 + if (!parentObject || parentObject->unknownProperties()) 1.3300 + return false; 1.3301 + HeapTypeSet *parentTypes = parentObject->getProperty(cx, id); 1.3302 + if (!parentTypes || parentTypes->nonDataProperty() || parentTypes->nonWritableProperty()) 1.3303 + return false; 1.3304 + if (!parentTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(type))) 1.3305 + return false; 1.3306 + parent = parent->getProto(); 1.3307 + } 1.3308 + return true; 1.3309 +} 1.3310 + 1.3311 +/* 1.3312 + * Constraint which clears definite properties on an object should a type set 1.3313 + * contain any types other than a single object. 1.3314 + */ 1.3315 +class TypeConstraintClearDefiniteSingle : public TypeConstraint 1.3316 +{ 1.3317 + public: 1.3318 + TypeObject *object; 1.3319 + 1.3320 + TypeConstraintClearDefiniteSingle(TypeObject *object) 1.3321 + : object(object) 1.3322 + {} 1.3323 + 1.3324 + const char *kind() { return "clearDefiniteSingle"; } 1.3325 + 1.3326 + void newType(JSContext *cx, TypeSet *source, Type type) { 1.3327 + if (object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED) 1.3328 + return; 1.3329 + 1.3330 + if (source->baseFlags() || source->getObjectCount() > 1) 1.3331 + object->clearAddendum(cx); 1.3332 + } 1.3333 + 1.3334 + bool sweep(TypeZone &zone, TypeConstraint **res) { 1.3335 + if (IsTypeObjectAboutToBeFinalized(&object)) 1.3336 + return false; 1.3337 + *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object); 1.3338 + return true; 1.3339 + } 1.3340 +}; 1.3341 + 1.3342 +bool 1.3343 +types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type, 1.3344 + JSScript *script, JSScript *calleeScript) 1.3345 +{ 1.3346 + // Look for any uses of the specified calleeScript in type sets for 1.3347 + // |script|, and add constraints to ensure that if the type sets' contents 1.3348 + // change then the definite properties are cleared from the type. 1.3349 + // This ensures that the inlining performed when the definite properties 1.3350 + // analysis was done is stable. 1.3351 + 1.3352 + TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey(); 1.3353 + 1.3354 + unsigned count = TypeScript::NumTypeSets(script); 1.3355 + StackTypeSet *typeArray = script->types->typeArray(); 1.3356 + 1.3357 + for (unsigned i = 0; i < count; i++) { 1.3358 + StackTypeSet *types = &typeArray[i]; 1.3359 + if (!types->unknownObject() && types->getObjectCount() == 1) { 1.3360 + if (calleeKey != types->getObject(0)) { 1.3361 + // Also check if the object is the Function.call or 1.3362 + // Function.apply native. IonBuilder uses the presence of these 1.3363 + // functions during inlining. 1.3364 + JSObject *singleton = types->getSingleObject(0); 1.3365 + if (!singleton || !singleton->is<JSFunction>()) 1.3366 + continue; 1.3367 + JSFunction *fun = &singleton->as<JSFunction>(); 1.3368 + if (!fun->isNative()) 1.3369 + continue; 1.3370 + if (fun->native() != js_fun_call && fun->native() != js_fun_apply) 1.3371 + continue; 1.3372 + } 1.3373 + // This is a type set that might have been used when inlining 1.3374 + // |calleeScript| into |script|. 1.3375 + if (!types->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type))) 1.3376 + return false; 1.3377 + } 1.3378 + } 1.3379 + 1.3380 + return true; 1.3381 +} 1.3382 + 1.3383 +/* 1.3384 + * Either make the newScript information for type when it is constructed 1.3385 + * by the specified script, or regenerate the constraints for an existing 1.3386 + * newScript on the type after they were cleared by a GC. 1.3387 + */ 1.3388 +static void 1.3389 +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) 1.3390 +{ 1.3391 + JS_ASSERT(cx->compartment()->activeAnalysis); 1.3392 + 1.3393 +#ifdef JS_ION 1.3394 + if (type->unknownProperties()) 1.3395 + return; 1.3396 + 1.3397 + /* Strawman object to add properties to and watch for duplicates. */ 1.3398 + RootedObject baseobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16)); 1.3399 + if (!baseobj) 1.3400 + return; 1.3401 + 1.3402 + Vector<TypeNewScript::Initializer> initializerList(cx); 1.3403 + 1.3404 + if (!jit::AnalyzeNewScriptProperties(cx, fun, type, baseobj, &initializerList) || 1.3405 + baseobj->slotSpan() == 0 || 1.3406 + !!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED)) 1.3407 + { 1.3408 + if (type->hasNewScript()) 1.3409 + type->clearAddendum(cx); 1.3410 + return; 1.3411 + } 1.3412 + 1.3413 + /* 1.3414 + * If the type already has a new script, we are just regenerating the type 1.3415 + * constraints and don't need to make another TypeNewScript. Make sure that 1.3416 + * the properties added to baseobj match the type's definite properties. 1.3417 + */ 1.3418 + if (type->hasNewScript()) { 1.3419 + if (!type->matchDefiniteProperties(baseobj)) 1.3420 + type->clearAddendum(cx); 1.3421 + return; 1.3422 + } 1.3423 + JS_ASSERT(!type->hasNewScript()); 1.3424 + JS_ASSERT(!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED)); 1.3425 + 1.3426 + gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); 1.3427 + 1.3428 + /* We should not have overflowed the maximum number of fixed slots for an object. */ 1.3429 + JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan()); 1.3430 + 1.3431 + TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0); 1.3432 + 1.3433 + /* 1.3434 + * The base object may have been created with a different finalize kind 1.3435 + * than we will use for subsequent new objects. Generate an object with the 1.3436 + * appropriate final shape. 1.3437 + */ 1.3438 + Rooted<TypeObject *> rootedType(cx, type); 1.3439 + RootedShape shape(cx, baseobj->lastProperty()); 1.3440 + baseobj = NewReshapedObject(cx, rootedType, baseobj->getParent(), kind, shape, MaybeSingletonObject); 1.3441 + if (!baseobj || 1.3442 + !type->addDefiniteProperties(cx, baseobj) || 1.3443 + !initializerList.append(done)) 1.3444 + { 1.3445 + return; 1.3446 + } 1.3447 + 1.3448 + size_t numBytes = sizeof(TypeNewScript) 1.3449 + + (initializerList.length() * sizeof(TypeNewScript::Initializer)); 1.3450 + TypeNewScript *newScript = (TypeNewScript *) cx->calloc_(numBytes); 1.3451 + if (!newScript) 1.3452 + return; 1.3453 + 1.3454 + new (newScript) TypeNewScript(); 1.3455 + 1.3456 + type->setAddendum(newScript); 1.3457 + 1.3458 + newScript->fun = fun; 1.3459 + newScript->templateObject = baseobj; 1.3460 + 1.3461 + newScript->initializerList = (TypeNewScript::Initializer *) 1.3462 + ((char *) newScript + sizeof(TypeNewScript)); 1.3463 + PodCopy(newScript->initializerList, 1.3464 + initializerList.begin(), 1.3465 + initializerList.length()); 1.3466 +#endif // JS_ION 1.3467 +} 1.3468 + 1.3469 +///////////////////////////////////////////////////////////////////// 1.3470 +// Interface functions 1.3471 +///////////////////////////////////////////////////////////////////// 1.3472 + 1.3473 +void 1.3474 +types::TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args, 1.3475 + bool constructing) 1.3476 +{ 1.3477 + unsigned nargs = callee->as<JSFunction>().nargs(); 1.3478 + JSScript *script = callee->as<JSFunction>().nonLazyScript(); 1.3479 + 1.3480 + if (!constructing) 1.3481 + TypeScript::SetThis(cx, script, args.thisv()); 1.3482 + 1.3483 + /* 1.3484 + * Add constraints going up to the minimum of the actual and formal count. 1.3485 + * If there are more actuals than formals the later values can only be 1.3486 + * accessed through the arguments object, which is monitored. 1.3487 + */ 1.3488 + unsigned arg = 0; 1.3489 + for (; arg < args.length() && arg < nargs; arg++) 1.3490 + TypeScript::SetArgument(cx, script, arg, args[arg]); 1.3491 + 1.3492 + /* Watch for fewer actuals than formals to the call. */ 1.3493 + for (; arg < nargs; arg++) 1.3494 + TypeScript::SetArgument(cx, script, arg, UndefinedValue()); 1.3495 +} 1.3496 + 1.3497 +static inline bool 1.3498 +IsAboutToBeFinalized(TypeObjectKey *key) 1.3499 +{ 1.3500 + /* Mask out the low bit indicating whether this is a type or JS object. */ 1.3501 + gc::Cell *tmp = reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1); 1.3502 + bool isAboutToBeFinalized = IsCellAboutToBeFinalized(&tmp); 1.3503 + JS_ASSERT(tmp == reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1)); 1.3504 + return isAboutToBeFinalized; 1.3505 +} 1.3506 + 1.3507 +void 1.3508 +types::FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap) 1.3509 +{ 1.3510 + uint32_t added = 0; 1.3511 + for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) { 1.3512 + JSOp op = JSOp(*pc); 1.3513 + if (js_CodeSpec[op].format & JOF_TYPESET) { 1.3514 + bytecodeMap[added++] = script->pcToOffset(pc); 1.3515 + if (added == script->nTypeSets()) 1.3516 + break; 1.3517 + } 1.3518 + } 1.3519 + JS_ASSERT(added == script->nTypeSets()); 1.3520 +} 1.3521 + 1.3522 +void 1.3523 +types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) 1.3524 +{ 1.3525 + /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */ 1.3526 + if (!(js_CodeSpec[*pc].format & JOF_TYPESET)) 1.3527 + return; 1.3528 + 1.3529 + if (!script->hasBaselineScript()) 1.3530 + return; 1.3531 + 1.3532 + AutoEnterAnalysis enter(cx); 1.3533 + 1.3534 + Type type = GetValueType(rval); 1.3535 + StackTypeSet *types = TypeScript::BytecodeTypes(script, pc); 1.3536 + if (types->hasType(type)) 1.3537 + return; 1.3538 + 1.3539 + InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s", 1.3540 + script->id(), script->pcToOffset(pc), TypeString(type)); 1.3541 + types->addType(cx, type); 1.3542 +} 1.3543 + 1.3544 +bool 1.3545 +types::UseNewTypeForClone(JSFunction *fun) 1.3546 +{ 1.3547 + if (!fun->isInterpreted()) 1.3548 + return false; 1.3549 + 1.3550 + if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite()) 1.3551 + return true; 1.3552 + 1.3553 + if (fun->isArrow()) 1.3554 + return false; 1.3555 + 1.3556 + if (fun->hasSingletonType()) 1.3557 + return false; 1.3558 + 1.3559 + /* 1.3560 + * When a function is being used as a wrapper for another function, it 1.3561 + * improves precision greatly to distinguish between different instances of 1.3562 + * the wrapper; otherwise we will conflate much of the information about 1.3563 + * the wrapped functions. 1.3564 + * 1.3565 + * An important example is the Class.create function at the core of the 1.3566 + * Prototype.js library, which looks like: 1.3567 + * 1.3568 + * var Class = { 1.3569 + * create: function() { 1.3570 + * return function() { 1.3571 + * this.initialize.apply(this, arguments); 1.3572 + * } 1.3573 + * } 1.3574 + * }; 1.3575 + * 1.3576 + * Each instance of the innermost function will have a different wrapped 1.3577 + * initialize method. We capture this, along with similar cases, by looking 1.3578 + * for short scripts which use both .apply and arguments. For such scripts, 1.3579 + * whenever creating a new instance of the function we both give that 1.3580 + * instance a singleton type and clone the underlying script. 1.3581 + */ 1.3582 + 1.3583 + uint32_t begin, end; 1.3584 + if (fun->hasScript()) { 1.3585 + if (!fun->nonLazyScript()->usesArgumentsAndApply()) 1.3586 + return false; 1.3587 + begin = fun->nonLazyScript()->sourceStart(); 1.3588 + end = fun->nonLazyScript()->sourceEnd(); 1.3589 + } else { 1.3590 + if (!fun->lazyScript()->usesArgumentsAndApply()) 1.3591 + return false; 1.3592 + begin = fun->lazyScript()->begin(); 1.3593 + end = fun->lazyScript()->end(); 1.3594 + } 1.3595 + 1.3596 + return end - begin <= 100; 1.3597 +} 1.3598 +///////////////////////////////////////////////////////////////////// 1.3599 +// TypeScript 1.3600 +///////////////////////////////////////////////////////////////////// 1.3601 + 1.3602 +bool 1.3603 +JSScript::makeTypes(JSContext *cx) 1.3604 +{ 1.3605 + JS_ASSERT(!types); 1.3606 + 1.3607 + AutoEnterAnalysis enter(cx); 1.3608 + 1.3609 + unsigned count = TypeScript::NumTypeSets(this); 1.3610 + 1.3611 + TypeScript *typeScript = (TypeScript *) 1.3612 + cx->calloc_(TypeScript::SizeIncludingTypeArray(count)); 1.3613 + if (!typeScript) 1.3614 + return false; 1.3615 + 1.3616 + new(typeScript) TypeScript(); 1.3617 + 1.3618 + TypeSet *typeArray = typeScript->typeArray(); 1.3619 + 1.3620 + for (unsigned i = 0; i < count; i++) 1.3621 + new (&typeArray[i]) StackTypeSet(); 1.3622 + 1.3623 + types = typeScript; 1.3624 + 1.3625 +#ifdef DEBUG 1.3626 + for (unsigned i = 0; i < nTypeSets(); i++) { 1.3627 + InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u", 1.3628 + InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(), 1.3629 + i, id()); 1.3630 + } 1.3631 + TypeSet *thisTypes = TypeScript::ThisTypes(this); 1.3632 + InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u", 1.3633 + InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(), 1.3634 + id()); 1.3635 + unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0; 1.3636 + for (unsigned i = 0; i < nargs; i++) { 1.3637 + TypeSet *types = TypeScript::ArgTypes(this, i); 1.3638 + InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u", 1.3639 + InferSpewColor(types), types, InferSpewColorReset(), 1.3640 + i, id()); 1.3641 + } 1.3642 +#endif 1.3643 + 1.3644 + return true; 1.3645 +} 1.3646 + 1.3647 +/* static */ bool 1.3648 +JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun, 1.3649 + bool singleton /* = false */) 1.3650 +{ 1.3651 + if (singleton) { 1.3652 + if (!setSingletonType(cx, fun)) 1.3653 + return false; 1.3654 + } else { 1.3655 + RootedObject funProto(cx, fun->getProto()); 1.3656 + TypeObject *type = 1.3657 + cx->compartment()->types.newTypeObject(cx, &JSFunction::class_, funProto); 1.3658 + if (!type) 1.3659 + return false; 1.3660 + 1.3661 + fun->setType(type); 1.3662 + type->interpretedFunction = fun; 1.3663 + } 1.3664 + 1.3665 + return true; 1.3666 +} 1.3667 + 1.3668 +///////////////////////////////////////////////////////////////////// 1.3669 +// JSObject 1.3670 +///////////////////////////////////////////////////////////////////// 1.3671 + 1.3672 +bool 1.3673 +JSObject::shouldSplicePrototype(JSContext *cx) 1.3674 +{ 1.3675 + /* 1.3676 + * During bootstrapping, if inference is enabled we need to make sure not 1.3677 + * to splice a new prototype in for Function.prototype or the global 1.3678 + * object if their __proto__ had previously been set to null, as this 1.3679 + * will change the prototype for all other objects with the same type. 1.3680 + */ 1.3681 + if (getProto() != nullptr) 1.3682 + return false; 1.3683 + return hasSingletonType(); 1.3684 +} 1.3685 + 1.3686 +bool 1.3687 +JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle<TaggedProto> proto) 1.3688 +{ 1.3689 + JS_ASSERT(cx->compartment() == compartment()); 1.3690 + 1.3691 + RootedObject self(cx, this); 1.3692 + 1.3693 + /* 1.3694 + * For singleton types representing only a single JSObject, the proto 1.3695 + * can be rearranged as needed without destroying type information for 1.3696 + * the old or new types. 1.3697 + */ 1.3698 + JS_ASSERT(self->hasSingletonType()); 1.3699 + 1.3700 + /* Inner objects may not appear on prototype chains. */ 1.3701 + JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); 1.3702 + 1.3703 + /* 1.3704 + * Force type instantiation when splicing lazy types. This may fail, 1.3705 + * in which case inference will be disabled for the compartment. 1.3706 + */ 1.3707 + Rooted<TypeObject*> type(cx, self->getType(cx)); 1.3708 + if (!type) 1.3709 + return false; 1.3710 + Rooted<TypeObject*> protoType(cx, nullptr); 1.3711 + if (proto.isObject()) { 1.3712 + protoType = proto.toObject()->getType(cx); 1.3713 + if (!protoType) 1.3714 + return false; 1.3715 + } 1.3716 + 1.3717 + type->setClasp(clasp); 1.3718 + type->setProto(cx, proto); 1.3719 + return true; 1.3720 +} 1.3721 + 1.3722 +/* static */ TypeObject * 1.3723 +JSObject::makeLazyType(JSContext *cx, HandleObject obj) 1.3724 +{ 1.3725 + JS_ASSERT(obj->hasLazyType()); 1.3726 + JS_ASSERT(cx->compartment() == obj->compartment()); 1.3727 + 1.3728 + /* De-lazification of functions can GC, so we need to do it up here. */ 1.3729 + if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) { 1.3730 + RootedFunction fun(cx, &obj->as<JSFunction>()); 1.3731 + if (!fun->getOrCreateScript(cx)) 1.3732 + return nullptr; 1.3733 + } 1.3734 + 1.3735 + // Find flags which need to be specified immediately on the object. 1.3736 + // Don't track whether singletons are packed. 1.3737 + TypeObjectFlags initialFlags = OBJECT_FLAG_NON_PACKED; 1.3738 + 1.3739 + if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) 1.3740 + initialFlags |= OBJECT_FLAG_ITERATED; 1.3741 + 1.3742 + if (obj->isIndexed()) 1.3743 + initialFlags |= OBJECT_FLAG_SPARSE_INDEXES; 1.3744 + 1.3745 + if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX) 1.3746 + initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; 1.3747 + 1.3748 + Rooted<TaggedProto> proto(cx, obj->getTaggedProto()); 1.3749 + TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto, initialFlags); 1.3750 + if (!type) 1.3751 + return nullptr; 1.3752 + 1.3753 + AutoEnterAnalysis enter(cx); 1.3754 + 1.3755 + /* Fill in the type according to the state of this object. */ 1.3756 + 1.3757 + type->initSingleton(obj); 1.3758 + 1.3759 + if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) 1.3760 + type->interpretedFunction = &obj->as<JSFunction>(); 1.3761 + 1.3762 + obj->type_ = type; 1.3763 + 1.3764 + return type; 1.3765 +} 1.3766 + 1.3767 +/* static */ inline HashNumber 1.3768 +TypeObjectWithNewScriptEntry::hash(const Lookup &lookup) 1.3769 +{ 1.3770 + return PointerHasher<JSObject *, 3>::hash(lookup.hashProto.raw()) ^ 1.3771 + PointerHasher<const Class *, 3>::hash(lookup.clasp) ^ 1.3772 + PointerHasher<JSFunction *, 3>::hash(lookup.newFunction); 1.3773 +} 1.3774 + 1.3775 +/* static */ inline bool 1.3776 +TypeObjectWithNewScriptEntry::match(const TypeObjectWithNewScriptEntry &key, const Lookup &lookup) 1.3777 +{ 1.3778 + return key.object->proto() == lookup.matchProto && 1.3779 + key.object->clasp() == lookup.clasp && 1.3780 + key.newFunction == lookup.newFunction; 1.3781 +} 1.3782 + 1.3783 +#ifdef DEBUG 1.3784 +bool 1.3785 +JSObject::hasNewType(const Class *clasp, TypeObject *type) 1.3786 +{ 1.3787 + TypeObjectWithNewScriptSet &table = compartment()->newTypeObjects; 1.3788 + 1.3789 + if (!table.initialized()) 1.3790 + return false; 1.3791 + 1.3792 + TypeObjectWithNewScriptSet::Ptr p = table.lookup(TypeObjectWithNewScriptSet::Lookup(clasp, this, nullptr)); 1.3793 + return p && p->object == type; 1.3794 +} 1.3795 +#endif /* DEBUG */ 1.3796 + 1.3797 +/* static */ bool 1.3798 +JSObject::setNewTypeUnknown(JSContext *cx, const Class *clasp, HandleObject obj) 1.3799 +{ 1.3800 + if (!obj->setFlag(cx, js::BaseShape::NEW_TYPE_UNKNOWN)) 1.3801 + return false; 1.3802 + 1.3803 + /* 1.3804 + * If the object already has a new type, mark that type as unknown. It will 1.3805 + * not have the SETS_MARKED_UNKNOWN bit set, so may require a type set 1.3806 + * crawl if prototypes of the object change dynamically in the future. 1.3807 + */ 1.3808 + TypeObjectWithNewScriptSet &table = cx->compartment()->newTypeObjects; 1.3809 + if (table.initialized()) { 1.3810 + if (TypeObjectWithNewScriptSet::Ptr p = table.lookup(TypeObjectWithNewScriptSet::Lookup(clasp, obj.get(), nullptr))) 1.3811 + MarkTypeObjectUnknownProperties(cx, p->object); 1.3812 + } 1.3813 + 1.3814 + return true; 1.3815 +} 1.3816 + 1.3817 +#ifdef JSGC_GENERATIONAL 1.3818 +/* 1.3819 + * This class is used to add a post barrier on the newTypeObjects set, as the 1.3820 + * key is calculated from a prototype object which may be moved by generational 1.3821 + * GC. 1.3822 + */ 1.3823 +class NewTypeObjectsSetRef : public BufferableRef 1.3824 +{ 1.3825 + TypeObjectWithNewScriptSet *set; 1.3826 + const Class *clasp; 1.3827 + JSObject *proto; 1.3828 + JSFunction *newFunction; 1.3829 + 1.3830 + public: 1.3831 + NewTypeObjectsSetRef(TypeObjectWithNewScriptSet *s, const Class *clasp, JSObject *proto, JSFunction *newFunction) 1.3832 + : set(s), clasp(clasp), proto(proto), newFunction(newFunction) 1.3833 + {} 1.3834 + 1.3835 + void mark(JSTracer *trc) { 1.3836 + JSObject *prior = proto; 1.3837 + trc->setTracingLocation(&*prior); 1.3838 + Mark(trc, &proto, "newTypeObjects set prototype"); 1.3839 + if (prior == proto) 1.3840 + return; 1.3841 + 1.3842 + TypeObjectWithNewScriptSet::Ptr p = set->lookup(TypeObjectWithNewScriptSet::Lookup(clasp, prior, proto, newFunction)); 1.3843 + JS_ASSERT(p); // newTypeObjects set must still contain original entry. 1.3844 + 1.3845 + set->rekeyAs(TypeObjectWithNewScriptSet::Lookup(clasp, prior, proto, newFunction), 1.3846 + TypeObjectWithNewScriptSet::Lookup(clasp, proto, newFunction), *p); 1.3847 + } 1.3848 +}; 1.3849 +#endif 1.3850 + 1.3851 +TypeObject * 1.3852 +ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction *fun) 1.3853 +{ 1.3854 + JS_ASSERT_IF(fun, proto.isObject()); 1.3855 + JS_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject())); 1.3856 + 1.3857 + TypeObjectWithNewScriptSet &newTypeObjects = compartment()->newTypeObjects; 1.3858 + 1.3859 + if (!newTypeObjects.initialized() && !newTypeObjects.init()) 1.3860 + return nullptr; 1.3861 + 1.3862 + // Canonicalize new functions to use the original one associated with its script. 1.3863 + if (fun) { 1.3864 + if (fun->hasScript()) 1.3865 + fun = fun->nonLazyScript()->functionNonDelazifying(); 1.3866 + else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin()) 1.3867 + fun = fun->lazyScript()->functionNonDelazifying(); 1.3868 + else 1.3869 + fun = nullptr; 1.3870 + } 1.3871 + 1.3872 + TypeObjectWithNewScriptSet::AddPtr p = 1.3873 + newTypeObjects.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, fun)); 1.3874 + if (p) { 1.3875 + TypeObject *type = p->object; 1.3876 + JS_ASSERT(type->clasp() == clasp); 1.3877 + JS_ASSERT(type->proto() == proto); 1.3878 + JS_ASSERT_IF(type->hasNewScript(), type->newScript()->fun == fun); 1.3879 + return type; 1.3880 + } 1.3881 + 1.3882 + AutoEnterAnalysis enter(this); 1.3883 + 1.3884 + if (proto.isObject() && !proto.toObject()->setDelegate(this)) 1.3885 + return nullptr; 1.3886 + 1.3887 + TypeObjectFlags initialFlags = 0; 1.3888 + if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)) { 1.3889 + // The new type is not present in any type sets, so mark the object as 1.3890 + // unknown in all type sets it appears in. This allows the prototype of 1.3891 + // such objects to mutate freely without triggering an expensive walk of 1.3892 + // the compartment's type sets. (While scripts normally don't mutate 1.3893 + // __proto__, the browser will for proxies and such, and we need to 1.3894 + // accommodate this behavior). 1.3895 + initialFlags = OBJECT_FLAG_UNKNOWN_MASK | OBJECT_FLAG_SETS_MARKED_UNKNOWN; 1.3896 + } 1.3897 + 1.3898 + Rooted<TaggedProto> protoRoot(this, proto); 1.3899 + TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags); 1.3900 + if (!type) 1.3901 + return nullptr; 1.3902 + 1.3903 + if (!newTypeObjects.add(p, TypeObjectWithNewScriptEntry(type, fun))) 1.3904 + return nullptr; 1.3905 + 1.3906 +#ifdef JSGC_GENERATIONAL 1.3907 + if (proto.isObject() && hasNursery() && nursery().isInside(proto.toObject())) { 1.3908 + asJSContext()->runtime()->gcStoreBuffer.putGeneric( 1.3909 + NewTypeObjectsSetRef(&newTypeObjects, clasp, proto.toObject(), fun)); 1.3910 + } 1.3911 +#endif 1.3912 + 1.3913 + if (proto.isObject()) { 1.3914 + RootedObject obj(this, proto.toObject()); 1.3915 + 1.3916 + if (fun) 1.3917 + CheckNewScriptProperties(asJSContext(), type, fun); 1.3918 + 1.3919 + /* 1.3920 + * Some builtin objects have slotful native properties baked in at 1.3921 + * creation via the Shape::{insert,get}initialShape mechanism. Since 1.3922 + * these properties are never explicitly defined on new objects, update 1.3923 + * the type information for them here. 1.3924 + */ 1.3925 + 1.3926 + if (obj->is<RegExpObject>()) { 1.3927 + AddTypePropertyId(this, type, NameToId(names().source), Type::StringType()); 1.3928 + AddTypePropertyId(this, type, NameToId(names().global), Type::BooleanType()); 1.3929 + AddTypePropertyId(this, type, NameToId(names().ignoreCase), Type::BooleanType()); 1.3930 + AddTypePropertyId(this, type, NameToId(names().multiline), Type::BooleanType()); 1.3931 + AddTypePropertyId(this, type, NameToId(names().sticky), Type::BooleanType()); 1.3932 + AddTypePropertyId(this, type, NameToId(names().lastIndex), Type::Int32Type()); 1.3933 + } 1.3934 + 1.3935 + if (obj->is<StringObject>()) 1.3936 + AddTypePropertyId(this, type, NameToId(names().length), Type::Int32Type()); 1.3937 + 1.3938 + if (obj->is<ErrorObject>()) { 1.3939 + AddTypePropertyId(this, type, NameToId(names().fileName), Type::StringType()); 1.3940 + AddTypePropertyId(this, type, NameToId(names().lineNumber), Type::Int32Type()); 1.3941 + AddTypePropertyId(this, type, NameToId(names().columnNumber), Type::Int32Type()); 1.3942 + AddTypePropertyId(this, type, NameToId(names().stack), Type::StringType()); 1.3943 + } 1.3944 + } 1.3945 + 1.3946 + return type; 1.3947 +} 1.3948 + 1.3949 +#if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL) 1.3950 +void 1.3951 +JSCompartment::checkNewTypeObjectTableAfterMovingGC() 1.3952 +{ 1.3953 + /* 1.3954 + * Assert that the postbarriers have worked and that nothing is left in 1.3955 + * newTypeObjects that points into the nursery, and that the hash table 1.3956 + * entries are discoverable. 1.3957 + */ 1.3958 + JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtimeFromMainThread()); 1.3959 + for (TypeObjectWithNewScriptSet::Enum e(newTypeObjects); !e.empty(); e.popFront()) { 1.3960 + TypeObjectWithNewScriptEntry entry = e.front(); 1.3961 + JS_ASSERT(!IsInsideNursery(rt, entry.newFunction)); 1.3962 + TaggedProto proto = entry.object->proto(); 1.3963 + JS_ASSERT_IF(proto.isObject(), !IsInsideNursery(rt, proto.toObject())); 1.3964 + TypeObjectWithNewScriptEntry::Lookup 1.3965 + lookup(entry.object->clasp(), proto, entry.newFunction); 1.3966 + TypeObjectWithNewScriptSet::Ptr ptr = newTypeObjects.lookup(lookup); 1.3967 + JS_ASSERT(ptr.found() && &*ptr == &e.front()); 1.3968 + } 1.3969 +} 1.3970 +#endif 1.3971 + 1.3972 +TypeObject * 1.3973 +ExclusiveContext::getSingletonType(const Class *clasp, TaggedProto proto) 1.3974 +{ 1.3975 + JS_ASSERT_IF(proto.isObject(), compartment() == proto.toObject()->compartment()); 1.3976 + 1.3977 + AutoEnterAnalysis enter(this); 1.3978 + 1.3979 + TypeObjectWithNewScriptSet &table = compartment()->lazyTypeObjects; 1.3980 + 1.3981 + if (!table.initialized() && !table.init()) 1.3982 + return nullptr; 1.3983 + 1.3984 + TypeObjectWithNewScriptSet::AddPtr p = table.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, nullptr)); 1.3985 + if (p) { 1.3986 + TypeObject *type = p->object; 1.3987 + JS_ASSERT(type->lazy()); 1.3988 + 1.3989 + return type; 1.3990 + } 1.3991 + 1.3992 + Rooted<TaggedProto> protoRoot(this, proto); 1.3993 + TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot); 1.3994 + if (!type) 1.3995 + return nullptr; 1.3996 + 1.3997 + if (!table.add(p, TypeObjectWithNewScriptEntry(type, nullptr))) 1.3998 + return nullptr; 1.3999 + 1.4000 + type->initSingleton((JSObject *) TypeObject::LAZY_SINGLETON); 1.4001 + MOZ_ASSERT(type->singleton(), "created type must be a proper singleton"); 1.4002 + 1.4003 + return type; 1.4004 +} 1.4005 + 1.4006 +///////////////////////////////////////////////////////////////////// 1.4007 +// Tracing 1.4008 +///////////////////////////////////////////////////////////////////// 1.4009 + 1.4010 +void 1.4011 +ConstraintTypeSet::sweep(Zone *zone, bool *oom) 1.4012 +{ 1.4013 + /* 1.4014 + * Purge references to type objects that are no longer live. Type sets hold 1.4015 + * only weak references. For type sets containing more than one object, 1.4016 + * live entries in the object hash need to be copied to the zone's 1.4017 + * new arena. 1.4018 + */ 1.4019 + unsigned objectCount = baseObjectCount(); 1.4020 + if (objectCount >= 2) { 1.4021 + unsigned oldCapacity = HashSetCapacity(objectCount); 1.4022 + TypeObjectKey **oldArray = objectSet; 1.4023 + 1.4024 + clearObjects(); 1.4025 + objectCount = 0; 1.4026 + for (unsigned i = 0; i < oldCapacity; i++) { 1.4027 + TypeObjectKey *object = oldArray[i]; 1.4028 + if (object && !IsAboutToBeFinalized(object)) { 1.4029 + TypeObjectKey **pentry = 1.4030 + HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey> 1.4031 + (zone->types.typeLifoAlloc, objectSet, objectCount, object); 1.4032 + if (pentry) { 1.4033 + *pentry = object; 1.4034 + } else { 1.4035 + *oom = true; 1.4036 + flags |= TYPE_FLAG_ANYOBJECT; 1.4037 + clearObjects(); 1.4038 + objectCount = 0; 1.4039 + break; 1.4040 + } 1.4041 + } 1.4042 + } 1.4043 + setBaseObjectCount(objectCount); 1.4044 + } else if (objectCount == 1) { 1.4045 + TypeObjectKey *object = (TypeObjectKey *) objectSet; 1.4046 + if (IsAboutToBeFinalized(object)) { 1.4047 + objectSet = nullptr; 1.4048 + setBaseObjectCount(0); 1.4049 + } 1.4050 + } 1.4051 + 1.4052 + /* 1.4053 + * Type constraints only hold weak references. Copy constraints referring 1.4054 + * to data that is still live into the zone's new arena. 1.4055 + */ 1.4056 + TypeConstraint *constraint = constraintList; 1.4057 + constraintList = nullptr; 1.4058 + while (constraint) { 1.4059 + TypeConstraint *copy; 1.4060 + if (constraint->sweep(zone->types, ©)) { 1.4061 + if (copy) { 1.4062 + copy->next = constraintList; 1.4063 + constraintList = copy; 1.4064 + } else { 1.4065 + *oom = true; 1.4066 + } 1.4067 + } 1.4068 + constraint = constraint->next; 1.4069 + } 1.4070 +} 1.4071 + 1.4072 +inline void 1.4073 +TypeObject::clearProperties() 1.4074 +{ 1.4075 + setBasePropertyCount(0); 1.4076 + propertySet = nullptr; 1.4077 +} 1.4078 + 1.4079 +/* 1.4080 + * Before sweeping the arenas themselves, scan all type objects in a 1.4081 + * compartment to fixup weak references: property type sets referencing dead 1.4082 + * JS and type objects, and singleton JS objects whose type is not referenced 1.4083 + * elsewhere. This also releases memory associated with dead type objects, 1.4084 + * so that type objects do not need later finalization. 1.4085 + */ 1.4086 +inline void 1.4087 +TypeObject::sweep(FreeOp *fop, bool *oom) 1.4088 +{ 1.4089 + if (!isMarked()) { 1.4090 + if (addendum) 1.4091 + fop->free_(addendum); 1.4092 + return; 1.4093 + } 1.4094 + 1.4095 + LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc; 1.4096 + 1.4097 + /* 1.4098 + * Properties were allocated from the old arena, and need to be copied over 1.4099 + * to the new one. 1.4100 + */ 1.4101 + unsigned propertyCount = basePropertyCount(); 1.4102 + if (propertyCount >= 2) { 1.4103 + unsigned oldCapacity = HashSetCapacity(propertyCount); 1.4104 + Property **oldArray = propertySet; 1.4105 + 1.4106 + clearProperties(); 1.4107 + propertyCount = 0; 1.4108 + for (unsigned i = 0; i < oldCapacity; i++) { 1.4109 + Property *prop = oldArray[i]; 1.4110 + if (prop) { 1.4111 + if (singleton() && !prop->types.constraintList && !zone()->isPreservingCode()) { 1.4112 + /* 1.4113 + * Don't copy over properties of singleton objects when their 1.4114 + * presence will not be required by jitcode or type constraints 1.4115 + * (i.e. for the definite properties analysis). The contents of 1.4116 + * these type sets will be regenerated as necessary. 1.4117 + */ 1.4118 + continue; 1.4119 + } 1.4120 + 1.4121 + Property *newProp = typeLifoAlloc.new_<Property>(*prop); 1.4122 + if (newProp) { 1.4123 + Property **pentry = 1.4124 + HashSetInsert<jsid,Property,Property> 1.4125 + (typeLifoAlloc, propertySet, propertyCount, prop->id); 1.4126 + if (pentry) { 1.4127 + *pentry = newProp; 1.4128 + newProp->types.sweep(zone(), oom); 1.4129 + continue; 1.4130 + } 1.4131 + } 1.4132 + 1.4133 + *oom = true; 1.4134 + addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); 1.4135 + clearProperties(); 1.4136 + return; 1.4137 + } 1.4138 + } 1.4139 + setBasePropertyCount(propertyCount); 1.4140 + } else if (propertyCount == 1) { 1.4141 + Property *prop = (Property *) propertySet; 1.4142 + if (singleton() && !prop->types.constraintList && !zone()->isPreservingCode()) { 1.4143 + // Skip, as above. 1.4144 + clearProperties(); 1.4145 + } else { 1.4146 + Property *newProp = typeLifoAlloc.new_<Property>(*prop); 1.4147 + if (newProp) { 1.4148 + propertySet = (Property **) newProp; 1.4149 + newProp->types.sweep(zone(), oom); 1.4150 + } else { 1.4151 + *oom = true; 1.4152 + addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); 1.4153 + clearProperties(); 1.4154 + return; 1.4155 + } 1.4156 + } 1.4157 + } 1.4158 +} 1.4159 + 1.4160 +void 1.4161 +TypeCompartment::clearTables() 1.4162 +{ 1.4163 + if (allocationSiteTable && allocationSiteTable->initialized()) 1.4164 + allocationSiteTable->clear(); 1.4165 + if (arrayTypeTable && arrayTypeTable->initialized()) 1.4166 + arrayTypeTable->clear(); 1.4167 + if (objectTypeTable && objectTypeTable->initialized()) 1.4168 + objectTypeTable->clear(); 1.4169 +} 1.4170 + 1.4171 +void 1.4172 +TypeCompartment::sweep(FreeOp *fop) 1.4173 +{ 1.4174 + /* 1.4175 + * Iterate through the array/object type tables and remove all entries 1.4176 + * referencing collected data. These tables only hold weak references. 1.4177 + */ 1.4178 + 1.4179 + if (arrayTypeTable) { 1.4180 + for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) { 1.4181 + const ArrayTableKey &key = e.front().key(); 1.4182 + JS_ASSERT(key.type.isUnknown() || !key.type.isSingleObject()); 1.4183 + 1.4184 + bool remove = false; 1.4185 + TypeObject *typeObject = nullptr; 1.4186 + if (!key.type.isUnknown() && key.type.isTypeObject()) { 1.4187 + typeObject = key.type.typeObject(); 1.4188 + if (IsTypeObjectAboutToBeFinalized(&typeObject)) 1.4189 + remove = true; 1.4190 + } 1.4191 + if (IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet())) 1.4192 + remove = true; 1.4193 + 1.4194 + if (remove) { 1.4195 + e.removeFront(); 1.4196 + } else if (typeObject && typeObject != key.type.typeObject()) { 1.4197 + ArrayTableKey newKey; 1.4198 + newKey.type = Type::ObjectType(typeObject); 1.4199 + newKey.proto = key.proto; 1.4200 + e.rekeyFront(newKey); 1.4201 + } 1.4202 + } 1.4203 + } 1.4204 + 1.4205 + if (objectTypeTable) { 1.4206 + for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) { 1.4207 + const ObjectTableKey &key = e.front().key(); 1.4208 + ObjectTableEntry &entry = e.front().value(); 1.4209 + 1.4210 + bool remove = false; 1.4211 + if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet())) 1.4212 + remove = true; 1.4213 + if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) 1.4214 + remove = true; 1.4215 + for (unsigned i = 0; !remove && i < key.nproperties; i++) { 1.4216 + if (JSID_IS_STRING(key.properties[i])) { 1.4217 + JSString *str = JSID_TO_STRING(key.properties[i]); 1.4218 + if (IsStringAboutToBeFinalized(&str)) 1.4219 + remove = true; 1.4220 + JS_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]); 1.4221 + } 1.4222 + JS_ASSERT(!entry.types[i].isSingleObject()); 1.4223 + TypeObject *typeObject = nullptr; 1.4224 + if (entry.types[i].isTypeObject()) { 1.4225 + typeObject = entry.types[i].typeObject(); 1.4226 + if (IsTypeObjectAboutToBeFinalized(&typeObject)) 1.4227 + remove = true; 1.4228 + else if (typeObject != entry.types[i].typeObject()) 1.4229 + entry.types[i] = Type::ObjectType(typeObject); 1.4230 + } 1.4231 + } 1.4232 + 1.4233 + if (remove) { 1.4234 + js_free(key.properties); 1.4235 + js_free(entry.types); 1.4236 + e.removeFront(); 1.4237 + } 1.4238 + } 1.4239 + } 1.4240 + 1.4241 + if (allocationSiteTable) { 1.4242 + for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { 1.4243 + AllocationSiteKey key = e.front().key(); 1.4244 + bool keyDying = IsScriptAboutToBeFinalized(&key.script); 1.4245 + bool valDying = IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet()); 1.4246 + if (keyDying || valDying) 1.4247 + e.removeFront(); 1.4248 + else if (key.script != e.front().key().script) 1.4249 + e.rekeyFront(key); 1.4250 + } 1.4251 + } 1.4252 +} 1.4253 + 1.4254 +void 1.4255 +JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table) 1.4256 +{ 1.4257 + gcstats::AutoPhase ap(runtimeFromMainThread()->gcStats, 1.4258 + gcstats::PHASE_SWEEP_TABLES_TYPE_OBJECT); 1.4259 + 1.4260 + JS_ASSERT(zone()->isGCSweeping()); 1.4261 + if (table.initialized()) { 1.4262 + for (TypeObjectWithNewScriptSet::Enum e(table); !e.empty(); e.popFront()) { 1.4263 + TypeObjectWithNewScriptEntry entry = e.front(); 1.4264 + if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet())) { 1.4265 + e.removeFront(); 1.4266 + } else if (entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)) { 1.4267 + e.removeFront(); 1.4268 + } else if (entry.object != e.front().object) { 1.4269 + TypeObjectWithNewScriptSet::Lookup lookup(entry.object->clasp(), 1.4270 + entry.object->proto(), 1.4271 + entry.newFunction); 1.4272 + e.rekeyFront(lookup, entry); 1.4273 + } 1.4274 + } 1.4275 + } 1.4276 +} 1.4277 + 1.4278 +TypeCompartment::~TypeCompartment() 1.4279 +{ 1.4280 + js_delete(arrayTypeTable); 1.4281 + js_delete(objectTypeTable); 1.4282 + js_delete(allocationSiteTable); 1.4283 +} 1.4284 + 1.4285 +/* static */ void 1.4286 +TypeScript::Sweep(FreeOp *fop, JSScript *script, bool *oom) 1.4287 +{ 1.4288 + JSCompartment *compartment = script->compartment(); 1.4289 + JS_ASSERT(compartment->zone()->isGCSweeping()); 1.4290 + 1.4291 + unsigned num = NumTypeSets(script); 1.4292 + StackTypeSet *typeArray = script->types->typeArray(); 1.4293 + 1.4294 + /* Remove constraints and references to dead objects from the persistent type sets. */ 1.4295 + for (unsigned i = 0; i < num; i++) 1.4296 + typeArray[i].sweep(compartment->zone(), oom); 1.4297 +} 1.4298 + 1.4299 +void 1.4300 +TypeScript::destroy() 1.4301 +{ 1.4302 + js_free(this); 1.4303 +} 1.4304 + 1.4305 +void 1.4306 +Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, 1.4307 + size_t *typePool, 1.4308 + size_t *baselineStubsOptimized) 1.4309 +{ 1.4310 + *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf); 1.4311 +#ifdef JS_ION 1.4312 + if (jitZone()) { 1.4313 + *baselineStubsOptimized += 1.4314 + jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf); 1.4315 + } 1.4316 +#endif 1.4317 +} 1.4318 + 1.4319 +void 1.4320 +TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, 1.4321 + size_t *allocationSiteTables, 1.4322 + size_t *arrayTypeTables, 1.4323 + size_t *objectTypeTables) 1.4324 +{ 1.4325 + if (allocationSiteTable) 1.4326 + *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); 1.4327 + 1.4328 + if (arrayTypeTable) 1.4329 + *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf); 1.4330 + 1.4331 + if (objectTypeTable) { 1.4332 + *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf); 1.4333 + 1.4334 + for (ObjectTypeTable::Enum e(*objectTypeTable); 1.4335 + !e.empty(); 1.4336 + e.popFront()) 1.4337 + { 1.4338 + const ObjectTableKey &key = e.front().key(); 1.4339 + const ObjectTableEntry &value = e.front().value(); 1.4340 + 1.4341 + /* key.ids and values.types have the same length. */ 1.4342 + *objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types); 1.4343 + } 1.4344 + } 1.4345 +} 1.4346 + 1.4347 +size_t 1.4348 +TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.4349 +{ 1.4350 + return mallocSizeOf(addendum); 1.4351 +} 1.4352 + 1.4353 +TypeZone::TypeZone(Zone *zone) 1.4354 + : zone_(zone), 1.4355 + typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), 1.4356 + compilerOutputs(nullptr), 1.4357 + pendingRecompiles(nullptr) 1.4358 +{ 1.4359 +} 1.4360 + 1.4361 +TypeZone::~TypeZone() 1.4362 +{ 1.4363 + js_delete(compilerOutputs); 1.4364 + js_delete(pendingRecompiles); 1.4365 +} 1.4366 + 1.4367 +void 1.4368 +TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) 1.4369 +{ 1.4370 + JS_ASSERT(zone()->isGCSweeping()); 1.4371 + 1.4372 + JSRuntime *rt = fop->runtime(); 1.4373 + 1.4374 + /* 1.4375 + * Clear the analysis pool, but don't release its data yet. While 1.4376 + * sweeping types any live data will be allocated into the pool. 1.4377 + */ 1.4378 + LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); 1.4379 + oldAlloc.steal(&typeLifoAlloc); 1.4380 + 1.4381 + /* Sweep and find compressed indexes for each compiler output. */ 1.4382 + size_t newCompilerOutputCount = 0; 1.4383 + 1.4384 +#ifdef JS_ION 1.4385 + if (compilerOutputs) { 1.4386 + for (size_t i = 0; i < compilerOutputs->length(); i++) { 1.4387 + CompilerOutput &output = (*compilerOutputs)[i]; 1.4388 + if (output.isValid()) { 1.4389 + JSScript *script = output.script(); 1.4390 + if (IsScriptAboutToBeFinalized(&script)) { 1.4391 + jit::GetIonScript(script, output.mode())->recompileInfoRef() = uint32_t(-1); 1.4392 + output.invalidate(); 1.4393 + } else { 1.4394 + output.setSweepIndex(newCompilerOutputCount++); 1.4395 + } 1.4396 + } 1.4397 + } 1.4398 + } 1.4399 +#endif 1.4400 + 1.4401 + { 1.4402 + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI); 1.4403 + 1.4404 + for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) { 1.4405 + JSScript *script = i.get<JSScript>(); 1.4406 + if (script->types) { 1.4407 + types::TypeScript::Sweep(fop, script, oom); 1.4408 + 1.4409 + if (releaseTypes) { 1.4410 + if (script->hasParallelIonScript()) { 1.4411 +#ifdef JS_ION 1.4412 + // It's possible that we preserved the parallel 1.4413 + // IonScript. The heuristic for their preservation is 1.4414 + // independent of general JIT code preservation. 1.4415 + MOZ_ASSERT(jit::ShouldPreserveParallelJITCode(rt, script)); 1.4416 + script->parallelIonScript()->recompileInfoRef().shouldSweep(*this); 1.4417 +#else 1.4418 + MOZ_CRASH(); 1.4419 +#endif 1.4420 + } else { 1.4421 + script->types->destroy(); 1.4422 + script->types = nullptr; 1.4423 + 1.4424 + /* 1.4425 + * Freeze constraints on stack type sets need to be 1.4426 + * regenerated the next time the script is analyzed. 1.4427 + */ 1.4428 + script->clearHasFreezeConstraints(); 1.4429 + } 1.4430 + 1.4431 + JS_ASSERT(!script->hasIonScript()); 1.4432 + } else { 1.4433 + /* Update the recompile indexes in any IonScripts still on the script. */ 1.4434 + if (script->hasIonScript()) 1.4435 + script->ionScript()->recompileInfoRef().shouldSweep(*this); 1.4436 + if (script->hasParallelIonScript()) 1.4437 + script->parallelIonScript()->recompileInfoRef().shouldSweep(*this); 1.4438 + } 1.4439 + } 1.4440 + } 1.4441 + } 1.4442 + 1.4443 + { 1.4444 + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES); 1.4445 + 1.4446 + for (gc::CellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT); 1.4447 + !iter.done(); iter.next()) 1.4448 + { 1.4449 + TypeObject *object = iter.get<TypeObject>(); 1.4450 + object->sweep(fop, oom); 1.4451 + } 1.4452 + 1.4453 + for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) 1.4454 + comp->types.sweep(fop); 1.4455 + } 1.4456 + 1.4457 + if (compilerOutputs) { 1.4458 + size_t sweepIndex = 0; 1.4459 + for (size_t i = 0; i < compilerOutputs->length(); i++) { 1.4460 + CompilerOutput output = (*compilerOutputs)[i]; 1.4461 + if (output.isValid()) { 1.4462 + JS_ASSERT(sweepIndex == output.sweepIndex()); 1.4463 + output.invalidateSweepIndex(); 1.4464 + (*compilerOutputs)[sweepIndex++] = output; 1.4465 + } 1.4466 + } 1.4467 + JS_ASSERT(sweepIndex == newCompilerOutputCount); 1.4468 + JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount)); 1.4469 + } 1.4470 + 1.4471 + { 1.4472 + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA); 1.4473 + rt->freeLifoAlloc.transferFrom(&oldAlloc); 1.4474 + } 1.4475 +} 1.4476 + 1.4477 +void 1.4478 +TypeZone::clearAllNewScriptAddendumsOnOOM() 1.4479 +{ 1.4480 + for (gc::CellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT); 1.4481 + !iter.done(); iter.next()) 1.4482 + { 1.4483 + TypeObject *object = iter.get<TypeObject>(); 1.4484 + object->maybeClearNewScriptAddendumOnOOM(); 1.4485 + } 1.4486 +} 1.4487 + 1.4488 +#ifdef DEBUG 1.4489 +void 1.4490 +TypeScript::printTypes(JSContext *cx, HandleScript script) const 1.4491 +{ 1.4492 + JS_ASSERT(script->types == this); 1.4493 + 1.4494 + if (!script->hasBaselineScript()) 1.4495 + return; 1.4496 + 1.4497 + AutoEnterAnalysis enter(nullptr, script->compartment()); 1.4498 + 1.4499 + if (script->functionNonDelazifying()) 1.4500 + fprintf(stderr, "Function"); 1.4501 + else if (script->isForEval()) 1.4502 + fprintf(stderr, "Eval"); 1.4503 + else 1.4504 + fprintf(stderr, "Main"); 1.4505 + fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno()); 1.4506 + 1.4507 + if (script->functionNonDelazifying()) { 1.4508 + if (js::PropertyName *name = script->functionNonDelazifying()->name()) { 1.4509 + const jschar *chars = name->getChars(nullptr); 1.4510 + JSString::dumpChars(chars, name->length()); 1.4511 + } 1.4512 + } 1.4513 + 1.4514 + fprintf(stderr, "\n this:"); 1.4515 + TypeScript::ThisTypes(script)->print(); 1.4516 + 1.4517 + for (unsigned i = 0; 1.4518 + script->functionNonDelazifying() && i < script->functionNonDelazifying()->nargs(); 1.4519 + i++) 1.4520 + { 1.4521 + fprintf(stderr, "\n arg%u:", i); 1.4522 + TypeScript::ArgTypes(script, i)->print(); 1.4523 + } 1.4524 + fprintf(stderr, "\n"); 1.4525 + 1.4526 + for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) { 1.4527 + PrintBytecode(cx, script, pc); 1.4528 + 1.4529 + if (js_CodeSpec[*pc].format & JOF_TYPESET) { 1.4530 + StackTypeSet *types = TypeScript::BytecodeTypes(script, pc); 1.4531 + fprintf(stderr, " typeset %u:", unsigned(types - typeArray())); 1.4532 + types->print(); 1.4533 + fprintf(stderr, "\n"); 1.4534 + } 1.4535 + } 1.4536 + 1.4537 + fprintf(stderr, "\n"); 1.4538 +} 1.4539 +#endif /* DEBUG */ 1.4540 + 1.4541 +///////////////////////////////////////////////////////////////////// 1.4542 +// Binary data 1.4543 +///////////////////////////////////////////////////////////////////// 1.4544 + 1.4545 +void 1.4546 +TypeObject::setAddendum(TypeObjectAddendum *addendum) 1.4547 +{ 1.4548 + this->addendum = addendum; 1.4549 +} 1.4550 + 1.4551 +bool 1.4552 +TypeObject::addTypedObjectAddendum(JSContext *cx, Handle<TypeDescr*> descr) 1.4553 +{ 1.4554 + // Type descriptors are always pre-tenured. This is both because 1.4555 + // we expect them to live a long time and so that they can be 1.4556 + // safely accessed during ion compilation. 1.4557 + JS_ASSERT(!IsInsideNursery(cx->runtime(), descr)); 1.4558 + JS_ASSERT(descr); 1.4559 + 1.4560 + if (flags() & OBJECT_FLAG_ADDENDUM_CLEARED) 1.4561 + return true; 1.4562 + 1.4563 + JS_ASSERT(!unknownProperties()); 1.4564 + 1.4565 + if (addendum) { 1.4566 + JS_ASSERT(hasTypedObject()); 1.4567 + JS_ASSERT(&typedObject()->descr() == descr); 1.4568 + return true; 1.4569 + } 1.4570 + 1.4571 + TypeTypedObject *typedObject = js_new<TypeTypedObject>(descr); 1.4572 + if (!typedObject) 1.4573 + return false; 1.4574 + addendum = typedObject; 1.4575 + return true; 1.4576 +} 1.4577 + 1.4578 +///////////////////////////////////////////////////////////////////// 1.4579 +// Type object addenda constructor 1.4580 +///////////////////////////////////////////////////////////////////// 1.4581 + 1.4582 +TypeObjectAddendum::TypeObjectAddendum(Kind kind) 1.4583 + : kind(kind) 1.4584 +{} 1.4585 + 1.4586 +TypeNewScript::TypeNewScript() 1.4587 + : TypeObjectAddendum(NewScript) 1.4588 +{} 1.4589 + 1.4590 +TypeTypedObject::TypeTypedObject(Handle<TypeDescr*> descr) 1.4591 + : TypeObjectAddendum(TypedObject), 1.4592 + descr_(descr) 1.4593 +{ 1.4594 +} 1.4595 + 1.4596 +TypeDescr & 1.4597 +js::types::TypeTypedObject::descr() { 1.4598 + return descr_->as<TypeDescr>(); 1.4599 +}