js/src/jsinfer.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jsinferinlines.h"
michael@0 8
michael@0 9 #include "mozilla/DebugOnly.h"
michael@0 10 #include "mozilla/MemoryReporting.h"
michael@0 11 #include "mozilla/PodOperations.h"
michael@0 12
michael@0 13 #include "jsapi.h"
michael@0 14 #include "jscntxt.h"
michael@0 15 #include "jsgc.h"
michael@0 16 #include "jshashutil.h"
michael@0 17 #include "jsobj.h"
michael@0 18 #include "jsprf.h"
michael@0 19 #include "jsscript.h"
michael@0 20 #include "jsstr.h"
michael@0 21 #include "jsworkers.h"
michael@0 22 #include "prmjtime.h"
michael@0 23
michael@0 24 #include "gc/Marking.h"
michael@0 25 #ifdef JS_ION
michael@0 26 #include "jit/BaselineJIT.h"
michael@0 27 #include "jit/Ion.h"
michael@0 28 #include "jit/IonAnalysis.h"
michael@0 29 #include "jit/JitCompartment.h"
michael@0 30 #endif
michael@0 31 #include "js/MemoryMetrics.h"
michael@0 32 #include "vm/Opcodes.h"
michael@0 33 #include "vm/Shape.h"
michael@0 34
michael@0 35 #include "jsatominlines.h"
michael@0 36 #include "jsgcinlines.h"
michael@0 37 #include "jsobjinlines.h"
michael@0 38 #include "jsscriptinlines.h"
michael@0 39
michael@0 40 #include "jit/ExecutionMode-inl.h"
michael@0 41
michael@0 42 using namespace js;
michael@0 43 using namespace js::gc;
michael@0 44 using namespace js::types;
michael@0 45 using namespace js::analyze;
michael@0 46
michael@0 47 using mozilla::DebugOnly;
michael@0 48 using mozilla::Maybe;
michael@0 49 using mozilla::PodArrayZero;
michael@0 50 using mozilla::PodCopy;
michael@0 51 using mozilla::PodZero;
michael@0 52
michael@0 53 static inline jsid
michael@0 54 id_prototype(JSContext *cx) {
michael@0 55 return NameToId(cx->names().prototype);
michael@0 56 }
michael@0 57
michael@0 58 static inline jsid
michael@0 59 id___proto__(JSContext *cx) {
michael@0 60 return NameToId(cx->names().proto);
michael@0 61 }
michael@0 62
michael@0 63 static inline jsid
michael@0 64 id_constructor(JSContext *cx) {
michael@0 65 return NameToId(cx->names().constructor);
michael@0 66 }
michael@0 67
michael@0 68 static inline jsid
michael@0 69 id_caller(JSContext *cx) {
michael@0 70 return NameToId(cx->names().caller);
michael@0 71 }
michael@0 72
michael@0 73 #ifdef DEBUG
michael@0 74 const char *
michael@0 75 types::TypeIdStringImpl(jsid id)
michael@0 76 {
michael@0 77 if (JSID_IS_VOID(id))
michael@0 78 return "(index)";
michael@0 79 if (JSID_IS_EMPTY(id))
michael@0 80 return "(new)";
michael@0 81 static char bufs[4][100];
michael@0 82 static unsigned which = 0;
michael@0 83 which = (which + 1) & 3;
michael@0 84 PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0);
michael@0 85 return bufs[which];
michael@0 86 }
michael@0 87 #endif
michael@0 88
michael@0 89 /////////////////////////////////////////////////////////////////////
michael@0 90 // Logging
michael@0 91 /////////////////////////////////////////////////////////////////////
michael@0 92
michael@0 93 #ifdef DEBUG
michael@0 94
michael@0 95 static bool InferSpewActive(SpewChannel channel)
michael@0 96 {
michael@0 97 static bool active[SPEW_COUNT];
michael@0 98 static bool checked = false;
michael@0 99 if (!checked) {
michael@0 100 checked = true;
michael@0 101 PodArrayZero(active);
michael@0 102 const char *env = getenv("INFERFLAGS");
michael@0 103 if (!env)
michael@0 104 return false;
michael@0 105 if (strstr(env, "ops"))
michael@0 106 active[ISpewOps] = true;
michael@0 107 if (strstr(env, "result"))
michael@0 108 active[ISpewResult] = true;
michael@0 109 if (strstr(env, "full")) {
michael@0 110 for (unsigned i = 0; i < SPEW_COUNT; i++)
michael@0 111 active[i] = true;
michael@0 112 }
michael@0 113 }
michael@0 114 return active[channel];
michael@0 115 }
michael@0 116
michael@0 117 static bool InferSpewColorable()
michael@0 118 {
michael@0 119 /* Only spew colors on xterm-color to not screw up emacs. */
michael@0 120 static bool colorable = false;
michael@0 121 static bool checked = false;
michael@0 122 if (!checked) {
michael@0 123 checked = true;
michael@0 124 const char *env = getenv("TERM");
michael@0 125 if (!env)
michael@0 126 return false;
michael@0 127 if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
michael@0 128 colorable = true;
michael@0 129 }
michael@0 130 return colorable;
michael@0 131 }
michael@0 132
michael@0 133 const char *
michael@0 134 types::InferSpewColorReset()
michael@0 135 {
michael@0 136 if (!InferSpewColorable())
michael@0 137 return "";
michael@0 138 return "\x1b[0m";
michael@0 139 }
michael@0 140
michael@0 141 const char *
michael@0 142 types::InferSpewColor(TypeConstraint *constraint)
michael@0 143 {
michael@0 144 /* Type constraints are printed out using foreground colors. */
michael@0 145 static const char * const colors[] = { "\x1b[31m", "\x1b[32m", "\x1b[33m",
michael@0 146 "\x1b[34m", "\x1b[35m", "\x1b[36m",
michael@0 147 "\x1b[37m" };
michael@0 148 if (!InferSpewColorable())
michael@0 149 return "";
michael@0 150 return colors[DefaultHasher<TypeConstraint *>::hash(constraint) % 7];
michael@0 151 }
michael@0 152
michael@0 153 const char *
michael@0 154 types::InferSpewColor(TypeSet *types)
michael@0 155 {
michael@0 156 /* Type sets are printed out using bold colors. */
michael@0 157 static const char * const colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m",
michael@0 158 "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m",
michael@0 159 "\x1b[1;37m" };
michael@0 160 if (!InferSpewColorable())
michael@0 161 return "";
michael@0 162 return colors[DefaultHasher<TypeSet *>::hash(types) % 7];
michael@0 163 }
michael@0 164
michael@0 165 const char *
michael@0 166 types::TypeString(Type type)
michael@0 167 {
michael@0 168 if (type.isPrimitive()) {
michael@0 169 switch (type.primitive()) {
michael@0 170 case JSVAL_TYPE_UNDEFINED:
michael@0 171 return "void";
michael@0 172 case JSVAL_TYPE_NULL:
michael@0 173 return "null";
michael@0 174 case JSVAL_TYPE_BOOLEAN:
michael@0 175 return "bool";
michael@0 176 case JSVAL_TYPE_INT32:
michael@0 177 return "int";
michael@0 178 case JSVAL_TYPE_DOUBLE:
michael@0 179 return "float";
michael@0 180 case JSVAL_TYPE_STRING:
michael@0 181 return "string";
michael@0 182 case JSVAL_TYPE_MAGIC:
michael@0 183 return "lazyargs";
michael@0 184 default:
michael@0 185 MOZ_ASSUME_UNREACHABLE("Bad type");
michael@0 186 }
michael@0 187 }
michael@0 188 if (type.isUnknown())
michael@0 189 return "unknown";
michael@0 190 if (type.isAnyObject())
michael@0 191 return " object";
michael@0 192
michael@0 193 static char bufs[4][40];
michael@0 194 static unsigned which = 0;
michael@0 195 which = (which + 1) & 3;
michael@0 196
michael@0 197 if (type.isSingleObject())
michael@0 198 JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleObject());
michael@0 199 else
michael@0 200 JS_snprintf(bufs[which], 40, "[0x%p]", (void *) type.typeObject());
michael@0 201
michael@0 202 return bufs[which];
michael@0 203 }
michael@0 204
michael@0 205 const char *
michael@0 206 types::TypeObjectString(TypeObject *type)
michael@0 207 {
michael@0 208 return TypeString(Type::ObjectType(type));
michael@0 209 }
michael@0 210
michael@0 211 unsigned JSScript::id() {
michael@0 212 if (!id_) {
michael@0 213 id_ = ++compartment()->types.scriptCount;
michael@0 214 InferSpew(ISpewOps, "script #%u: %p %s:%d",
michael@0 215 id_, this, filename() ? filename() : "<null>", lineno());
michael@0 216 }
michael@0 217 return id_;
michael@0 218 }
michael@0 219
michael@0 220 void
michael@0 221 types::InferSpew(SpewChannel channel, const char *fmt, ...)
michael@0 222 {
michael@0 223 if (!InferSpewActive(channel))
michael@0 224 return;
michael@0 225
michael@0 226 va_list ap;
michael@0 227 va_start(ap, fmt);
michael@0 228 fprintf(stderr, "[infer] ");
michael@0 229 vfprintf(stderr, fmt, ap);
michael@0 230 fprintf(stderr, "\n");
michael@0 231 va_end(ap);
michael@0 232 }
michael@0 233
michael@0 234 bool
michael@0 235 types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value)
michael@0 236 {
michael@0 237 /*
michael@0 238 * Check the correctness of the type information in the object's property
michael@0 239 * against an actual value.
michael@0 240 */
michael@0 241 if (!obj->unknownProperties() && !value.isUndefined()) {
michael@0 242 id = IdToTypeId(id);
michael@0 243
michael@0 244 /* Watch for properties which inference does not monitor. */
michael@0 245 if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
michael@0 246 return true;
michael@0 247
michael@0 248 Type type = GetValueType(value);
michael@0 249
michael@0 250 AutoEnterAnalysis enter(cx);
michael@0 251
michael@0 252 /*
michael@0 253 * We don't track types for properties inherited from prototypes which
michael@0 254 * haven't yet been accessed during analysis of the inheriting object.
michael@0 255 * Don't do the property instantiation now.
michael@0 256 */
michael@0 257 TypeSet *types = obj->maybeGetProperty(id);
michael@0 258 if (!types)
michael@0 259 return true;
michael@0 260
michael@0 261 if (!types->hasType(type)) {
michael@0 262 TypeFailure(cx, "Missing type in object %s %s: %s",
michael@0 263 TypeObjectString(obj), TypeIdString(id), TypeString(type));
michael@0 264 }
michael@0 265 }
michael@0 266 return true;
michael@0 267 }
michael@0 268
michael@0 269 #endif
michael@0 270
michael@0 271 void
michael@0 272 types::TypeFailure(JSContext *cx, const char *fmt, ...)
michael@0 273 {
michael@0 274 char msgbuf[1024]; /* Larger error messages will be truncated */
michael@0 275 char errbuf[1024];
michael@0 276
michael@0 277 va_list ap;
michael@0 278 va_start(ap, fmt);
michael@0 279 JS_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
michael@0 280 va_end(ap);
michael@0 281
michael@0 282 JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf);
michael@0 283
michael@0 284 /* Dump type state, even if INFERFLAGS is unset. */
michael@0 285 cx->compartment()->types.print(cx, true);
michael@0 286
michael@0 287 MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
michael@0 288 MOZ_CRASH();
michael@0 289 }
michael@0 290
michael@0 291 /////////////////////////////////////////////////////////////////////
michael@0 292 // TypeSet
michael@0 293 /////////////////////////////////////////////////////////////////////
michael@0 294
michael@0 295 TemporaryTypeSet::TemporaryTypeSet(Type type)
michael@0 296 {
michael@0 297 if (type.isUnknown()) {
michael@0 298 flags |= TYPE_FLAG_BASE_MASK;
michael@0 299 } else if (type.isPrimitive()) {
michael@0 300 flags = PrimitiveTypeFlag(type.primitive());
michael@0 301 if (flags == TYPE_FLAG_DOUBLE)
michael@0 302 flags |= TYPE_FLAG_INT32;
michael@0 303 } else if (type.isAnyObject()) {
michael@0 304 flags |= TYPE_FLAG_ANYOBJECT;
michael@0 305 } else if (type.isTypeObject() && type.typeObject()->unknownProperties()) {
michael@0 306 flags |= TYPE_FLAG_ANYOBJECT;
michael@0 307 } else {
michael@0 308 setBaseObjectCount(1);
michael@0 309 objectSet = reinterpret_cast<TypeObjectKey**>(type.objectKey());
michael@0 310 }
michael@0 311 }
michael@0 312
michael@0 313 bool
michael@0 314 TypeSet::mightBeMIRType(jit::MIRType type)
michael@0 315 {
michael@0 316 if (unknown())
michael@0 317 return true;
michael@0 318
michael@0 319 if (type == jit::MIRType_Object)
michael@0 320 return unknownObject() || baseObjectCount() != 0;
michael@0 321
michael@0 322 switch (type) {
michael@0 323 case jit::MIRType_Undefined:
michael@0 324 return baseFlags() & TYPE_FLAG_UNDEFINED;
michael@0 325 case jit::MIRType_Null:
michael@0 326 return baseFlags() & TYPE_FLAG_NULL;
michael@0 327 case jit::MIRType_Boolean:
michael@0 328 return baseFlags() & TYPE_FLAG_BOOLEAN;
michael@0 329 case jit::MIRType_Int32:
michael@0 330 return baseFlags() & TYPE_FLAG_INT32;
michael@0 331 case jit::MIRType_Float32: // Fall through, there's no JSVAL for Float32.
michael@0 332 case jit::MIRType_Double:
michael@0 333 return baseFlags() & TYPE_FLAG_DOUBLE;
michael@0 334 case jit::MIRType_String:
michael@0 335 return baseFlags() & TYPE_FLAG_STRING;
michael@0 336 case jit::MIRType_MagicOptimizedArguments:
michael@0 337 return baseFlags() & TYPE_FLAG_LAZYARGS;
michael@0 338 case jit::MIRType_MagicHole:
michael@0 339 case jit::MIRType_MagicIsConstructing:
michael@0 340 // These magic constants do not escape to script and are not observed
michael@0 341 // in the type sets.
michael@0 342 //
michael@0 343 // The reason we can return false here is subtle: if Ion is asking the
michael@0 344 // type set if it has seen such a magic constant, then the MIR in
michael@0 345 // question is the most generic type, MIRType_Value. A magic constant
michael@0 346 // could only be emitted by a MIR of MIRType_Value if that MIR is a
michael@0 347 // phi, and we check that different magic constants do not flow to the
michael@0 348 // same join point in GuessPhiType.
michael@0 349 return false;
michael@0 350 default:
michael@0 351 MOZ_ASSUME_UNREACHABLE("Bad MIR type");
michael@0 352 }
michael@0 353 }
michael@0 354
michael@0 355 bool
michael@0 356 TypeSet::isSubset(TypeSet *other)
michael@0 357 {
michael@0 358 if ((baseFlags() & other->baseFlags()) != baseFlags())
michael@0 359 return false;
michael@0 360
michael@0 361 if (unknownObject()) {
michael@0 362 JS_ASSERT(other->unknownObject());
michael@0 363 } else {
michael@0 364 for (unsigned i = 0; i < getObjectCount(); i++) {
michael@0 365 TypeObjectKey *obj = getObject(i);
michael@0 366 if (!obj)
michael@0 367 continue;
michael@0 368 if (!other->hasType(Type::ObjectType(obj)))
michael@0 369 return false;
michael@0 370 }
michael@0 371 }
michael@0 372
michael@0 373 return true;
michael@0 374 }
michael@0 375
michael@0 376 bool
michael@0 377 TypeSet::enumerateTypes(TypeList *list)
michael@0 378 {
michael@0 379 /* If any type is possible, there's no need to worry about specifics. */
michael@0 380 if (flags & TYPE_FLAG_UNKNOWN)
michael@0 381 return list->append(Type::UnknownType());
michael@0 382
michael@0 383 /* Enqueue type set members stored as bits. */
michael@0 384 for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
michael@0 385 if (flags & flag) {
michael@0 386 Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
michael@0 387 if (!list->append(type))
michael@0 388 return false;
michael@0 389 }
michael@0 390 }
michael@0 391
michael@0 392 /* If any object is possible, skip specifics. */
michael@0 393 if (flags & TYPE_FLAG_ANYOBJECT)
michael@0 394 return list->append(Type::AnyObjectType());
michael@0 395
michael@0 396 /* Enqueue specific object types. */
michael@0 397 unsigned count = getObjectCount();
michael@0 398 for (unsigned i = 0; i < count; i++) {
michael@0 399 TypeObjectKey *object = getObject(i);
michael@0 400 if (object) {
michael@0 401 if (!list->append(Type::ObjectType(object)))
michael@0 402 return false;
michael@0 403 }
michael@0 404 }
michael@0 405
michael@0 406 return true;
michael@0 407 }
michael@0 408
michael@0 409 inline bool
michael@0 410 TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint)
michael@0 411 {
michael@0 412 /*
michael@0 413 * Build all types in the set into a vector before triggering the
michael@0 414 * constraint, as doing so may modify this type set.
michael@0 415 */
michael@0 416 TypeList types;
michael@0 417 if (!enumerateTypes(&types))
michael@0 418 return false;
michael@0 419
michael@0 420 for (unsigned i = 0; i < types.length(); i++)
michael@0 421 constraint->newType(cx, this, types[i]);
michael@0 422
michael@0 423 return true;
michael@0 424 }
michael@0 425
michael@0 426 bool
michael@0 427 ConstraintTypeSet::addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting)
michael@0 428 {
michael@0 429 if (!constraint) {
michael@0 430 /* OOM failure while constructing the constraint. */
michael@0 431 return false;
michael@0 432 }
michael@0 433
michael@0 434 JS_ASSERT(cx->compartment()->activeAnalysis);
michael@0 435
michael@0 436 InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s",
michael@0 437 InferSpewColor(this), this, InferSpewColorReset(),
michael@0 438 InferSpewColor(constraint), constraint, InferSpewColorReset(),
michael@0 439 constraint->kind());
michael@0 440
michael@0 441 JS_ASSERT(constraint->next == nullptr);
michael@0 442 constraint->next = constraintList;
michael@0 443 constraintList = constraint;
michael@0 444
michael@0 445 if (callExisting)
michael@0 446 return addTypesToConstraint(cx, constraint);
michael@0 447 return true;
michael@0 448 }
michael@0 449
michael@0 450 void
michael@0 451 TypeSet::clearObjects()
michael@0 452 {
michael@0 453 setBaseObjectCount(0);
michael@0 454 objectSet = nullptr;
michael@0 455 }
michael@0 456
michael@0 457 void
michael@0 458 TypeSet::addType(Type type, LifoAlloc *alloc)
michael@0 459 {
michael@0 460 if (unknown())
michael@0 461 return;
michael@0 462
michael@0 463 if (type.isUnknown()) {
michael@0 464 flags |= TYPE_FLAG_BASE_MASK;
michael@0 465 clearObjects();
michael@0 466 JS_ASSERT(unknown());
michael@0 467 return;
michael@0 468 }
michael@0 469
michael@0 470 if (type.isPrimitive()) {
michael@0 471 TypeFlags flag = PrimitiveTypeFlag(type.primitive());
michael@0 472 if (flags & flag)
michael@0 473 return;
michael@0 474
michael@0 475 /* If we add float to a type set it is also considered to contain int. */
michael@0 476 if (flag == TYPE_FLAG_DOUBLE)
michael@0 477 flag |= TYPE_FLAG_INT32;
michael@0 478
michael@0 479 flags |= flag;
michael@0 480 return;
michael@0 481 }
michael@0 482
michael@0 483 if (flags & TYPE_FLAG_ANYOBJECT)
michael@0 484 return;
michael@0 485 if (type.isAnyObject())
michael@0 486 goto unknownObject;
michael@0 487
michael@0 488 {
michael@0 489 uint32_t objectCount = baseObjectCount();
michael@0 490 TypeObjectKey *object = type.objectKey();
michael@0 491 TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
michael@0 492 (*alloc, objectSet, objectCount, object);
michael@0 493 if (!pentry)
michael@0 494 goto unknownObject;
michael@0 495 if (*pentry)
michael@0 496 return;
michael@0 497 *pentry = object;
michael@0 498
michael@0 499 setBaseObjectCount(objectCount);
michael@0 500
michael@0 501 if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
michael@0 502 goto unknownObject;
michael@0 503 }
michael@0 504
michael@0 505 if (type.isTypeObject()) {
michael@0 506 TypeObject *nobject = type.typeObject();
michael@0 507 JS_ASSERT(!nobject->singleton());
michael@0 508 if (nobject->unknownProperties())
michael@0 509 goto unknownObject;
michael@0 510 }
michael@0 511
michael@0 512 if (false) {
michael@0 513 unknownObject:
michael@0 514 flags |= TYPE_FLAG_ANYOBJECT;
michael@0 515 clearObjects();
michael@0 516 }
michael@0 517 }
michael@0 518
michael@0 519 void
michael@0 520 ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
michael@0 521 {
michael@0 522 JS_ASSERT(cxArg->compartment()->activeAnalysis);
michael@0 523
michael@0 524 if (hasType(type))
michael@0 525 return;
michael@0 526
michael@0 527 TypeSet::addType(type, &cxArg->typeLifoAlloc());
michael@0 528
michael@0 529 if (type.isObjectUnchecked() && unknownObject())
michael@0 530 type = Type::AnyObjectType();
michael@0 531
michael@0 532 InferSpew(ISpewOps, "addType: %sT%p%s %s",
michael@0 533 InferSpewColor(this), this, InferSpewColorReset(),
michael@0 534 TypeString(type));
michael@0 535
michael@0 536 /* Propagate the type to all constraints. */
michael@0 537 if (JSContext *cx = cxArg->maybeJSContext()) {
michael@0 538 TypeConstraint *constraint = constraintList;
michael@0 539 while (constraint) {
michael@0 540 constraint->newType(cx, this, type);
michael@0 541 constraint = constraint->next;
michael@0 542 }
michael@0 543 } else {
michael@0 544 JS_ASSERT(!constraintList);
michael@0 545 }
michael@0 546 }
michael@0 547
michael@0 548 void
michael@0 549 TypeSet::print()
michael@0 550 {
michael@0 551 if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
michael@0 552 fprintf(stderr, " [non-data]");
michael@0 553
michael@0 554 if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY)
michael@0 555 fprintf(stderr, " [non-writable]");
michael@0 556
michael@0 557 if (definiteProperty())
michael@0 558 fprintf(stderr, " [definite:%d]", definiteSlot());
michael@0 559
michael@0 560 if (baseFlags() == 0 && !baseObjectCount()) {
michael@0 561 fprintf(stderr, " missing");
michael@0 562 return;
michael@0 563 }
michael@0 564
michael@0 565 if (flags & TYPE_FLAG_UNKNOWN)
michael@0 566 fprintf(stderr, " unknown");
michael@0 567 if (flags & TYPE_FLAG_ANYOBJECT)
michael@0 568 fprintf(stderr, " object");
michael@0 569
michael@0 570 if (flags & TYPE_FLAG_UNDEFINED)
michael@0 571 fprintf(stderr, " void");
michael@0 572 if (flags & TYPE_FLAG_NULL)
michael@0 573 fprintf(stderr, " null");
michael@0 574 if (flags & TYPE_FLAG_BOOLEAN)
michael@0 575 fprintf(stderr, " bool");
michael@0 576 if (flags & TYPE_FLAG_INT32)
michael@0 577 fprintf(stderr, " int");
michael@0 578 if (flags & TYPE_FLAG_DOUBLE)
michael@0 579 fprintf(stderr, " float");
michael@0 580 if (flags & TYPE_FLAG_STRING)
michael@0 581 fprintf(stderr, " string");
michael@0 582 if (flags & TYPE_FLAG_LAZYARGS)
michael@0 583 fprintf(stderr, " lazyargs");
michael@0 584
michael@0 585 uint32_t objectCount = baseObjectCount();
michael@0 586 if (objectCount) {
michael@0 587 fprintf(stderr, " object[%u]", objectCount);
michael@0 588
michael@0 589 unsigned count = getObjectCount();
michael@0 590 for (unsigned i = 0; i < count; i++) {
michael@0 591 TypeObjectKey *object = getObject(i);
michael@0 592 if (object)
michael@0 593 fprintf(stderr, " %s", TypeString(Type::ObjectType(object)));
michael@0 594 }
michael@0 595 }
michael@0 596 }
michael@0 597
michael@0 598 bool
michael@0 599 TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const
michael@0 600 {
michael@0 601 JS_ASSERT(result->empty());
michael@0 602
michael@0 603 unsigned objectCount = baseObjectCount();
michael@0 604 unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
michael@0 605
michael@0 606 TypeObjectKey **newSet;
michael@0 607 if (capacity) {
michael@0 608 newSet = alloc->newArray<TypeObjectKey*>(capacity);
michael@0 609 if (!newSet)
michael@0 610 return false;
michael@0 611 PodCopy(newSet, objectSet, capacity);
michael@0 612 }
michael@0 613
michael@0 614 new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet);
michael@0 615 return true;
michael@0 616 }
michael@0 617
michael@0 618 TemporaryTypeSet *
michael@0 619 TypeSet::clone(LifoAlloc *alloc) const
michael@0 620 {
michael@0 621 TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>();
michael@0 622 if (!res || !clone(alloc, res))
michael@0 623 return nullptr;
michael@0 624 return res;
michael@0 625 }
michael@0 626
michael@0 627 TemporaryTypeSet *
michael@0 628 TypeSet::filter(LifoAlloc *alloc, bool filterUndefined, bool filterNull) const
michael@0 629 {
michael@0 630 TemporaryTypeSet *res = clone(alloc);
michael@0 631 if (!res)
michael@0 632 return nullptr;
michael@0 633
michael@0 634 if (filterUndefined)
michael@0 635 res->flags = res->flags & ~TYPE_FLAG_UNDEFINED;
michael@0 636
michael@0 637 if (filterNull)
michael@0 638 res->flags = res->flags & ~TYPE_FLAG_NULL;
michael@0 639
michael@0 640 return res;
michael@0 641 }
michael@0 642
michael@0 643 /* static */ TemporaryTypeSet *
michael@0 644 TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc)
michael@0 645 {
michael@0 646 TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>(a->baseFlags() | b->baseFlags(),
michael@0 647 static_cast<TypeObjectKey**>(nullptr));
michael@0 648 if (!res)
michael@0 649 return nullptr;
michael@0 650
michael@0 651 if (!res->unknownObject()) {
michael@0 652 for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) {
michael@0 653 if (TypeObjectKey *key = a->getObject(i))
michael@0 654 res->addType(Type::ObjectType(key), alloc);
michael@0 655 }
michael@0 656 for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
michael@0 657 if (TypeObjectKey *key = b->getObject(i))
michael@0 658 res->addType(Type::ObjectType(key), alloc);
michael@0 659 }
michael@0 660 }
michael@0 661
michael@0 662 return res;
michael@0 663 }
michael@0 664
michael@0 665 /////////////////////////////////////////////////////////////////////
michael@0 666 // Compiler constraints
michael@0 667 /////////////////////////////////////////////////////////////////////
michael@0 668
michael@0 669 // Compiler constraints overview
michael@0 670 //
michael@0 671 // Constraints generated during Ion compilation capture assumptions made about
michael@0 672 // heap properties that will trigger invalidation of the resulting Ion code if
michael@0 673 // the constraint is violated. Constraints can only be attached to type sets on
michael@0 674 // the main thread, so to allow compilation to occur almost entirely off thread
michael@0 675 // the generation is split into two phases.
michael@0 676 //
michael@0 677 // During compilation, CompilerConstraint values are constructed in a list,
michael@0 678 // recording the heap property type set which was read from and its expected
michael@0 679 // contents, along with the assumption made about those contents.
michael@0 680 //
michael@0 681 // At the end of compilation, when linking the result on the main thread, the
michael@0 682 // list of compiler constraints are read and converted to type constraints and
michael@0 683 // attached to the type sets. If the property type sets have changed so that the
michael@0 684 // assumptions no longer hold then the compilation is aborted and its result
michael@0 685 // discarded.
michael@0 686
michael@0 687 // Superclass of all constraints generated during Ion compilation. These may
michael@0 688 // be allocated off the main thread, using the current Ion context's allocator.
michael@0 689 class CompilerConstraint
michael@0 690 {
michael@0 691 public:
michael@0 692 // Property being queried by the compiler.
michael@0 693 HeapTypeSetKey property;
michael@0 694
michael@0 695 // Contents of the property at the point when the query was performed. This
michael@0 696 // may differ from the actual property types later in compilation as the
michael@0 697 // main thread performs side effects.
michael@0 698 TemporaryTypeSet *expected;
michael@0 699
michael@0 700 CompilerConstraint(LifoAlloc *alloc, const HeapTypeSetKey &property)
michael@0 701 : property(property),
michael@0 702 expected(property.maybeTypes() ? property.maybeTypes()->clone(alloc) : nullptr)
michael@0 703 {}
michael@0 704
michael@0 705 // Generate the type constraint recording the assumption made by this
michael@0 706 // compilation. Returns true if the assumption originally made still holds.
michael@0 707 virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0;
michael@0 708 };
michael@0 709
michael@0 710 class types::CompilerConstraintList
michael@0 711 {
michael@0 712 public:
michael@0 713 struct FrozenScript
michael@0 714 {
michael@0 715 JSScript *script;
michael@0 716 TemporaryTypeSet *thisTypes;
michael@0 717 TemporaryTypeSet *argTypes;
michael@0 718 TemporaryTypeSet *bytecodeTypes;
michael@0 719 };
michael@0 720
michael@0 721 private:
michael@0 722
michael@0 723 // OOM during generation of some constraint.
michael@0 724 bool failed_;
michael@0 725
michael@0 726 #ifdef JS_ION
michael@0 727 // Allocator used for constraints.
michael@0 728 LifoAlloc *alloc_;
michael@0 729
michael@0 730 // Constraints generated on heap properties.
michael@0 731 Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints;
michael@0 732
michael@0 733 // Scripts whose stack type sets were frozen for the compilation.
michael@0 734 Vector<FrozenScript, 1, jit::IonAllocPolicy> frozenScripts;
michael@0 735 #endif
michael@0 736
michael@0 737 public:
michael@0 738 CompilerConstraintList(jit::TempAllocator &alloc)
michael@0 739 : failed_(false)
michael@0 740 #ifdef JS_ION
michael@0 741 , alloc_(alloc.lifoAlloc())
michael@0 742 , constraints(alloc)
michael@0 743 , frozenScripts(alloc)
michael@0 744 #endif
michael@0 745 {}
michael@0 746
michael@0 747 void add(CompilerConstraint *constraint) {
michael@0 748 #ifdef JS_ION
michael@0 749 if (!constraint || !constraints.append(constraint))
michael@0 750 setFailed();
michael@0 751 #else
michael@0 752 MOZ_CRASH();
michael@0 753 #endif
michael@0 754 }
michael@0 755
michael@0 756 void freezeScript(JSScript *script,
michael@0 757 TemporaryTypeSet *thisTypes,
michael@0 758 TemporaryTypeSet *argTypes,
michael@0 759 TemporaryTypeSet *bytecodeTypes)
michael@0 760 {
michael@0 761 #ifdef JS_ION
michael@0 762 FrozenScript entry;
michael@0 763 entry.script = script;
michael@0 764 entry.thisTypes = thisTypes;
michael@0 765 entry.argTypes = argTypes;
michael@0 766 entry.bytecodeTypes = bytecodeTypes;
michael@0 767 if (!frozenScripts.append(entry))
michael@0 768 setFailed();
michael@0 769 #else
michael@0 770 MOZ_CRASH();
michael@0 771 #endif
michael@0 772 }
michael@0 773
michael@0 774 size_t length() {
michael@0 775 #ifdef JS_ION
michael@0 776 return constraints.length();
michael@0 777 #else
michael@0 778 MOZ_CRASH();
michael@0 779 #endif
michael@0 780 }
michael@0 781
michael@0 782 CompilerConstraint *get(size_t i) {
michael@0 783 #ifdef JS_ION
michael@0 784 return constraints[i];
michael@0 785 #else
michael@0 786 MOZ_CRASH();
michael@0 787 #endif
michael@0 788 }
michael@0 789
michael@0 790 size_t numFrozenScripts() {
michael@0 791 #ifdef JS_ION
michael@0 792 return frozenScripts.length();
michael@0 793 #else
michael@0 794 MOZ_CRASH();
michael@0 795 #endif
michael@0 796 }
michael@0 797
michael@0 798 const FrozenScript &frozenScript(size_t i) {
michael@0 799 #ifdef JS_ION
michael@0 800 return frozenScripts[i];
michael@0 801 #else
michael@0 802 MOZ_CRASH();
michael@0 803 #endif
michael@0 804 }
michael@0 805
michael@0 806 bool failed() {
michael@0 807 return failed_;
michael@0 808 }
michael@0 809 void setFailed() {
michael@0 810 failed_ = true;
michael@0 811 }
michael@0 812 LifoAlloc *alloc() const {
michael@0 813 #ifdef JS_ION
michael@0 814 return alloc_;
michael@0 815 #else
michael@0 816 MOZ_CRASH();
michael@0 817 #endif
michael@0 818 }
michael@0 819 };
michael@0 820
michael@0 821 CompilerConstraintList *
michael@0 822 types::NewCompilerConstraintList(jit::TempAllocator &alloc)
michael@0 823 {
michael@0 824 #ifdef JS_ION
michael@0 825 return alloc.lifoAlloc()->new_<CompilerConstraintList>(alloc);
michael@0 826 #else
michael@0 827 MOZ_CRASH();
michael@0 828 #endif
michael@0 829 }
michael@0 830
michael@0 831 /* static */ bool
michael@0 832 TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script,
michael@0 833 TemporaryTypeSet **pThisTypes,
michael@0 834 TemporaryTypeSet **pArgTypes,
michael@0 835 TemporaryTypeSet **pBytecodeTypes)
michael@0 836 {
michael@0 837 LifoAlloc *alloc = constraints->alloc();
michael@0 838 StackTypeSet *existing = script->types->typeArray();
michael@0 839
michael@0 840 size_t count = NumTypeSets(script);
michael@0 841 TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
michael@0 842 if (!types)
michael@0 843 return false;
michael@0 844 PodZero(types, count);
michael@0 845
michael@0 846 for (size_t i = 0; i < count; i++) {
michael@0 847 if (!existing[i].clone(alloc, &types[i]))
michael@0 848 return false;
michael@0 849 }
michael@0 850
michael@0 851 *pThisTypes = types + (ThisTypes(script) - existing);
michael@0 852 *pArgTypes = (script->functionNonDelazifying() && script->functionNonDelazifying()->nargs())
michael@0 853 ? (types + (ArgTypes(script, 0) - existing))
michael@0 854 : nullptr;
michael@0 855 *pBytecodeTypes = types;
michael@0 856
michael@0 857 constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes);
michael@0 858 return true;
michael@0 859 }
michael@0 860
michael@0 861 namespace {
michael@0 862
michael@0 863 template <typename T>
michael@0 864 class CompilerConstraintInstance : public CompilerConstraint
michael@0 865 {
michael@0 866 T data;
michael@0 867
michael@0 868 public:
michael@0 869 CompilerConstraintInstance<T>(LifoAlloc *alloc, const HeapTypeSetKey &property, const T &data)
michael@0 870 : CompilerConstraint(alloc, property), data(data)
michael@0 871 {}
michael@0 872
michael@0 873 bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo);
michael@0 874 };
michael@0 875
michael@0 876 // Constraint generated from a CompilerConstraint when linking the compilation.
michael@0 877 template <typename T>
michael@0 878 class TypeCompilerConstraint : public TypeConstraint
michael@0 879 {
michael@0 880 // Compilation which this constraint may invalidate.
michael@0 881 RecompileInfo compilation;
michael@0 882
michael@0 883 T data;
michael@0 884
michael@0 885 public:
michael@0 886 TypeCompilerConstraint<T>(RecompileInfo compilation, const T &data)
michael@0 887 : compilation(compilation), data(data)
michael@0 888 {}
michael@0 889
michael@0 890 const char *kind() { return data.kind(); }
michael@0 891
michael@0 892 void newType(JSContext *cx, TypeSet *source, Type type) {
michael@0 893 if (data.invalidateOnNewType(type))
michael@0 894 cx->zone()->types.addPendingRecompile(cx, compilation);
michael@0 895 }
michael@0 896
michael@0 897 void newPropertyState(JSContext *cx, TypeSet *source) {
michael@0 898 if (data.invalidateOnNewPropertyState(source))
michael@0 899 cx->zone()->types.addPendingRecompile(cx, compilation);
michael@0 900 }
michael@0 901
michael@0 902 void newObjectState(JSContext *cx, TypeObject *object) {
michael@0 903 // Note: Once the object has unknown properties, no more notifications
michael@0 904 // will be sent on changes to its state, so always invalidate any
michael@0 905 // associated compilations.
michael@0 906 if (object->unknownProperties() || data.invalidateOnNewObjectState(object))
michael@0 907 cx->zone()->types.addPendingRecompile(cx, compilation);
michael@0 908 }
michael@0 909
michael@0 910 bool sweep(TypeZone &zone, TypeConstraint **res) {
michael@0 911 if (data.shouldSweep() || compilation.shouldSweep(zone))
michael@0 912 return false;
michael@0 913 *res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data);
michael@0 914 return true;
michael@0 915 }
michael@0 916 };
michael@0 917
michael@0 918 template <typename T>
michael@0 919 bool
michael@0 920 CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo)
michael@0 921 {
michael@0 922 if (property.object()->unknownProperties())
michael@0 923 return false;
michael@0 924
michael@0 925 if (!property.instantiate(cx))
michael@0 926 return false;
michael@0 927
michael@0 928 if (!data.constraintHolds(cx, property, expected))
michael@0 929 return false;
michael@0 930
michael@0 931 return property.maybeTypes()->addConstraint(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
michael@0 932 /* callExisting = */ false);
michael@0 933 }
michael@0 934
michael@0 935 } /* anonymous namespace */
michael@0 936
michael@0 937 const Class *
michael@0 938 TypeObjectKey::clasp()
michael@0 939 {
michael@0 940 return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass();
michael@0 941 }
michael@0 942
michael@0 943 TaggedProto
michael@0 944 TypeObjectKey::proto()
michael@0 945 {
michael@0 946 JS_ASSERT(hasTenuredProto());
michael@0 947 return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
michael@0 948 }
michael@0 949
michael@0 950 bool
michael@0 951 ObjectImpl::hasTenuredProto() const
michael@0 952 {
michael@0 953 return type_->hasTenuredProto();
michael@0 954 }
michael@0 955
michael@0 956 bool
michael@0 957 TypeObjectKey::hasTenuredProto()
michael@0 958 {
michael@0 959 return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto();
michael@0 960 }
michael@0 961
michael@0 962 JSObject *
michael@0 963 TypeObjectKey::singleton()
michael@0 964 {
michael@0 965 return isTypeObject() ? asTypeObject()->singleton() : asSingleObject();
michael@0 966 }
michael@0 967
michael@0 968 TypeNewScript *
michael@0 969 TypeObjectKey::newScript()
michael@0 970 {
michael@0 971 if (isTypeObject() && asTypeObject()->hasNewScript())
michael@0 972 return asTypeObject()->newScript();
michael@0 973 return nullptr;
michael@0 974 }
michael@0 975
michael@0 976 TypeObject *
michael@0 977 TypeObjectKey::maybeType()
michael@0 978 {
michael@0 979 if (isTypeObject())
michael@0 980 return asTypeObject();
michael@0 981 if (asSingleObject()->hasLazyType())
michael@0 982 return nullptr;
michael@0 983 return asSingleObject()->type();
michael@0 984 }
michael@0 985
michael@0 986 bool
michael@0 987 TypeObjectKey::unknownProperties()
michael@0 988 {
michael@0 989 if (TypeObject *type = maybeType())
michael@0 990 return type->unknownProperties();
michael@0 991 return false;
michael@0 992 }
michael@0 993
michael@0 994 HeapTypeSetKey
michael@0 995 TypeObjectKey::property(jsid id)
michael@0 996 {
michael@0 997 JS_ASSERT(!unknownProperties());
michael@0 998
michael@0 999 HeapTypeSetKey property;
michael@0 1000 property.object_ = this;
michael@0 1001 property.id_ = id;
michael@0 1002 if (TypeObject *type = maybeType())
michael@0 1003 property.maybeTypes_ = type->maybeGetProperty(id);
michael@0 1004
michael@0 1005 return property;
michael@0 1006 }
michael@0 1007
michael@0 1008 void
michael@0 1009 TypeObjectKey::ensureTrackedProperty(JSContext *cx, jsid id)
michael@0 1010 {
michael@0 1011 #ifdef JS_ION
michael@0 1012 // If we are accessing a lazily defined property which actually exists in
michael@0 1013 // the VM and has not been instantiated yet, instantiate it now if we are
michael@0 1014 // on the main thread and able to do so.
michael@0 1015 if (!JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) {
michael@0 1016 JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
michael@0 1017 if (JSObject *obj = singleton()) {
michael@0 1018 if (obj->isNative() && obj->nativeLookupPure(id))
michael@0 1019 EnsureTrackPropertyTypes(cx, obj, id);
michael@0 1020 }
michael@0 1021 }
michael@0 1022 #endif // JS_ION
michael@0 1023 }
michael@0 1024
michael@0 1025 bool
michael@0 1026 HeapTypeSetKey::instantiate(JSContext *cx)
michael@0 1027 {
michael@0 1028 if (maybeTypes())
michael@0 1029 return true;
michael@0 1030 if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx)) {
michael@0 1031 cx->clearPendingException();
michael@0 1032 return false;
michael@0 1033 }
michael@0 1034 maybeTypes_ = object()->maybeType()->getProperty(cx, id());
michael@0 1035 return maybeTypes_ != nullptr;
michael@0 1036 }
michael@0 1037
michael@0 1038 static bool
michael@0 1039 CheckFrozenTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual)
michael@0 1040 {
michael@0 1041 // Return whether the types frozen for a script during compilation are
michael@0 1042 // still valid. Also check for any new types added to the frozen set during
michael@0 1043 // compilation, and add them to the actual stack type sets. These new types
michael@0 1044 // indicate places where the compiler relaxed its possible inputs to be
michael@0 1045 // more tolerant of potential new types.
michael@0 1046
michael@0 1047 if (!actual->isSubset(frozen))
michael@0 1048 return false;
michael@0 1049
michael@0 1050 if (!frozen->isSubset(actual)) {
michael@0 1051 TypeSet::TypeList list;
michael@0 1052 frozen->enumerateTypes(&list);
michael@0 1053
michael@0 1054 for (size_t i = 0; i < list.length(); i++)
michael@0 1055 actual->addType(cx, list[i]);
michael@0 1056 }
michael@0 1057
michael@0 1058 return true;
michael@0 1059 }
michael@0 1060
michael@0 1061 namespace {
michael@0 1062
michael@0 1063 /*
michael@0 1064 * As for TypeConstraintFreeze, but describes an implicit freeze constraint
michael@0 1065 * added for stack types within a script. Applies to all compilations of the
michael@0 1066 * script, not just a single one.
michael@0 1067 */
michael@0 1068 class TypeConstraintFreezeStack : public TypeConstraint
michael@0 1069 {
michael@0 1070 JSScript *script_;
michael@0 1071
michael@0 1072 public:
michael@0 1073 TypeConstraintFreezeStack(JSScript *script)
michael@0 1074 : script_(script)
michael@0 1075 {}
michael@0 1076
michael@0 1077 const char *kind() { return "freezeStack"; }
michael@0 1078
michael@0 1079 void newType(JSContext *cx, TypeSet *source, Type type) {
michael@0 1080 /*
michael@0 1081 * Unlike TypeConstraintFreeze, triggering this constraint once does
michael@0 1082 * not disable it on future changes to the type set.
michael@0 1083 */
michael@0 1084 cx->zone()->types.addPendingRecompile(cx, script_);
michael@0 1085 }
michael@0 1086
michael@0 1087 bool sweep(TypeZone &zone, TypeConstraint **res) {
michael@0 1088 if (IsScriptAboutToBeFinalized(&script_))
michael@0 1089 return false;
michael@0 1090 *res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_);
michael@0 1091 return true;
michael@0 1092 }
michael@0 1093 };
michael@0 1094
michael@0 1095 } /* anonymous namespace */
michael@0 1096
michael@0 1097 bool
michael@0 1098 types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode executionMode,
michael@0 1099 CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
michael@0 1100 {
michael@0 1101 if (constraints->failed())
michael@0 1102 return false;
michael@0 1103
michael@0 1104 CompilerOutput co(script, executionMode);
michael@0 1105
michael@0 1106 TypeZone &types = cx->zone()->types;
michael@0 1107 if (!types.compilerOutputs) {
michael@0 1108 types.compilerOutputs = cx->new_< Vector<CompilerOutput> >(cx);
michael@0 1109 if (!types.compilerOutputs)
michael@0 1110 return false;
michael@0 1111 }
michael@0 1112
michael@0 1113 #ifdef DEBUG
michael@0 1114 for (size_t i = 0; i < types.compilerOutputs->length(); i++) {
michael@0 1115 const CompilerOutput &co = (*types.compilerOutputs)[i];
michael@0 1116 JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode);
michael@0 1117 }
michael@0 1118 #endif
michael@0 1119
michael@0 1120 uint32_t index = types.compilerOutputs->length();
michael@0 1121 if (!types.compilerOutputs->append(co))
michael@0 1122 return false;
michael@0 1123
michael@0 1124 *precompileInfo = RecompileInfo(index);
michael@0 1125
michael@0 1126 bool succeeded = true;
michael@0 1127
michael@0 1128 for (size_t i = 0; i < constraints->length(); i++) {
michael@0 1129 CompilerConstraint *constraint = constraints->get(i);
michael@0 1130 if (!constraint->generateTypeConstraint(cx, *precompileInfo))
michael@0 1131 succeeded = false;
michael@0 1132 }
michael@0 1133
michael@0 1134 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
michael@0 1135 const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
michael@0 1136 JS_ASSERT(entry.script->types);
michael@0 1137
michael@0 1138 if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script)))
michael@0 1139 succeeded = false;
michael@0 1140 unsigned nargs = entry.script->functionNonDelazifying()
michael@0 1141 ? entry.script->functionNonDelazifying()->nargs()
michael@0 1142 : 0;
michael@0 1143 for (size_t i = 0; i < nargs; i++) {
michael@0 1144 if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], types::TypeScript::ArgTypes(entry.script, i)))
michael@0 1145 succeeded = false;
michael@0 1146 }
michael@0 1147 for (size_t i = 0; i < entry.script->nTypeSets(); i++) {
michael@0 1148 if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types->typeArray()[i]))
michael@0 1149 succeeded = false;
michael@0 1150 }
michael@0 1151
michael@0 1152 // If necessary, add constraints to trigger invalidation on the script
michael@0 1153 // after any future changes to the stack type sets.
michael@0 1154 if (entry.script->hasFreezeConstraints())
michael@0 1155 continue;
michael@0 1156 entry.script->setHasFreezeConstraints();
michael@0 1157
michael@0 1158 size_t count = TypeScript::NumTypeSets(entry.script);
michael@0 1159
michael@0 1160 StackTypeSet *array = entry.script->types->typeArray();
michael@0 1161 for (size_t i = 0; i < count; i++) {
michael@0 1162 if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false))
michael@0 1163 succeeded = false;
michael@0 1164 }
michael@0 1165 }
michael@0 1166
michael@0 1167 if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) {
michael@0 1168 types.compilerOutputs->back().invalidate();
michael@0 1169 script->resetUseCount();
michael@0 1170 return false;
michael@0 1171 }
michael@0 1172
michael@0 1173 return true;
michael@0 1174 }
michael@0 1175
michael@0 1176 static void
michael@0 1177 CheckDefinitePropertiesTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual)
michael@0 1178 {
michael@0 1179 // The definite properties analysis happens on the main thread, so no new
michael@0 1180 // types can have been added to actual. The analysis may have updated the
michael@0 1181 // contents of |frozen| though with new speculative types, and these need
michael@0 1182 // to be reflected in |actual| for AddClearDefiniteFunctionUsesInScript
michael@0 1183 // to work.
michael@0 1184 if (!frozen->isSubset(actual)) {
michael@0 1185 TypeSet::TypeList list;
michael@0 1186 frozen->enumerateTypes(&list);
michael@0 1187
michael@0 1188 for (size_t i = 0; i < list.length(); i++)
michael@0 1189 actual->addType(cx, list[i]);
michael@0 1190 }
michael@0 1191 }
michael@0 1192
michael@0 1193 void
michael@0 1194 types::FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *constraints)
michael@0 1195 {
michael@0 1196 #ifdef DEBUG
michael@0 1197 // Assert no new types have been added to the StackTypeSets. Do this before
michael@0 1198 // calling CheckDefinitePropertiesTypeSet, as it may add new types to the
michael@0 1199 // StackTypeSets and break these invariants if a script is inlined more
michael@0 1200 // than once. See also CheckDefinitePropertiesTypeSet.
michael@0 1201 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
michael@0 1202 const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
michael@0 1203 JSScript *script = entry.script;
michael@0 1204 JS_ASSERT(script->types);
michael@0 1205
michael@0 1206 JS_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes));
michael@0 1207
michael@0 1208 unsigned nargs = entry.script->functionNonDelazifying()
michael@0 1209 ? entry.script->functionNonDelazifying()->nargs()
michael@0 1210 : 0;
michael@0 1211 for (size_t j = 0; j < nargs; j++)
michael@0 1212 JS_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j]));
michael@0 1213
michael@0 1214 for (size_t j = 0; j < script->nTypeSets(); j++)
michael@0 1215 JS_ASSERT(script->types->typeArray()[j].isSubset(&entry.bytecodeTypes[j]));
michael@0 1216 }
michael@0 1217 #endif
michael@0 1218
michael@0 1219 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
michael@0 1220 const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
michael@0 1221 JSScript *script = entry.script;
michael@0 1222 JS_ASSERT(script->types);
michael@0 1223
michael@0 1224 if (!script->types)
michael@0 1225 MOZ_CRASH();
michael@0 1226
michael@0 1227 CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script));
michael@0 1228
michael@0 1229 unsigned nargs = script->functionNonDelazifying()
michael@0 1230 ? script->functionNonDelazifying()->nargs()
michael@0 1231 : 0;
michael@0 1232 for (size_t j = 0; j < nargs; j++)
michael@0 1233 CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j));
michael@0 1234
michael@0 1235 for (size_t j = 0; j < script->nTypeSets(); j++)
michael@0 1236 CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types->typeArray()[j]);
michael@0 1237 }
michael@0 1238 }
michael@0 1239
michael@0 1240 namespace {
michael@0 1241
michael@0 1242 // Constraint which triggers recompilation of a script if any type is added to a type set. */
michael@0 1243 class ConstraintDataFreeze
michael@0 1244 {
michael@0 1245 public:
michael@0 1246 ConstraintDataFreeze() {}
michael@0 1247
michael@0 1248 const char *kind() { return "freeze"; }
michael@0 1249
michael@0 1250 bool invalidateOnNewType(Type type) { return true; }
michael@0 1251 bool invalidateOnNewPropertyState(TypeSet *property) { return true; }
michael@0 1252 bool invalidateOnNewObjectState(TypeObject *object) { return false; }
michael@0 1253
michael@0 1254 bool constraintHolds(JSContext *cx,
michael@0 1255 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
michael@0 1256 {
michael@0 1257 return expected
michael@0 1258 ? property.maybeTypes()->isSubset(expected)
michael@0 1259 : property.maybeTypes()->empty();
michael@0 1260 }
michael@0 1261
michael@0 1262 bool shouldSweep() { return false; }
michael@0 1263 };
michael@0 1264
michael@0 1265 } /* anonymous namespace */
michael@0 1266
michael@0 1267 void
michael@0 1268 HeapTypeSetKey::freeze(CompilerConstraintList *constraints)
michael@0 1269 {
michael@0 1270 LifoAlloc *alloc = constraints->alloc();
michael@0 1271
michael@0 1272 typedef CompilerConstraintInstance<ConstraintDataFreeze> T;
michael@0 1273 constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataFreeze()));
michael@0 1274 }
michael@0 1275
michael@0 1276 static inline jit::MIRType
michael@0 1277 GetMIRTypeFromTypeFlags(TypeFlags flags)
michael@0 1278 {
michael@0 1279 switch (flags) {
michael@0 1280 case TYPE_FLAG_UNDEFINED:
michael@0 1281 return jit::MIRType_Undefined;
michael@0 1282 case TYPE_FLAG_NULL:
michael@0 1283 return jit::MIRType_Null;
michael@0 1284 case TYPE_FLAG_BOOLEAN:
michael@0 1285 return jit::MIRType_Boolean;
michael@0 1286 case TYPE_FLAG_INT32:
michael@0 1287 return jit::MIRType_Int32;
michael@0 1288 case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
michael@0 1289 return jit::MIRType_Double;
michael@0 1290 case TYPE_FLAG_STRING:
michael@0 1291 return jit::MIRType_String;
michael@0 1292 case TYPE_FLAG_LAZYARGS:
michael@0 1293 return jit::MIRType_MagicOptimizedArguments;
michael@0 1294 case TYPE_FLAG_ANYOBJECT:
michael@0 1295 return jit::MIRType_Object;
michael@0 1296 default:
michael@0 1297 return jit::MIRType_Value;
michael@0 1298 }
michael@0 1299 }
michael@0 1300
michael@0 1301 jit::MIRType
michael@0 1302 TemporaryTypeSet::getKnownMIRType()
michael@0 1303 {
michael@0 1304 TypeFlags flags = baseFlags();
michael@0 1305 jit::MIRType type;
michael@0 1306
michael@0 1307 if (baseObjectCount())
michael@0 1308 type = flags ? jit::MIRType_Value : jit::MIRType_Object;
michael@0 1309 else
michael@0 1310 type = GetMIRTypeFromTypeFlags(flags);
michael@0 1311
michael@0 1312 /*
michael@0 1313 * If the type set is totally empty then it will be treated as unknown,
michael@0 1314 * but we still need to record the dependency as adding a new type can give
michael@0 1315 * it a definite type tag. This is not needed if there are enough types
michael@0 1316 * that the exact tag is unknown, as it will stay unknown as more types are
michael@0 1317 * added to the set.
michael@0 1318 */
michael@0 1319 DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
michael@0 1320 JS_ASSERT_IF(empty, type == jit::MIRType_Value);
michael@0 1321
michael@0 1322 return type;
michael@0 1323 }
michael@0 1324
michael@0 1325 jit::MIRType
michael@0 1326 HeapTypeSetKey::knownMIRType(CompilerConstraintList *constraints)
michael@0 1327 {
michael@0 1328 TypeSet *types = maybeTypes();
michael@0 1329
michael@0 1330 if (!types || types->unknown())
michael@0 1331 return jit::MIRType_Value;
michael@0 1332
michael@0 1333 TypeFlags flags = types->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
michael@0 1334 jit::MIRType type;
michael@0 1335
michael@0 1336 if (types->unknownObject() || types->getObjectCount())
michael@0 1337 type = flags ? jit::MIRType_Value : jit::MIRType_Object;
michael@0 1338 else
michael@0 1339 type = GetMIRTypeFromTypeFlags(flags);
michael@0 1340
michael@0 1341 if (type != jit::MIRType_Value)
michael@0 1342 freeze(constraints);
michael@0 1343
michael@0 1344 /*
michael@0 1345 * If the type set is totally empty then it will be treated as unknown,
michael@0 1346 * but we still need to record the dependency as adding a new type can give
michael@0 1347 * it a definite type tag. This is not needed if there are enough types
michael@0 1348 * that the exact tag is unknown, as it will stay unknown as more types are
michael@0 1349 * added to the set.
michael@0 1350 */
michael@0 1351 JS_ASSERT_IF(types->empty(), type == jit::MIRType_Value);
michael@0 1352
michael@0 1353 return type;
michael@0 1354 }
michael@0 1355
michael@0 1356 bool
michael@0 1357 HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints)
michael@0 1358 {
michael@0 1359 if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
michael@0 1360 return true;
michael@0 1361 if (JSObject *obj = object()->singleton()) {
michael@0 1362 if (CanHaveEmptyPropertyTypesForOwnProperty(obj))
michael@0 1363 return true;
michael@0 1364 }
michael@0 1365 freeze(constraints);
michael@0 1366 return false;
michael@0 1367 }
michael@0 1368
michael@0 1369 bool
michael@0 1370 HeapTypeSetKey::knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other)
michael@0 1371 {
michael@0 1372 if (!maybeTypes() || maybeTypes()->empty()) {
michael@0 1373 freeze(constraints);
michael@0 1374 return true;
michael@0 1375 }
michael@0 1376 if (!other.maybeTypes() || !maybeTypes()->isSubset(other.maybeTypes()))
michael@0 1377 return false;
michael@0 1378 freeze(constraints);
michael@0 1379 return true;
michael@0 1380 }
michael@0 1381
michael@0 1382 JSObject *
michael@0 1383 TemporaryTypeSet::getSingleton()
michael@0 1384 {
michael@0 1385 if (baseFlags() != 0 || baseObjectCount() != 1)
michael@0 1386 return nullptr;
michael@0 1387
michael@0 1388 return getSingleObject(0);
michael@0 1389 }
michael@0 1390
michael@0 1391 JSObject *
michael@0 1392 HeapTypeSetKey::singleton(CompilerConstraintList *constraints)
michael@0 1393 {
michael@0 1394 HeapTypeSet *types = maybeTypes();
michael@0 1395
michael@0 1396 if (!types || types->nonDataProperty() || types->baseFlags() != 0 || types->getObjectCount() != 1)
michael@0 1397 return nullptr;
michael@0 1398
michael@0 1399 JSObject *obj = types->getSingleObject(0);
michael@0 1400
michael@0 1401 if (obj)
michael@0 1402 freeze(constraints);
michael@0 1403
michael@0 1404 return obj;
michael@0 1405 }
michael@0 1406
michael@0 1407 bool
michael@0 1408 HeapTypeSetKey::needsBarrier(CompilerConstraintList *constraints)
michael@0 1409 {
michael@0 1410 TypeSet *types = maybeTypes();
michael@0 1411 if (!types)
michael@0 1412 return false;
michael@0 1413 bool result = types->unknownObject()
michael@0 1414 || types->getObjectCount() > 0
michael@0 1415 || types->hasAnyFlag(TYPE_FLAG_STRING);
michael@0 1416 if (!result)
michael@0 1417 freeze(constraints);
michael@0 1418 return result;
michael@0 1419 }
michael@0 1420
michael@0 1421 namespace {
michael@0 1422
michael@0 1423 // Constraint which triggers recompilation if an object acquires particular flags.
michael@0 1424 class ConstraintDataFreezeObjectFlags
michael@0 1425 {
michael@0 1426 public:
michael@0 1427 // Flags we are watching for on this object.
michael@0 1428 TypeObjectFlags flags;
michael@0 1429
michael@0 1430 ConstraintDataFreezeObjectFlags(TypeObjectFlags flags)
michael@0 1431 : flags(flags)
michael@0 1432 {
michael@0 1433 JS_ASSERT(flags);
michael@0 1434 }
michael@0 1435
michael@0 1436 const char *kind() { return "freezeObjectFlags"; }
michael@0 1437
michael@0 1438 bool invalidateOnNewType(Type type) { return false; }
michael@0 1439 bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
michael@0 1440 bool invalidateOnNewObjectState(TypeObject *object) {
michael@0 1441 return object->hasAnyFlags(flags);
michael@0 1442 }
michael@0 1443
michael@0 1444 bool constraintHolds(JSContext *cx,
michael@0 1445 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
michael@0 1446 {
michael@0 1447 return !invalidateOnNewObjectState(property.object()->maybeType());
michael@0 1448 }
michael@0 1449
michael@0 1450 bool shouldSweep() { return false; }
michael@0 1451 };
michael@0 1452
michael@0 1453 } /* anonymous namespace */
michael@0 1454
michael@0 1455 bool
michael@0 1456 TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
michael@0 1457 {
michael@0 1458 JS_ASSERT(flags);
michael@0 1459
michael@0 1460 if (TypeObject *type = maybeType()) {
michael@0 1461 if (type->hasAnyFlags(flags))
michael@0 1462 return true;
michael@0 1463 }
michael@0 1464
michael@0 1465 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
michael@0 1466 LifoAlloc *alloc = constraints->alloc();
michael@0 1467
michael@0 1468 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T;
michael@0 1469 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(flags)));
michael@0 1470 return false;
michael@0 1471 }
michael@0 1472
michael@0 1473 bool
michael@0 1474 TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
michael@0 1475 {
michael@0 1476 if (unknownObject())
michael@0 1477 return true;
michael@0 1478
michael@0 1479 /*
michael@0 1480 * Treat type sets containing no objects as having all object flags,
michael@0 1481 * to spare callers from having to check this.
michael@0 1482 */
michael@0 1483 if (baseObjectCount() == 0)
michael@0 1484 return true;
michael@0 1485
michael@0 1486 unsigned count = getObjectCount();
michael@0 1487 for (unsigned i = 0; i < count; i++) {
michael@0 1488 TypeObjectKey *object = getObject(i);
michael@0 1489 if (object && object->hasFlags(constraints, flags))
michael@0 1490 return true;
michael@0 1491 }
michael@0 1492
michael@0 1493 return false;
michael@0 1494 }
michael@0 1495
michael@0 1496 gc::InitialHeap
michael@0 1497 TypeObject::initialHeap(CompilerConstraintList *constraints)
michael@0 1498 {
michael@0 1499 // If this object is not required to be pretenured but could be in the
michael@0 1500 // future, add a constraint to trigger recompilation if the requirement
michael@0 1501 // changes.
michael@0 1502
michael@0 1503 if (shouldPreTenure())
michael@0 1504 return gc::TenuredHeap;
michael@0 1505
michael@0 1506 if (!canPreTenure())
michael@0 1507 return gc::DefaultHeap;
michael@0 1508
michael@0 1509 HeapTypeSetKey objectProperty = TypeObjectKey::get(this)->property(JSID_EMPTY);
michael@0 1510 LifoAlloc *alloc = constraints->alloc();
michael@0 1511
michael@0 1512 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T;
michael@0 1513 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(OBJECT_FLAG_PRE_TENURE)));
michael@0 1514
michael@0 1515 return gc::DefaultHeap;
michael@0 1516 }
michael@0 1517
michael@0 1518 namespace {
michael@0 1519
michael@0 1520 // Constraint which triggers recompilation on any type change in an inlined
michael@0 1521 // script. The freeze constraints added to stack type sets will only directly
michael@0 1522 // invalidate the script containing those stack type sets. To invalidate code
michael@0 1523 // for scripts into which the base script was inlined, ObjectStateChange is used.
michael@0 1524 class ConstraintDataFreezeObjectForInlinedCall
michael@0 1525 {
michael@0 1526 public:
michael@0 1527 ConstraintDataFreezeObjectForInlinedCall()
michael@0 1528 {}
michael@0 1529
michael@0 1530 const char *kind() { return "freezeObjectForInlinedCall"; }
michael@0 1531
michael@0 1532 bool invalidateOnNewType(Type type) { return false; }
michael@0 1533 bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
michael@0 1534 bool invalidateOnNewObjectState(TypeObject *object) {
michael@0 1535 // We don't keep track of the exact dependencies the caller has on its
michael@0 1536 // inlined scripts' type sets, so always invalidate the caller.
michael@0 1537 return true;
michael@0 1538 }
michael@0 1539
michael@0 1540 bool constraintHolds(JSContext *cx,
michael@0 1541 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
michael@0 1542 {
michael@0 1543 return true;
michael@0 1544 }
michael@0 1545
michael@0 1546 bool shouldSweep() { return false; }
michael@0 1547 };
michael@0 1548
michael@0 1549 // Constraint which triggers recompilation when the template object for a
michael@0 1550 // type's new script changes.
michael@0 1551 class ConstraintDataFreezeObjectForNewScriptTemplate
michael@0 1552 {
michael@0 1553 JSObject *templateObject;
michael@0 1554
michael@0 1555 public:
michael@0 1556 ConstraintDataFreezeObjectForNewScriptTemplate(JSObject *templateObject)
michael@0 1557 : templateObject(templateObject)
michael@0 1558 {}
michael@0 1559
michael@0 1560 const char *kind() { return "freezeObjectForNewScriptTemplate"; }
michael@0 1561
michael@0 1562 bool invalidateOnNewType(Type type) { return false; }
michael@0 1563 bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
michael@0 1564 bool invalidateOnNewObjectState(TypeObject *object) {
michael@0 1565 return !object->hasNewScript() || object->newScript()->templateObject != templateObject;
michael@0 1566 }
michael@0 1567
michael@0 1568 bool constraintHolds(JSContext *cx,
michael@0 1569 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
michael@0 1570 {
michael@0 1571 return !invalidateOnNewObjectState(property.object()->maybeType());
michael@0 1572 }
michael@0 1573
michael@0 1574 bool shouldSweep() {
michael@0 1575 // Note: |templateObject| is only used for equality testing.
michael@0 1576 return false;
michael@0 1577 }
michael@0 1578 };
michael@0 1579
michael@0 1580 // Constraint which triggers recompilation when a typed array's data becomes
michael@0 1581 // invalid.
michael@0 1582 class ConstraintDataFreezeObjectForTypedArrayData
michael@0 1583 {
michael@0 1584 void *viewData;
michael@0 1585 uint32_t length;
michael@0 1586
michael@0 1587 public:
michael@0 1588 ConstraintDataFreezeObjectForTypedArrayData(TypedArrayObject &tarray)
michael@0 1589 : viewData(tarray.viewData()),
michael@0 1590 length(tarray.length())
michael@0 1591 {}
michael@0 1592
michael@0 1593 const char *kind() { return "freezeObjectForTypedArrayData"; }
michael@0 1594
michael@0 1595 bool invalidateOnNewType(Type type) { return false; }
michael@0 1596 bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
michael@0 1597 bool invalidateOnNewObjectState(TypeObject *object) {
michael@0 1598 TypedArrayObject &tarray = object->singleton()->as<TypedArrayObject>();
michael@0 1599 return tarray.viewData() != viewData || tarray.length() != length;
michael@0 1600 }
michael@0 1601
michael@0 1602 bool constraintHolds(JSContext *cx,
michael@0 1603 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
michael@0 1604 {
michael@0 1605 return !invalidateOnNewObjectState(property.object()->maybeType());
michael@0 1606 }
michael@0 1607
michael@0 1608 bool shouldSweep() {
michael@0 1609 // Note: |viewData| is only used for equality testing.
michael@0 1610 return false;
michael@0 1611 }
michael@0 1612 };
michael@0 1613
michael@0 1614 } /* anonymous namespace */
michael@0 1615
michael@0 1616 void
michael@0 1617 TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints)
michael@0 1618 {
michael@0 1619 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
michael@0 1620 LifoAlloc *alloc = constraints->alloc();
michael@0 1621
michael@0 1622 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForInlinedCall> T;
michael@0 1623 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectForInlinedCall()));
michael@0 1624 }
michael@0 1625
michael@0 1626 void
michael@0 1627 TypeObjectKey::watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints)
michael@0 1628 {
michael@0 1629 JSObject *templateObject = asTypeObject()->newScript()->templateObject;
michael@0 1630 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
michael@0 1631 LifoAlloc *alloc = constraints->alloc();
michael@0 1632
michael@0 1633 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForNewScriptTemplate> T;
michael@0 1634 constraints->add(alloc->new_<T>(alloc, objectProperty,
michael@0 1635 ConstraintDataFreezeObjectForNewScriptTemplate(templateObject)));
michael@0 1636 }
michael@0 1637
michael@0 1638 void
michael@0 1639 TypeObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList *constraints)
michael@0 1640 {
michael@0 1641 TypedArrayObject &tarray = asSingleObject()->as<TypedArrayObject>();
michael@0 1642 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
michael@0 1643 LifoAlloc *alloc = constraints->alloc();
michael@0 1644
michael@0 1645 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForTypedArrayData> T;
michael@0 1646 constraints->add(alloc->new_<T>(alloc, objectProperty,
michael@0 1647 ConstraintDataFreezeObjectForTypedArrayData(tarray)));
michael@0 1648 }
michael@0 1649
michael@0 1650 static void
michael@0 1651 ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown)
michael@0 1652 {
michael@0 1653 if (object->unknownProperties())
michael@0 1654 return;
michael@0 1655
michael@0 1656 /* All constraints listening to state changes are on the empty id. */
michael@0 1657 HeapTypeSet *types = object->maybeGetProperty(JSID_EMPTY);
michael@0 1658
michael@0 1659 /* Mark as unknown after getting the types, to avoid assertion. */
michael@0 1660 if (markingUnknown)
michael@0 1661 object->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
michael@0 1662
michael@0 1663 if (types) {
michael@0 1664 if (JSContext *cx = cxArg->maybeJSContext()) {
michael@0 1665 TypeConstraint *constraint = types->constraintList;
michael@0 1666 while (constraint) {
michael@0 1667 constraint->newObjectState(cx, object);
michael@0 1668 constraint = constraint->next;
michael@0 1669 }
michael@0 1670 } else {
michael@0 1671 JS_ASSERT(!types->constraintList);
michael@0 1672 }
michael@0 1673 }
michael@0 1674 }
michael@0 1675
michael@0 1676 static void
michael@0 1677 CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun);
michael@0 1678
michael@0 1679 namespace {
michael@0 1680
michael@0 1681 class ConstraintDataFreezePropertyState
michael@0 1682 {
michael@0 1683 public:
michael@0 1684 enum Which {
michael@0 1685 NON_DATA,
michael@0 1686 NON_WRITABLE
michael@0 1687 } which;
michael@0 1688
michael@0 1689 ConstraintDataFreezePropertyState(Which which)
michael@0 1690 : which(which)
michael@0 1691 {}
michael@0 1692
michael@0 1693 const char *kind() { return (which == NON_DATA) ? "freezeNonDataProperty" : "freezeNonWritableProperty"; }
michael@0 1694
michael@0 1695 bool invalidateOnNewType(Type type) { return false; }
michael@0 1696 bool invalidateOnNewPropertyState(TypeSet *property) {
michael@0 1697 return (which == NON_DATA)
michael@0 1698 ? property->nonDataProperty()
michael@0 1699 : property->nonWritableProperty();
michael@0 1700 }
michael@0 1701 bool invalidateOnNewObjectState(TypeObject *object) { return false; }
michael@0 1702
michael@0 1703 bool constraintHolds(JSContext *cx,
michael@0 1704 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
michael@0 1705 {
michael@0 1706 return !invalidateOnNewPropertyState(property.maybeTypes());
michael@0 1707 }
michael@0 1708
michael@0 1709 bool shouldSweep() { return false; }
michael@0 1710 };
michael@0 1711
michael@0 1712 } /* anonymous namespace */
michael@0 1713
michael@0 1714 bool
michael@0 1715 HeapTypeSetKey::nonData(CompilerConstraintList *constraints)
michael@0 1716 {
michael@0 1717 if (maybeTypes() && maybeTypes()->nonDataProperty())
michael@0 1718 return true;
michael@0 1719
michael@0 1720 LifoAlloc *alloc = constraints->alloc();
michael@0 1721
michael@0 1722 typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T;
michael@0 1723 constraints->add(alloc->new_<T>(alloc, *this,
michael@0 1724 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_DATA)));
michael@0 1725 return false;
michael@0 1726 }
michael@0 1727
michael@0 1728 bool
michael@0 1729 HeapTypeSetKey::nonWritable(CompilerConstraintList *constraints)
michael@0 1730 {
michael@0 1731 if (maybeTypes() && maybeTypes()->nonWritableProperty())
michael@0 1732 return true;
michael@0 1733
michael@0 1734 LifoAlloc *alloc = constraints->alloc();
michael@0 1735
michael@0 1736 typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T;
michael@0 1737 constraints->add(alloc->new_<T>(alloc, *this,
michael@0 1738 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_WRITABLE)));
michael@0 1739 return false;
michael@0 1740 }
michael@0 1741
michael@0 1742 bool
michael@0 1743 TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const
michael@0 1744 {
michael@0 1745 if (other->unknown())
michael@0 1746 return unknown();
michael@0 1747
michael@0 1748 for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
michael@0 1749 Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
michael@0 1750 if (type != filteredType && other->hasType(type) && !hasType(type))
michael@0 1751 return false;
michael@0 1752 }
michael@0 1753
michael@0 1754 if (other->unknownObject())
michael@0 1755 return unknownObject();
michael@0 1756
michael@0 1757 for (size_t i = 0; i < other->getObjectCount(); i++) {
michael@0 1758 TypeObjectKey *key = other->getObject(i);
michael@0 1759 if (key) {
michael@0 1760 Type type = Type::ObjectType(key);
michael@0 1761 if (type != filteredType && !hasType(type))
michael@0 1762 return false;
michael@0 1763 }
michael@0 1764 }
michael@0 1765
michael@0 1766 return true;
michael@0 1767 }
michael@0 1768
michael@0 1769 TemporaryTypeSet::DoubleConversion
michael@0 1770 TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints)
michael@0 1771 {
michael@0 1772 if (unknownObject() || !getObjectCount())
michael@0 1773 return AmbiguousDoubleConversion;
michael@0 1774
michael@0 1775 bool alwaysConvert = true;
michael@0 1776 bool maybeConvert = false;
michael@0 1777 bool dontConvert = false;
michael@0 1778
michael@0 1779 for (unsigned i = 0; i < getObjectCount(); i++) {
michael@0 1780 TypeObjectKey *type = getObject(i);
michael@0 1781 if (!type)
michael@0 1782 continue;
michael@0 1783
michael@0 1784 if (type->unknownProperties()) {
michael@0 1785 alwaysConvert = false;
michael@0 1786 continue;
michael@0 1787 }
michael@0 1788
michael@0 1789 HeapTypeSetKey property = type->property(JSID_VOID);
michael@0 1790 property.freeze(constraints);
michael@0 1791
michael@0 1792 // We can't convert to double elements for objects which do not have
michael@0 1793 // double in their element types (as the conversion may render the type
michael@0 1794 // information incorrect), nor for non-array objects (as their elements
michael@0 1795 // may point to emptyObjectElements, which cannot be converted).
michael@0 1796 if (!property.maybeTypes() ||
michael@0 1797 !property.maybeTypes()->hasType(Type::DoubleType()) ||
michael@0 1798 type->clasp() != &ArrayObject::class_)
michael@0 1799 {
michael@0 1800 dontConvert = true;
michael@0 1801 alwaysConvert = false;
michael@0 1802 continue;
michael@0 1803 }
michael@0 1804
michael@0 1805 // Only bother with converting known packed arrays whose possible
michael@0 1806 // element types are int or double. Other arrays require type tests
michael@0 1807 // when elements are accessed regardless of the conversion.
michael@0 1808 if (property.knownMIRType(constraints) == jit::MIRType_Double &&
michael@0 1809 !type->hasFlags(constraints, OBJECT_FLAG_NON_PACKED))
michael@0 1810 {
michael@0 1811 maybeConvert = true;
michael@0 1812 } else {
michael@0 1813 alwaysConvert = false;
michael@0 1814 }
michael@0 1815 }
michael@0 1816
michael@0 1817 JS_ASSERT_IF(alwaysConvert, maybeConvert);
michael@0 1818
michael@0 1819 if (maybeConvert && dontConvert)
michael@0 1820 return AmbiguousDoubleConversion;
michael@0 1821 if (alwaysConvert)
michael@0 1822 return AlwaysConvertToDoubles;
michael@0 1823 if (maybeConvert)
michael@0 1824 return MaybeConvertToDoubles;
michael@0 1825 return DontConvertToDoubles;
michael@0 1826 }
michael@0 1827
michael@0 1828 const Class *
michael@0 1829 TemporaryTypeSet::getKnownClass()
michael@0 1830 {
michael@0 1831 if (unknownObject())
michael@0 1832 return nullptr;
michael@0 1833
michael@0 1834 const Class *clasp = nullptr;
michael@0 1835 unsigned count = getObjectCount();
michael@0 1836
michael@0 1837 for (unsigned i = 0; i < count; i++) {
michael@0 1838 const Class *nclasp = getObjectClass(i);
michael@0 1839 if (!nclasp)
michael@0 1840 continue;
michael@0 1841
michael@0 1842 if (clasp && clasp != nclasp)
michael@0 1843 return nullptr;
michael@0 1844 clasp = nclasp;
michael@0 1845 }
michael@0 1846
michael@0 1847 return clasp;
michael@0 1848 }
michael@0 1849
michael@0 1850 TemporaryTypeSet::ForAllResult
michael@0 1851 TemporaryTypeSet::forAllClasses(bool (*func)(const Class* clasp))
michael@0 1852 {
michael@0 1853 if (unknownObject())
michael@0 1854 return ForAllResult::MIXED;
michael@0 1855
michael@0 1856 unsigned count = getObjectCount();
michael@0 1857 if (count == 0)
michael@0 1858 return ForAllResult::EMPTY;
michael@0 1859
michael@0 1860 bool true_results = false;
michael@0 1861 bool false_results = false;
michael@0 1862 for (unsigned i = 0; i < count; i++) {
michael@0 1863 const Class *clasp = getObjectClass(i);
michael@0 1864 if (!clasp)
michael@0 1865 return ForAllResult::MIXED;
michael@0 1866 if (func(clasp)) {
michael@0 1867 true_results = true;
michael@0 1868 if (false_results) return ForAllResult::MIXED;
michael@0 1869 }
michael@0 1870 else {
michael@0 1871 false_results = true;
michael@0 1872 if (true_results) return ForAllResult::MIXED;
michael@0 1873 }
michael@0 1874 }
michael@0 1875
michael@0 1876 JS_ASSERT(true_results != false_results);
michael@0 1877
michael@0 1878 return true_results ? ForAllResult::ALL_TRUE : ForAllResult::ALL_FALSE;
michael@0 1879 }
michael@0 1880
michael@0 1881 int
michael@0 1882 TemporaryTypeSet::getTypedArrayType()
michael@0 1883 {
michael@0 1884 const Class *clasp = getKnownClass();
michael@0 1885
michael@0 1886 if (clasp && IsTypedArrayClass(clasp))
michael@0 1887 return clasp - &TypedArrayObject::classes[0];
michael@0 1888 return ScalarTypeDescr::TYPE_MAX;
michael@0 1889 }
michael@0 1890
michael@0 1891 bool
michael@0 1892 TemporaryTypeSet::isDOMClass()
michael@0 1893 {
michael@0 1894 if (unknownObject())
michael@0 1895 return false;
michael@0 1896
michael@0 1897 unsigned count = getObjectCount();
michael@0 1898 for (unsigned i = 0; i < count; i++) {
michael@0 1899 const Class *clasp = getObjectClass(i);
michael@0 1900 if (clasp && !clasp->isDOMClass())
michael@0 1901 return false;
michael@0 1902 }
michael@0 1903
michael@0 1904 return count > 0;
michael@0 1905 }
michael@0 1906
michael@0 1907 bool
michael@0 1908 TemporaryTypeSet::maybeCallable()
michael@0 1909 {
michael@0 1910 if (!maybeObject())
michael@0 1911 return false;
michael@0 1912
michael@0 1913 if (unknownObject())
michael@0 1914 return true;
michael@0 1915
michael@0 1916 unsigned count = getObjectCount();
michael@0 1917 for (unsigned i = 0; i < count; i++) {
michael@0 1918 const Class *clasp = getObjectClass(i);
michael@0 1919 if (clasp && clasp->isCallable())
michael@0 1920 return true;
michael@0 1921 }
michael@0 1922
michael@0 1923 return false;
michael@0 1924 }
michael@0 1925
michael@0 1926 bool
michael@0 1927 TemporaryTypeSet::maybeEmulatesUndefined()
michael@0 1928 {
michael@0 1929 if (!maybeObject())
michael@0 1930 return false;
michael@0 1931
michael@0 1932 if (unknownObject())
michael@0 1933 return true;
michael@0 1934
michael@0 1935 unsigned count = getObjectCount();
michael@0 1936 for (unsigned i = 0; i < count; i++) {
michael@0 1937 // The object emulates undefined if clasp->emulatesUndefined() or if
michael@0 1938 // it's a WrapperObject, see EmulatesUndefined. Since all wrappers are
michael@0 1939 // proxies, we can just check for that.
michael@0 1940 const Class *clasp = getObjectClass(i);
michael@0 1941 if (clasp && (clasp->emulatesUndefined() || clasp->isProxy()))
michael@0 1942 return true;
michael@0 1943 }
michael@0 1944
michael@0 1945 return false;
michael@0 1946 }
michael@0 1947
michael@0 1948 JSObject *
michael@0 1949 TemporaryTypeSet::getCommonPrototype()
michael@0 1950 {
michael@0 1951 if (unknownObject())
michael@0 1952 return nullptr;
michael@0 1953
michael@0 1954 JSObject *proto = nullptr;
michael@0 1955 unsigned count = getObjectCount();
michael@0 1956
michael@0 1957 for (unsigned i = 0; i < count; i++) {
michael@0 1958 TypeObjectKey *object = getObject(i);
michael@0 1959 if (!object)
michael@0 1960 continue;
michael@0 1961
michael@0 1962 if (!object->hasTenuredProto())
michael@0 1963 return nullptr;
michael@0 1964
michael@0 1965 TaggedProto nproto = object->proto();
michael@0 1966 if (proto) {
michael@0 1967 if (nproto != proto)
michael@0 1968 return nullptr;
michael@0 1969 } else {
michael@0 1970 if (!nproto.isObject())
michael@0 1971 return nullptr;
michael@0 1972 proto = nproto.toObject();
michael@0 1973 }
michael@0 1974 }
michael@0 1975
michael@0 1976 return proto;
michael@0 1977 }
michael@0 1978
michael@0 1979 bool
michael@0 1980 TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id)
michael@0 1981 {
michael@0 1982 if (unknownObject())
michael@0 1983 return true;
michael@0 1984
michael@0 1985 for (unsigned i = 0; i < getObjectCount(); i++) {
michael@0 1986 TypeObjectKey *type = getObject(i);
michael@0 1987 if (!type)
michael@0 1988 continue;
michael@0 1989
michael@0 1990 if (type->unknownProperties())
michael@0 1991 return true;
michael@0 1992
michael@0 1993 HeapTypeSetKey property = type->property(id);
michael@0 1994 if (property.needsBarrier(constraints))
michael@0 1995 return true;
michael@0 1996 }
michael@0 1997
michael@0 1998 return false;
michael@0 1999 }
michael@0 2000
michael@0 2001 /////////////////////////////////////////////////////////////////////
michael@0 2002 // TypeCompartment
michael@0 2003 /////////////////////////////////////////////////////////////////////
michael@0 2004
michael@0 2005 TypeCompartment::TypeCompartment()
michael@0 2006 {
michael@0 2007 PodZero(this);
michael@0 2008 }
michael@0 2009
michael@0 2010 TypeObject *
michael@0 2011 TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto,
michael@0 2012 TypeObjectFlags initialFlags)
michael@0 2013 {
michael@0 2014 JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
michael@0 2015
michael@0 2016 if (cx->isJSContext()) {
michael@0 2017 if (proto.isObject() && IsInsideNursery(cx->asJSContext()->runtime(), proto.toObject()))
michael@0 2018 initialFlags |= OBJECT_FLAG_NURSERY_PROTO;
michael@0 2019 }
michael@0 2020
michael@0 2021 TypeObject *object = js::NewTypeObject(cx);
michael@0 2022 if (!object)
michael@0 2023 return nullptr;
michael@0 2024 new(object) TypeObject(clasp, proto, initialFlags);
michael@0 2025
michael@0 2026 return object;
michael@0 2027 }
michael@0 2028
michael@0 2029 TypeObject *
michael@0 2030 TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key)
michael@0 2031 {
michael@0 2032 AutoEnterAnalysis enter(cx);
michael@0 2033
michael@0 2034 if (!allocationSiteTable) {
michael@0 2035 allocationSiteTable = cx->new_<AllocationSiteTable>();
michael@0 2036 if (!allocationSiteTable || !allocationSiteTable->init()) {
michael@0 2037 js_delete(allocationSiteTable);
michael@0 2038 return nullptr;
michael@0 2039 }
michael@0 2040 }
michael@0 2041
michael@0 2042 AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key);
michael@0 2043 JS_ASSERT(!p);
michael@0 2044
michael@0 2045 TypeObject *res = nullptr;
michael@0 2046
michael@0 2047 jsbytecode *pc = key.script->offsetToPC(key.offset);
michael@0 2048 RootedScript keyScript(cx, key.script);
michael@0 2049
michael@0 2050 if (!res) {
michael@0 2051 RootedObject proto(cx);
michael@0 2052 if (!GetBuiltinPrototype(cx, key.kind, &proto))
michael@0 2053 return nullptr;
michael@0 2054
michael@0 2055 Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
michael@0 2056 res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE);
michael@0 2057 if (!res)
michael@0 2058 return nullptr;
michael@0 2059 key.script = keyScript;
michael@0 2060 }
michael@0 2061
michael@0 2062 if (JSOp(*pc) == JSOP_NEWOBJECT) {
michael@0 2063 /*
michael@0 2064 * This object is always constructed the same way and will not be
michael@0 2065 * observed by other code before all properties have been added. Mark
michael@0 2066 * all the properties as definite properties of the object.
michael@0 2067 */
michael@0 2068 RootedObject baseobj(cx, key.script->getObject(GET_UINT32_INDEX(pc)));
michael@0 2069
michael@0 2070 if (!res->addDefiniteProperties(cx, baseobj))
michael@0 2071 return nullptr;
michael@0 2072 }
michael@0 2073
michael@0 2074 if (!allocationSiteTable->add(p, key, res))
michael@0 2075 return nullptr;
michael@0 2076
michael@0 2077 return res;
michael@0 2078 }
michael@0 2079
michael@0 2080 static inline jsid
michael@0 2081 GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
michael@0 2082 {
michael@0 2083 PropertyName *name = script->getName(GET_UINT32_INDEX(pc + offset));
michael@0 2084 return IdToTypeId(NameToId(name));
michael@0 2085 }
michael@0 2086
michael@0 2087 bool
michael@0 2088 types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc)
michael@0 2089 {
michael@0 2090 /*
michael@0 2091 * Make a heuristic guess at a use of JSOP_NEW that the constructed object
michael@0 2092 * should have a fresh type object. We do this when the NEW is immediately
michael@0 2093 * followed by a simple assignment to an object's .prototype field.
michael@0 2094 * This is designed to catch common patterns for subclassing in JS:
michael@0 2095 *
michael@0 2096 * function Super() { ... }
michael@0 2097 * function Sub1() { ... }
michael@0 2098 * function Sub2() { ... }
michael@0 2099 *
michael@0 2100 * Sub1.prototype = new Super();
michael@0 2101 * Sub2.prototype = new Super();
michael@0 2102 *
michael@0 2103 * Using distinct type objects for the particular prototypes of Sub1 and
michael@0 2104 * Sub2 lets us continue to distinguish the two subclasses and any extra
michael@0 2105 * properties added to those prototype objects.
michael@0 2106 */
michael@0 2107 if (JSOp(*pc) == JSOP_NEW)
michael@0 2108 pc += JSOP_NEW_LENGTH;
michael@0 2109 else if (JSOp(*pc) == JSOP_SPREADNEW)
michael@0 2110 pc += JSOP_SPREADNEW_LENGTH;
michael@0 2111 else
michael@0 2112 return false;
michael@0 2113 if (JSOp(*pc) == JSOP_SETPROP) {
michael@0 2114 jsid id = GetAtomId(cx, script, pc, 0);
michael@0 2115 if (id == id_prototype(cx))
michael@0 2116 return true;
michael@0 2117 }
michael@0 2118
michael@0 2119 return false;
michael@0 2120 }
michael@0 2121
michael@0 2122 NewObjectKind
michael@0 2123 types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key)
michael@0 2124 {
michael@0 2125 /*
michael@0 2126 * Objects created outside loops in global and eval scripts should have
michael@0 2127 * singleton types. For now this is only done for plain objects and typed
michael@0 2128 * arrays, but not normal arrays.
michael@0 2129 */
michael@0 2130
michael@0 2131 if (script->functionNonDelazifying() && !script->treatAsRunOnce())
michael@0 2132 return GenericObject;
michael@0 2133
michael@0 2134 if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
michael@0 2135 return GenericObject;
michael@0 2136
michael@0 2137 /*
michael@0 2138 * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
michael@0 2139 * indicating their boundary.
michael@0 2140 */
michael@0 2141
michael@0 2142 if (!script->hasTrynotes())
michael@0 2143 return SingletonObject;
michael@0 2144
michael@0 2145 unsigned offset = script->pcToOffset(pc);
michael@0 2146
michael@0 2147 JSTryNote *tn = script->trynotes()->vector;
michael@0 2148 JSTryNote *tnlimit = tn + script->trynotes()->length;
michael@0 2149 for (; tn < tnlimit; tn++) {
michael@0 2150 if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP)
michael@0 2151 continue;
michael@0 2152
michael@0 2153 unsigned startOffset = script->mainOffset() + tn->start;
michael@0 2154 unsigned endOffset = startOffset + tn->length;
michael@0 2155
michael@0 2156 if (offset >= startOffset && offset < endOffset)
michael@0 2157 return GenericObject;
michael@0 2158 }
michael@0 2159
michael@0 2160 return SingletonObject;
michael@0 2161 }
michael@0 2162
michael@0 2163 NewObjectKind
michael@0 2164 types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp)
michael@0 2165 {
michael@0 2166 return UseNewTypeForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp));
michael@0 2167 }
michael@0 2168
michael@0 2169 static inline bool
michael@0 2170 ClassCanHaveExtraProperties(const Class *clasp)
michael@0 2171 {
michael@0 2172 JS_ASSERT(clasp->resolve);
michael@0 2173 return clasp->resolve != JS_ResolveStub
michael@0 2174 || clasp->ops.lookupGeneric
michael@0 2175 || clasp->ops.getGeneric
michael@0 2176 || IsTypedArrayClass(clasp);
michael@0 2177 }
michael@0 2178
michael@0 2179 static inline bool
michael@0 2180 PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj)
michael@0 2181 {
michael@0 2182 do {
michael@0 2183 TypeObjectKey *type = TypeObjectKey::get(obj);
michael@0 2184 if (ClassCanHaveExtraProperties(type->clasp()))
michael@0 2185 return true;
michael@0 2186 if (type->unknownProperties())
michael@0 2187 return true;
michael@0 2188 HeapTypeSetKey index = type->property(JSID_VOID);
michael@0 2189 if (index.nonData(constraints) || index.isOwnProperty(constraints))
michael@0 2190 return true;
michael@0 2191 if (!obj->hasTenuredProto())
michael@0 2192 return true;
michael@0 2193 obj = obj->getProto();
michael@0 2194 } while (obj);
michael@0 2195
michael@0 2196 return false;
michael@0 2197 }
michael@0 2198
michael@0 2199 bool
michael@0 2200 types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSScript *script)
michael@0 2201 {
michael@0 2202 if (JSObject *proto = script->global().maybeGetArrayPrototype())
michael@0 2203 return PrototypeHasIndexedProperty(constraints, proto);
michael@0 2204 return true;
michael@0 2205 }
michael@0 2206
michael@0 2207 bool
michael@0 2208 types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
michael@0 2209 TemporaryTypeSet *types)
michael@0 2210 {
michael@0 2211 const Class *clasp = types->getKnownClass();
michael@0 2212
michael@0 2213 // Note: typed arrays have indexed properties not accounted for by type
michael@0 2214 // information, though these are all in bounds and will be accounted for
michael@0 2215 // by JIT paths.
michael@0 2216 if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp)))
michael@0 2217 return true;
michael@0 2218
michael@0 2219 if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES))
michael@0 2220 return true;
michael@0 2221
michael@0 2222 JSObject *proto = types->getCommonPrototype();
michael@0 2223 if (!proto)
michael@0 2224 return true;
michael@0 2225
michael@0 2226 return PrototypeHasIndexedProperty(constraints, proto);
michael@0 2227 }
michael@0 2228
michael@0 2229 void
michael@0 2230 TypeZone::processPendingRecompiles(FreeOp *fop)
michael@0 2231 {
michael@0 2232 if (!pendingRecompiles)
michael@0 2233 return;
michael@0 2234
michael@0 2235 /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
michael@0 2236 Vector<RecompileInfo> *pending = pendingRecompiles;
michael@0 2237 pendingRecompiles = nullptr;
michael@0 2238
michael@0 2239 JS_ASSERT(!pending->empty());
michael@0 2240
michael@0 2241 #ifdef JS_ION
michael@0 2242 jit::Invalidate(*this, fop, *pending);
michael@0 2243 #endif
michael@0 2244
michael@0 2245 fop->delete_(pending);
michael@0 2246 }
michael@0 2247
michael@0 2248 void
michael@0 2249 TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
michael@0 2250 {
michael@0 2251 CompilerOutput *co = info.compilerOutput(cx);
michael@0 2252 if (!co || !co->isValid() || co->pendingInvalidation())
michael@0 2253 return;
michael@0 2254
michael@0 2255 InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d",
michael@0 2256 co->script(), co->script()->filename(), co->script()->lineno());
michael@0 2257
michael@0 2258 co->setPendingInvalidation();
michael@0 2259
michael@0 2260 if (!pendingRecompiles) {
michael@0 2261 pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
michael@0 2262 if (!pendingRecompiles)
michael@0 2263 CrashAtUnhandlableOOM("Could not update pendingRecompiles");
michael@0 2264 }
michael@0 2265
michael@0 2266 if (!pendingRecompiles->append(info))
michael@0 2267 CrashAtUnhandlableOOM("Could not update pendingRecompiles");
michael@0 2268 }
michael@0 2269
michael@0 2270 void
michael@0 2271 TypeZone::addPendingRecompile(JSContext *cx, JSScript *script)
michael@0 2272 {
michael@0 2273 JS_ASSERT(script);
michael@0 2274
michael@0 2275 #ifdef JS_ION
michael@0 2276 CancelOffThreadIonCompile(cx->compartment(), script);
michael@0 2277
michael@0 2278 // Let the script warm up again before attempting another compile.
michael@0 2279 if (jit::IsBaselineEnabled(cx))
michael@0 2280 script->resetUseCount();
michael@0 2281
michael@0 2282 if (script->hasIonScript())
michael@0 2283 addPendingRecompile(cx, script->ionScript()->recompileInfo());
michael@0 2284
michael@0 2285 if (script->hasParallelIonScript())
michael@0 2286 addPendingRecompile(cx, script->parallelIonScript()->recompileInfo());
michael@0 2287 #endif
michael@0 2288
michael@0 2289 // When one script is inlined into another the caller listens to state
michael@0 2290 // changes on the callee's script, so trigger these to force recompilation
michael@0 2291 // of any such callers.
michael@0 2292 if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyType())
michael@0 2293 ObjectStateChange(cx, script->functionNonDelazifying()->type(), false);
michael@0 2294 }
michael@0 2295
michael@0 2296 void
michael@0 2297 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
michael@0 2298 {
michael@0 2299 JS_ASSERT(this == &cx->compartment()->types);
michael@0 2300 JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
michael@0 2301 JS_ASSERT(!target->singleton());
michael@0 2302 JS_ASSERT(target->unknownProperties());
michael@0 2303
michael@0 2304 AutoEnterAnalysis enter(cx);
michael@0 2305
michael@0 2306 /* Mark type sets which contain obj as having a generic object types. */
michael@0 2307
michael@0 2308 for (gc::CellIter i(cx->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
michael@0 2309 TypeObject *object = i.get<TypeObject>();
michael@0 2310 unsigned count = object->getPropertyCount();
michael@0 2311 for (unsigned i = 0; i < count; i++) {
michael@0 2312 Property *prop = object->getProperty(i);
michael@0 2313 if (prop && prop->types.hasType(Type::ObjectType(target)))
michael@0 2314 prop->types.addType(cx, Type::AnyObjectType());
michael@0 2315 }
michael@0 2316 }
michael@0 2317
michael@0 2318 for (gc::CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
michael@0 2319 RootedScript script(cx, i.get<JSScript>());
michael@0 2320 if (script->types) {
michael@0 2321 unsigned count = TypeScript::NumTypeSets(script);
michael@0 2322 StackTypeSet *typeArray = script->types->typeArray();
michael@0 2323 for (unsigned i = 0; i < count; i++) {
michael@0 2324 if (typeArray[i].hasType(Type::ObjectType(target)))
michael@0 2325 typeArray[i].addType(cx, Type::AnyObjectType());
michael@0 2326 }
michael@0 2327 }
michael@0 2328 }
michael@0 2329
michael@0 2330 target->addFlags(OBJECT_FLAG_SETS_MARKED_UNKNOWN);
michael@0 2331 }
michael@0 2332
michael@0 2333 void
michael@0 2334 TypeCompartment::print(JSContext *cx, bool force)
michael@0 2335 {
michael@0 2336 #ifdef DEBUG
michael@0 2337 gc::AutoSuppressGC suppressGC(cx);
michael@0 2338
michael@0 2339 JSCompartment *compartment = this->compartment();
michael@0 2340 AutoEnterAnalysis enter(nullptr, compartment);
michael@0 2341
michael@0 2342 if (!force && !InferSpewActive(ISpewResult))
michael@0 2343 return;
michael@0 2344
michael@0 2345 for (gc::CellIter i(compartment->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
michael@0 2346 // Note: use cx->runtime() instead of cx to work around IsInRequest(cx)
michael@0 2347 // assertion failures when we're called from DestroyContext.
michael@0 2348 RootedScript script(cx->runtime(), i.get<JSScript>());
michael@0 2349 if (script->types)
michael@0 2350 script->types->printTypes(cx, script);
michael@0 2351 }
michael@0 2352
michael@0 2353 for (gc::CellIter i(compartment->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
michael@0 2354 TypeObject *object = i.get<TypeObject>();
michael@0 2355 object->print();
michael@0 2356 }
michael@0 2357 #endif
michael@0 2358 }
michael@0 2359
michael@0 2360 /////////////////////////////////////////////////////////////////////
michael@0 2361 // TypeCompartment tables
michael@0 2362 /////////////////////////////////////////////////////////////////////
michael@0 2363
michael@0 2364 /*
michael@0 2365 * The arrayTypeTable and objectTypeTable are per-compartment tables for making
michael@0 2366 * common type objects to model the contents of large script singletons and
michael@0 2367 * JSON objects. These are vanilla Arrays and native Objects, so we distinguish
michael@0 2368 * the types of different ones by looking at the types of their properties.
michael@0 2369 *
michael@0 2370 * All singleton/JSON arrays which have the same prototype, are homogenous and
michael@0 2371 * of the same element type will share a type object. All singleton/JSON
michael@0 2372 * objects which have the same shape and property types will also share a type
michael@0 2373 * object. We don't try to collate arrays or objects that have type mismatches.
michael@0 2374 */
michael@0 2375
michael@0 2376 static inline bool
michael@0 2377 NumberTypes(Type a, Type b)
michael@0 2378 {
michael@0 2379 return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
michael@0 2380 && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
michael@0 2381 }
michael@0 2382
michael@0 2383 /*
michael@0 2384 * As for GetValueType, but requires object types to be non-singletons with
michael@0 2385 * their default prototype. These are the only values that should appear in
michael@0 2386 * arrays and objects whose type can be fixed.
michael@0 2387 */
michael@0 2388 static inline Type
michael@0 2389 GetValueTypeForTable(const Value &v)
michael@0 2390 {
michael@0 2391 Type type = GetValueType(v);
michael@0 2392 JS_ASSERT(!type.isSingleObject());
michael@0 2393 return type;
michael@0 2394 }
michael@0 2395
michael@0 2396 struct types::ArrayTableKey : public DefaultHasher<types::ArrayTableKey>
michael@0 2397 {
michael@0 2398 Type type;
michael@0 2399 JSObject *proto;
michael@0 2400
michael@0 2401 ArrayTableKey()
michael@0 2402 : type(Type::UndefinedType()), proto(nullptr)
michael@0 2403 {}
michael@0 2404
michael@0 2405 ArrayTableKey(Type type, JSObject *proto)
michael@0 2406 : type(type), proto(proto)
michael@0 2407 {}
michael@0 2408
michael@0 2409 static inline uint32_t hash(const ArrayTableKey &v) {
michael@0 2410 return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2));
michael@0 2411 }
michael@0 2412
michael@0 2413 static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) {
michael@0 2414 return v1.type == v2.type && v1.proto == v2.proto;
michael@0 2415 }
michael@0 2416 };
michael@0 2417
michael@0 2418 void
michael@0 2419 TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx,
michael@0 2420 JSObject *obj, Type elementType)
michael@0 2421 {
michael@0 2422 JS_ASSERT(cx->compartment()->activeAnalysis);
michael@0 2423
michael@0 2424 if (!arrayTypeTable) {
michael@0 2425 arrayTypeTable = cx->new_<ArrayTypeTable>();
michael@0 2426 if (!arrayTypeTable || !arrayTypeTable->init()) {
michael@0 2427 arrayTypeTable = nullptr;
michael@0 2428 return;
michael@0 2429 }
michael@0 2430 }
michael@0 2431
michael@0 2432 ArrayTableKey key(elementType, obj->getProto());
michael@0 2433 DependentAddPtr<ArrayTypeTable> p(cx, *arrayTypeTable, key);
michael@0 2434 if (p) {
michael@0 2435 obj->setType(p->value());
michael@0 2436 } else {
michael@0 2437 /* Make a new type to use for future arrays with the same elements. */
michael@0 2438 RootedObject objProto(cx, obj->getProto());
michael@0 2439 TypeObject *objType = newTypeObject(cx, &ArrayObject::class_, objProto);
michael@0 2440 if (!objType)
michael@0 2441 return;
michael@0 2442 obj->setType(objType);
michael@0 2443
michael@0 2444 if (!objType->unknownProperties())
michael@0 2445 objType->addPropertyType(cx, JSID_VOID, elementType);
michael@0 2446
michael@0 2447 key.proto = objProto;
michael@0 2448 (void) p.add(cx, *arrayTypeTable, key, objType);
michael@0 2449 }
michael@0 2450 }
michael@0 2451
michael@0 2452 void
michael@0 2453 TypeCompartment::fixArrayType(ExclusiveContext *cx, JSObject *obj)
michael@0 2454 {
michael@0 2455 AutoEnterAnalysis enter(cx);
michael@0 2456
michael@0 2457 /*
michael@0 2458 * If the array is of homogenous type, pick a type object which will be
michael@0 2459 * shared with all other singleton/JSON arrays of the same type.
michael@0 2460 * If the array is heterogenous, keep the existing type object, which has
michael@0 2461 * unknown properties.
michael@0 2462 */
michael@0 2463 JS_ASSERT(obj->is<ArrayObject>());
michael@0 2464
michael@0 2465 unsigned len = obj->getDenseInitializedLength();
michael@0 2466 if (len == 0)
michael@0 2467 return;
michael@0 2468
michael@0 2469 Type type = GetValueTypeForTable(obj->getDenseElement(0));
michael@0 2470
michael@0 2471 for (unsigned i = 1; i < len; i++) {
michael@0 2472 Type ntype = GetValueTypeForTable(obj->getDenseElement(i));
michael@0 2473 if (ntype != type) {
michael@0 2474 if (NumberTypes(type, ntype))
michael@0 2475 type = Type::DoubleType();
michael@0 2476 else
michael@0 2477 return;
michael@0 2478 }
michael@0 2479 }
michael@0 2480
michael@0 2481 setTypeToHomogenousArray(cx, obj, type);
michael@0 2482 }
michael@0 2483
michael@0 2484 void
michael@0 2485 types::FixRestArgumentsType(ExclusiveContext *cx, JSObject *obj)
michael@0 2486 {
michael@0 2487 cx->compartment()->types.fixRestArgumentsType(cx, obj);
michael@0 2488 }
michael@0 2489
michael@0 2490 void
michael@0 2491 TypeCompartment::fixRestArgumentsType(ExclusiveContext *cx, JSObject *obj)
michael@0 2492 {
michael@0 2493 AutoEnterAnalysis enter(cx);
michael@0 2494
michael@0 2495 /*
michael@0 2496 * Tracking element types for rest argument arrays is not worth it, but we
michael@0 2497 * still want it to be known that it's a dense array.
michael@0 2498 */
michael@0 2499 JS_ASSERT(obj->is<ArrayObject>());
michael@0 2500
michael@0 2501 setTypeToHomogenousArray(cx, obj, Type::UnknownType());
michael@0 2502 }
michael@0 2503
michael@0 2504 /*
michael@0 2505 * N.B. We could also use the initial shape of the object (before its type is
michael@0 2506 * fixed) as the key in the object table, but since all references in the table
michael@0 2507 * are weak the hash entries would usually be collected on GC even if objects
michael@0 2508 * with the new type/shape are still live.
michael@0 2509 */
michael@0 2510 struct types::ObjectTableKey
michael@0 2511 {
michael@0 2512 jsid *properties;
michael@0 2513 uint32_t nproperties;
michael@0 2514 uint32_t nfixed;
michael@0 2515
michael@0 2516 struct Lookup {
michael@0 2517 IdValuePair *properties;
michael@0 2518 uint32_t nproperties;
michael@0 2519 uint32_t nfixed;
michael@0 2520
michael@0 2521 Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed)
michael@0 2522 : properties(properties), nproperties(nproperties), nfixed(nfixed)
michael@0 2523 {}
michael@0 2524 };
michael@0 2525
michael@0 2526 static inline HashNumber hash(const Lookup &lookup) {
michael@0 2527 return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
michael@0 2528 lookup.nproperties ^
michael@0 2529 lookup.nfixed);
michael@0 2530 }
michael@0 2531
michael@0 2532 static inline bool match(const ObjectTableKey &v, const Lookup &lookup) {
michael@0 2533 if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed)
michael@0 2534 return false;
michael@0 2535 for (size_t i = 0; i < lookup.nproperties; i++) {
michael@0 2536 if (lookup.properties[i].id != v.properties[i])
michael@0 2537 return false;
michael@0 2538 }
michael@0 2539 return true;
michael@0 2540 }
michael@0 2541 };
michael@0 2542
michael@0 2543 struct types::ObjectTableEntry
michael@0 2544 {
michael@0 2545 ReadBarriered<TypeObject> object;
michael@0 2546 ReadBarriered<Shape> shape;
michael@0 2547 Type *types;
michael@0 2548 };
michael@0 2549
michael@0 2550 static inline void
michael@0 2551 UpdateObjectTableEntryTypes(ExclusiveContext *cx, ObjectTableEntry &entry,
michael@0 2552 IdValuePair *properties, size_t nproperties)
michael@0 2553 {
michael@0 2554 if (entry.object->unknownProperties())
michael@0 2555 return;
michael@0 2556 for (size_t i = 0; i < nproperties; i++) {
michael@0 2557 Type type = entry.types[i];
michael@0 2558 Type ntype = GetValueTypeForTable(properties[i].value);
michael@0 2559 if (ntype == type)
michael@0 2560 continue;
michael@0 2561 if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
michael@0 2562 type.isPrimitive(JSVAL_TYPE_DOUBLE))
michael@0 2563 {
michael@0 2564 /* The property types already reflect 'int32'. */
michael@0 2565 } else {
michael@0 2566 if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
michael@0 2567 type.isPrimitive(JSVAL_TYPE_INT32))
michael@0 2568 {
michael@0 2569 /* Include 'double' in the property types to avoid the update below later. */
michael@0 2570 entry.types[i] = Type::DoubleType();
michael@0 2571 }
michael@0 2572 entry.object->addPropertyType(cx, IdToTypeId(properties[i].id), ntype);
michael@0 2573 }
michael@0 2574 }
michael@0 2575 }
michael@0 2576
michael@0 2577 void
michael@0 2578 TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj)
michael@0 2579 {
michael@0 2580 AutoEnterAnalysis enter(cx);
michael@0 2581
michael@0 2582 if (!objectTypeTable) {
michael@0 2583 objectTypeTable = cx->new_<ObjectTypeTable>();
michael@0 2584 if (!objectTypeTable || !objectTypeTable->init()) {
michael@0 2585 js_delete(objectTypeTable);
michael@0 2586 objectTypeTable = nullptr;
michael@0 2587 return;
michael@0 2588 }
michael@0 2589 }
michael@0 2590
michael@0 2591 /*
michael@0 2592 * Use the same type object for all singleton/JSON objects with the same
michael@0 2593 * base shape, i.e. the same fields written in the same order.
michael@0 2594 */
michael@0 2595 JS_ASSERT(obj->is<JSObject>());
michael@0 2596
michael@0 2597 /*
michael@0 2598 * Exclude some objects we can't readily associate common types for based on their
michael@0 2599 * shape. Objects with metadata are excluded so that the metadata does not need to
michael@0 2600 * be included in the table lookup (the metadata object might be in the nursery).
michael@0 2601 */
michael@0 2602 if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata())
michael@0 2603 return;
michael@0 2604
michael@0 2605 Vector<IdValuePair> properties(cx);
michael@0 2606 if (!properties.resize(obj->slotSpan()))
michael@0 2607 return;
michael@0 2608
michael@0 2609 Shape *shape = obj->lastProperty();
michael@0 2610 while (!shape->isEmptyShape()) {
michael@0 2611 IdValuePair &entry = properties[shape->slot()];
michael@0 2612 entry.id = shape->propid();
michael@0 2613 entry.value = obj->getSlot(shape->slot());
michael@0 2614 shape = shape->previous();
michael@0 2615 }
michael@0 2616
michael@0 2617 ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots());
michael@0 2618 ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
michael@0 2619
michael@0 2620 if (p) {
michael@0 2621 JS_ASSERT(obj->getProto() == p->value().object->proto().toObject());
michael@0 2622 JS_ASSERT(obj->lastProperty() == p->value().shape);
michael@0 2623
michael@0 2624 UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length());
michael@0 2625 obj->setType(p->value().object);
michael@0 2626 return;
michael@0 2627 }
michael@0 2628
michael@0 2629 /* Make a new type to use for the object and similar future ones. */
michael@0 2630 Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
michael@0 2631 TypeObject *objType = newTypeObject(cx, &JSObject::class_, objProto);
michael@0 2632 if (!objType || !objType->addDefiniteProperties(cx, obj))
michael@0 2633 return;
michael@0 2634
michael@0 2635 if (obj->isIndexed())
michael@0 2636 objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
michael@0 2637
michael@0 2638 ScopedJSFreePtr<jsid> ids(cx->pod_calloc<jsid>(properties.length()));
michael@0 2639 if (!ids)
michael@0 2640 return;
michael@0 2641
michael@0 2642 ScopedJSFreePtr<Type> types(cx->pod_calloc<Type>(properties.length()));
michael@0 2643 if (!types)
michael@0 2644 return;
michael@0 2645
michael@0 2646 for (size_t i = 0; i < properties.length(); i++) {
michael@0 2647 ids[i] = properties[i].id;
michael@0 2648 types[i] = GetValueTypeForTable(obj->getSlot(i));
michael@0 2649 if (!objType->unknownProperties())
michael@0 2650 objType->addPropertyType(cx, IdToTypeId(ids[i]), types[i]);
michael@0 2651 }
michael@0 2652
michael@0 2653 ObjectTableKey key;
michael@0 2654 key.properties = ids;
michael@0 2655 key.nproperties = properties.length();
michael@0 2656 key.nfixed = obj->numFixedSlots();
michael@0 2657 JS_ASSERT(ObjectTableKey::match(key, lookup));
michael@0 2658
michael@0 2659 ObjectTableEntry entry;
michael@0 2660 entry.object = objType;
michael@0 2661 entry.shape = obj->lastProperty();
michael@0 2662 entry.types = types;
michael@0 2663
michael@0 2664 obj->setType(objType);
michael@0 2665
michael@0 2666 p = objectTypeTable->lookupForAdd(lookup);
michael@0 2667 if (objectTypeTable->add(p, key, entry)) {
michael@0 2668 ids.forget();
michael@0 2669 types.forget();
michael@0 2670 }
michael@0 2671 }
michael@0 2672
michael@0 2673 JSObject *
michael@0 2674 TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties)
michael@0 2675 {
michael@0 2676 AutoEnterAnalysis enter(cx);
michael@0 2677
michael@0 2678 if (!objectTypeTable) {
michael@0 2679 objectTypeTable = cx->new_<ObjectTypeTable>();
michael@0 2680 if (!objectTypeTable || !objectTypeTable->init()) {
michael@0 2681 js_delete(objectTypeTable);
michael@0 2682 objectTypeTable = nullptr;
michael@0 2683 return nullptr;
michael@0 2684 }
michael@0 2685 }
michael@0 2686
michael@0 2687 /*
michael@0 2688 * Use the object type table to allocate an object with the specified
michael@0 2689 * properties, filling in its final type and shape and failing if no cache
michael@0 2690 * entry could be found for the properties.
michael@0 2691 */
michael@0 2692
michael@0 2693 /*
michael@0 2694 * Filter out a few cases where we don't want to use the object type table.
michael@0 2695 * Note that if the properties contain any duplicates or dense indexes,
michael@0 2696 * the lookup below will fail as such arrays of properties cannot be stored
michael@0 2697 * in the object type table --- fixObjectType populates the table with
michael@0 2698 * properties read off its input object, which cannot be duplicates, and
michael@0 2699 * ignores objects with dense indexes.
michael@0 2700 */
michael@0 2701 if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT)
michael@0 2702 return nullptr;
michael@0 2703
michael@0 2704 gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
michael@0 2705 size_t nfixed = gc::GetGCKindSlots(allocKind, &JSObject::class_);
michael@0 2706
michael@0 2707 ObjectTableKey::Lookup lookup(properties, nproperties, nfixed);
michael@0 2708 ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
michael@0 2709
michael@0 2710 if (!p)
michael@0 2711 return nullptr;
michael@0 2712
michael@0 2713 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind));
michael@0 2714 if (!obj) {
michael@0 2715 cx->clearPendingException();
michael@0 2716 return nullptr;
michael@0 2717 }
michael@0 2718 JS_ASSERT(obj->getProto() == p->value().object->proto().toObject());
michael@0 2719
michael@0 2720 RootedShape shape(cx, p->value().shape);
michael@0 2721 if (!JSObject::setLastProperty(cx, obj, shape)) {
michael@0 2722 cx->clearPendingException();
michael@0 2723 return nullptr;
michael@0 2724 }
michael@0 2725
michael@0 2726 UpdateObjectTableEntryTypes(cx, p->value(), properties, nproperties);
michael@0 2727
michael@0 2728 for (size_t i = 0; i < nproperties; i++)
michael@0 2729 obj->setSlot(i, properties[i].value);
michael@0 2730
michael@0 2731 obj->setType(p->value().object);
michael@0 2732 return obj;
michael@0 2733 }
michael@0 2734
michael@0 2735 /////////////////////////////////////////////////////////////////////
michael@0 2736 // TypeObject
michael@0 2737 /////////////////////////////////////////////////////////////////////
michael@0 2738
michael@0 2739 void
michael@0 2740 TypeObject::setProto(JSContext *cx, TaggedProto proto)
michael@0 2741 {
michael@0 2742 JS_ASSERT(singleton());
michael@0 2743
michael@0 2744 if (proto.isObject() && IsInsideNursery(cx->runtime(), proto.toObject()))
michael@0 2745 addFlags(OBJECT_FLAG_NURSERY_PROTO);
michael@0 2746
michael@0 2747 setProtoUnchecked(proto);
michael@0 2748 }
michael@0 2749
michael@0 2750 static inline void
michael@0 2751 UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shape *shape,
michael@0 2752 bool indexed)
michael@0 2753 {
michael@0 2754 if (!shape->writable())
michael@0 2755 types->setNonWritableProperty(cx);
michael@0 2756
michael@0 2757 if (shape->hasGetterValue() || shape->hasSetterValue()) {
michael@0 2758 types->setNonDataProperty(cx);
michael@0 2759 types->TypeSet::addType(Type::UnknownType(), &cx->typeLifoAlloc());
michael@0 2760 } else if (shape->hasDefaultGetter() && shape->hasSlot()) {
michael@0 2761 if (!indexed && types->canSetDefinite(shape->slot()))
michael@0 2762 types->setDefinite(shape->slot());
michael@0 2763
michael@0 2764 const Value &value = obj->nativeGetSlot(shape->slot());
michael@0 2765
michael@0 2766 /*
michael@0 2767 * Don't add initial undefined types for properties of global objects
michael@0 2768 * that are not collated into the JSID_VOID property (see propertySet
michael@0 2769 * comment).
michael@0 2770 */
michael@0 2771 if (indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) {
michael@0 2772 Type type = GetValueType(value);
michael@0 2773 types->TypeSet::addType(type, &cx->typeLifoAlloc());
michael@0 2774 }
michael@0 2775 }
michael@0 2776 }
michael@0 2777
michael@0 2778 void
michael@0 2779 TypeObject::updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types)
michael@0 2780 {
michael@0 2781 InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
michael@0 2782 InferSpewColor(types), types, InferSpewColorReset(),
michael@0 2783 TypeObjectString(this), TypeIdString(id));
michael@0 2784
michael@0 2785 if (!singleton() || !singleton()->isNative())
michael@0 2786 return;
michael@0 2787
michael@0 2788 /*
michael@0 2789 * Fill the property in with any type the object already has in an own
michael@0 2790 * property. We are only interested in plain native properties and
michael@0 2791 * dense elements which don't go through a barrier when read by the VM
michael@0 2792 * or jitcode.
michael@0 2793 */
michael@0 2794
michael@0 2795 if (JSID_IS_VOID(id)) {
michael@0 2796 /* Go through all shapes on the object to get integer-valued properties. */
michael@0 2797 RootedShape shape(cx, singleton()->lastProperty());
michael@0 2798 while (!shape->isEmptyShape()) {
michael@0 2799 if (JSID_IS_VOID(IdToTypeId(shape->propid())))
michael@0 2800 UpdatePropertyType(cx, types, singleton(), shape, true);
michael@0 2801 shape = shape->previous();
michael@0 2802 }
michael@0 2803
michael@0 2804 /* Also get values of any dense elements in the object. */
michael@0 2805 for (size_t i = 0; i < singleton()->getDenseInitializedLength(); i++) {
michael@0 2806 const Value &value = singleton()->getDenseElement(i);
michael@0 2807 if (!value.isMagic(JS_ELEMENTS_HOLE)) {
michael@0 2808 Type type = GetValueType(value);
michael@0 2809 types->TypeSet::addType(type, &cx->typeLifoAlloc());
michael@0 2810 }
michael@0 2811 }
michael@0 2812 } else if (!JSID_IS_EMPTY(id)) {
michael@0 2813 RootedId rootedId(cx, id);
michael@0 2814 Shape *shape = singleton()->nativeLookup(cx, rootedId);
michael@0 2815 if (shape)
michael@0 2816 UpdatePropertyType(cx, types, singleton(), shape, false);
michael@0 2817 }
michael@0 2818
michael@0 2819 if (singleton()->watched()) {
michael@0 2820 /*
michael@0 2821 * Mark the property as non-data, to inhibit optimizations on it
michael@0 2822 * and avoid bypassing the watchpoint handler.
michael@0 2823 */
michael@0 2824 types->setNonDataProperty(cx);
michael@0 2825 }
michael@0 2826 }
michael@0 2827
michael@0 2828 bool
michael@0 2829 TypeObject::addDefiniteProperties(ExclusiveContext *cx, JSObject *obj)
michael@0 2830 {
michael@0 2831 if (unknownProperties())
michael@0 2832 return true;
michael@0 2833
michael@0 2834 /* Mark all properties of obj as definite properties of this type. */
michael@0 2835 AutoEnterAnalysis enter(cx);
michael@0 2836
michael@0 2837 RootedShape shape(cx, obj->lastProperty());
michael@0 2838 while (!shape->isEmptyShape()) {
michael@0 2839 jsid id = IdToTypeId(shape->propid());
michael@0 2840 if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot())) {
michael@0 2841 TypeSet *types = getProperty(cx, id);
michael@0 2842 if (!types)
michael@0 2843 return false;
michael@0 2844 types->setDefinite(shape->slot());
michael@0 2845 }
michael@0 2846 shape = shape->previous();
michael@0 2847 }
michael@0 2848
michael@0 2849 return true;
michael@0 2850 }
michael@0 2851
michael@0 2852 bool
michael@0 2853 TypeObject::matchDefiniteProperties(HandleObject obj)
michael@0 2854 {
michael@0 2855 unsigned count = getPropertyCount();
michael@0 2856 for (unsigned i = 0; i < count; i++) {
michael@0 2857 Property *prop = getProperty(i);
michael@0 2858 if (!prop)
michael@0 2859 continue;
michael@0 2860 if (prop->types.definiteProperty()) {
michael@0 2861 unsigned slot = prop->types.definiteSlot();
michael@0 2862
michael@0 2863 bool found = false;
michael@0 2864 Shape *shape = obj->lastProperty();
michael@0 2865 while (!shape->isEmptyShape()) {
michael@0 2866 if (shape->slot() == slot && shape->propid() == prop->id) {
michael@0 2867 found = true;
michael@0 2868 break;
michael@0 2869 }
michael@0 2870 shape = shape->previous();
michael@0 2871 }
michael@0 2872 if (!found)
michael@0 2873 return false;
michael@0 2874 }
michael@0 2875 }
michael@0 2876
michael@0 2877 return true;
michael@0 2878 }
michael@0 2879
michael@0 2880 static inline void
michael@0 2881 InlineAddTypeProperty(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type)
michael@0 2882 {
michael@0 2883 JS_ASSERT(id == IdToTypeId(id));
michael@0 2884
michael@0 2885 AutoEnterAnalysis enter(cx);
michael@0 2886
michael@0 2887 HeapTypeSet *types = obj->getProperty(cx, id);
michael@0 2888 if (!types || types->hasType(type))
michael@0 2889 return;
michael@0 2890
michael@0 2891 InferSpew(ISpewOps, "externalType: property %s %s: %s",
michael@0 2892 TypeObjectString(obj), TypeIdString(id), TypeString(type));
michael@0 2893 types->addType(cx, type);
michael@0 2894 }
michael@0 2895
michael@0 2896 void
michael@0 2897 TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, Type type)
michael@0 2898 {
michael@0 2899 InlineAddTypeProperty(cx, this, id, type);
michael@0 2900 }
michael@0 2901
michael@0 2902 void
michael@0 2903 TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, const Value &value)
michael@0 2904 {
michael@0 2905 InlineAddTypeProperty(cx, this, id, GetValueType(value));
michael@0 2906 }
michael@0 2907
michael@0 2908 void
michael@0 2909 TypeObject::markPropertyNonData(ExclusiveContext *cx, jsid id)
michael@0 2910 {
michael@0 2911 AutoEnterAnalysis enter(cx);
michael@0 2912
michael@0 2913 id = IdToTypeId(id);
michael@0 2914
michael@0 2915 HeapTypeSet *types = getProperty(cx, id);
michael@0 2916 if (types)
michael@0 2917 types->setNonDataProperty(cx);
michael@0 2918 }
michael@0 2919
michael@0 2920 void
michael@0 2921 TypeObject::markPropertyNonWritable(ExclusiveContext *cx, jsid id)
michael@0 2922 {
michael@0 2923 AutoEnterAnalysis enter(cx);
michael@0 2924
michael@0 2925 id = IdToTypeId(id);
michael@0 2926
michael@0 2927 HeapTypeSet *types = getProperty(cx, id);
michael@0 2928 if (types)
michael@0 2929 types->setNonWritableProperty(cx);
michael@0 2930 }
michael@0 2931
michael@0 2932 bool
michael@0 2933 TypeObject::isPropertyNonData(jsid id)
michael@0 2934 {
michael@0 2935 TypeSet *types = maybeGetProperty(id);
michael@0 2936 if (types)
michael@0 2937 return types->nonDataProperty();
michael@0 2938 return false;
michael@0 2939 }
michael@0 2940
michael@0 2941 bool
michael@0 2942 TypeObject::isPropertyNonWritable(jsid id)
michael@0 2943 {
michael@0 2944 TypeSet *types = maybeGetProperty(id);
michael@0 2945 if (types)
michael@0 2946 return types->nonWritableProperty();
michael@0 2947 return false;
michael@0 2948 }
michael@0 2949
michael@0 2950 void
michael@0 2951 TypeObject::markStateChange(ExclusiveContext *cxArg)
michael@0 2952 {
michael@0 2953 if (unknownProperties())
michael@0 2954 return;
michael@0 2955
michael@0 2956 AutoEnterAnalysis enter(cxArg);
michael@0 2957 HeapTypeSet *types = maybeGetProperty(JSID_EMPTY);
michael@0 2958 if (types) {
michael@0 2959 if (JSContext *cx = cxArg->maybeJSContext()) {
michael@0 2960 TypeConstraint *constraint = types->constraintList;
michael@0 2961 while (constraint) {
michael@0 2962 constraint->newObjectState(cx, this);
michael@0 2963 constraint = constraint->next;
michael@0 2964 }
michael@0 2965 } else {
michael@0 2966 JS_ASSERT(!types->constraintList);
michael@0 2967 }
michael@0 2968 }
michael@0 2969 }
michael@0 2970
michael@0 2971 void
michael@0 2972 TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags)
michael@0 2973 {
michael@0 2974 if (hasAllFlags(flags))
michael@0 2975 return;
michael@0 2976
michael@0 2977 AutoEnterAnalysis enter(cx);
michael@0 2978
michael@0 2979 if (singleton()) {
michael@0 2980 /* Make sure flags are consistent with persistent object state. */
michael@0 2981 JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
michael@0 2982 singleton()->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
michael@0 2983 }
michael@0 2984
michael@0 2985 addFlags(flags);
michael@0 2986
michael@0 2987 InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
michael@0 2988
michael@0 2989 ObjectStateChange(cx, this, false);
michael@0 2990 }
michael@0 2991
michael@0 2992 void
michael@0 2993 TypeObject::markUnknown(ExclusiveContext *cx)
michael@0 2994 {
michael@0 2995 AutoEnterAnalysis enter(cx);
michael@0 2996
michael@0 2997 JS_ASSERT(cx->compartment()->activeAnalysis);
michael@0 2998 JS_ASSERT(!unknownProperties());
michael@0 2999
michael@0 3000 if (!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED))
michael@0 3001 clearAddendum(cx);
michael@0 3002
michael@0 3003 InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
michael@0 3004
michael@0 3005 ObjectStateChange(cx, this, true);
michael@0 3006
michael@0 3007 /*
michael@0 3008 * Existing constraints may have already been added to this object, which we need
michael@0 3009 * to do the right thing for. We can't ensure that we will mark all unknown
michael@0 3010 * objects before they have been accessed, as the __proto__ of a known object
michael@0 3011 * could be dynamically set to an unknown object, and we can decide to ignore
michael@0 3012 * properties of an object during analysis (i.e. hashmaps). Adding unknown for
michael@0 3013 * any properties accessed already accounts for possible values read from them.
michael@0 3014 */
michael@0 3015
michael@0 3016 unsigned count = getPropertyCount();
michael@0 3017 for (unsigned i = 0; i < count; i++) {
michael@0 3018 Property *prop = getProperty(i);
michael@0 3019 if (prop) {
michael@0 3020 prop->types.addType(cx, Type::UnknownType());
michael@0 3021 prop->types.setNonDataProperty(cx);
michael@0 3022 }
michael@0 3023 }
michael@0 3024 }
michael@0 3025
michael@0 3026 void
michael@0 3027 TypeObject::clearAddendum(ExclusiveContext *cx)
michael@0 3028 {
michael@0 3029 JS_ASSERT(!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED));
michael@0 3030
michael@0 3031 addFlags(OBJECT_FLAG_ADDENDUM_CLEARED);
michael@0 3032
michael@0 3033 /*
michael@0 3034 * It is possible for the object to not have a new script or other
michael@0 3035 * addendum yet, but to have one added in the future. When
michael@0 3036 * analyzing properties of new scripts we mix in adding
michael@0 3037 * constraints to trigger clearNewScript with changes to the type
michael@0 3038 * sets themselves (from breakTypeBarriers). It is possible that
michael@0 3039 * we could trigger one of these constraints before
michael@0 3040 * AnalyzeNewScriptProperties has finished, in which case we want
michael@0 3041 * to make sure that call fails.
michael@0 3042 */
michael@0 3043 if (!addendum)
michael@0 3044 return;
michael@0 3045
michael@0 3046 switch (addendum->kind) {
michael@0 3047 case TypeObjectAddendum::NewScript:
michael@0 3048 clearNewScriptAddendum(cx);
michael@0 3049 break;
michael@0 3050
michael@0 3051 case TypeObjectAddendum::TypedObject:
michael@0 3052 clearTypedObjectAddendum(cx);
michael@0 3053 break;
michael@0 3054 }
michael@0 3055
michael@0 3056 /* We nullptr out addendum *before* freeing it so the write barrier works. */
michael@0 3057 TypeObjectAddendum *savedAddendum = addendum;
michael@0 3058 addendum = nullptr;
michael@0 3059 js_free(savedAddendum);
michael@0 3060
michael@0 3061 markStateChange(cx);
michael@0 3062 }
michael@0 3063
michael@0 3064 void
michael@0 3065 TypeObject::clearNewScriptAddendum(ExclusiveContext *cx)
michael@0 3066 {
michael@0 3067 AutoEnterAnalysis enter(cx);
michael@0 3068
michael@0 3069 /*
michael@0 3070 * Any definite properties we added due to analysis of the new script when
michael@0 3071 * the type object was created are now invalid: objects with the same type
michael@0 3072 * can be created by using 'new' on a different script or through some
michael@0 3073 * other mechanism (e.g. Object.create). Rather than clear out the definite
michael@0 3074 * bits on the object's properties, just mark such properties as having
michael@0 3075 * been deleted/reconfigured, which will have the same effect on JITs
michael@0 3076 * wanting to use the definite bits to optimize property accesses.
michael@0 3077 */
michael@0 3078 for (unsigned i = 0; i < getPropertyCount(); i++) {
michael@0 3079 Property *prop = getProperty(i);
michael@0 3080 if (!prop)
michael@0 3081 continue;
michael@0 3082 if (prop->types.definiteProperty())
michael@0 3083 prop->types.setNonDataProperty(cx);
michael@0 3084 }
michael@0 3085
michael@0 3086 /*
michael@0 3087 * If we cleared the new script while in the middle of initializing an
michael@0 3088 * object, it will still have the new script's shape and reflect the no
michael@0 3089 * longer correct state of the object once its initialization is completed.
michael@0 3090 * We can't really detect the possibility of this statically, but the new
michael@0 3091 * script keeps track of where each property is initialized so we can walk
michael@0 3092 * the stack and fix up any such objects.
michael@0 3093 */
michael@0 3094 if (cx->isJSContext()) {
michael@0 3095 Vector<uint32_t, 32> pcOffsets(cx);
michael@0 3096 for (ScriptFrameIter iter(cx->asJSContext()); !iter.done(); ++iter) {
michael@0 3097 pcOffsets.append(iter.script()->pcToOffset(iter.pc()));
michael@0 3098 if (!iter.isConstructing() ||
michael@0 3099 iter.callee() != newScript()->fun ||
michael@0 3100 !iter.thisv().isObject() ||
michael@0 3101 iter.thisv().toObject().hasLazyType() ||
michael@0 3102 iter.thisv().toObject().type() != this)
michael@0 3103 {
michael@0 3104 continue;
michael@0 3105 }
michael@0 3106
michael@0 3107 // Found a matching frame.
michael@0 3108 RootedObject obj(cx, &iter.thisv().toObject());
michael@0 3109
michael@0 3110 // Whether all identified 'new' properties have been initialized.
michael@0 3111 bool finished = false;
michael@0 3112
michael@0 3113 // If not finished, number of properties that have been added.
michael@0 3114 uint32_t numProperties = 0;
michael@0 3115
michael@0 3116 // Whether the current SETPROP is within an inner frame which has
michael@0 3117 // finished entirely.
michael@0 3118 bool pastProperty = false;
michael@0 3119
michael@0 3120 // Index in pcOffsets of the outermost frame.
michael@0 3121 int callDepth = pcOffsets.length() - 1;
michael@0 3122
michael@0 3123 // Index in pcOffsets of the frame currently being checked for a SETPROP.
michael@0 3124 int setpropDepth = callDepth;
michael@0 3125
michael@0 3126 for (TypeNewScript::Initializer *init = newScript()->initializerList;; init++) {
michael@0 3127 if (init->kind == TypeNewScript::Initializer::SETPROP) {
michael@0 3128 if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
michael@0 3129 // Have not yet reached this setprop.
michael@0 3130 break;
michael@0 3131 }
michael@0 3132 // This setprop has executed, reset state for the next one.
michael@0 3133 numProperties++;
michael@0 3134 pastProperty = false;
michael@0 3135 setpropDepth = callDepth;
michael@0 3136 } else if (init->kind == TypeNewScript::Initializer::SETPROP_FRAME) {
michael@0 3137 if (!pastProperty) {
michael@0 3138 if (pcOffsets[setpropDepth] < init->offset) {
michael@0 3139 // Have not yet reached this inner call.
michael@0 3140 break;
michael@0 3141 } else if (pcOffsets[setpropDepth] > init->offset) {
michael@0 3142 // Have advanced past this inner call.
michael@0 3143 pastProperty = true;
michael@0 3144 } else if (setpropDepth == 0) {
michael@0 3145 // Have reached this call but not yet in it.
michael@0 3146 break;
michael@0 3147 } else {
michael@0 3148 // Somewhere inside this inner call.
michael@0 3149 setpropDepth--;
michael@0 3150 }
michael@0 3151 }
michael@0 3152 } else {
michael@0 3153 JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE);
michael@0 3154 finished = true;
michael@0 3155 break;
michael@0 3156 }
michael@0 3157 }
michael@0 3158
michael@0 3159 if (!finished)
michael@0 3160 (void) JSObject::rollbackProperties(cx, obj, numProperties);
michael@0 3161 }
michael@0 3162 } else {
michael@0 3163 // Threads with an ExclusiveContext are not allowed to run scripts.
michael@0 3164 JS_ASSERT(!cx->perThreadData->activation());
michael@0 3165 }
michael@0 3166 }
michael@0 3167
michael@0 3168 void
michael@0 3169 TypeObject::maybeClearNewScriptAddendumOnOOM()
michael@0 3170 {
michael@0 3171 if (!isMarked())
michael@0 3172 return;
michael@0 3173
michael@0 3174 if (!addendum || addendum->kind != TypeObjectAddendum::NewScript)
michael@0 3175 return;
michael@0 3176
michael@0 3177 for (unsigned i = 0; i < getPropertyCount(); i++) {
michael@0 3178 Property *prop = getProperty(i);
michael@0 3179 if (!prop)
michael@0 3180 continue;
michael@0 3181 if (prop->types.definiteProperty())
michael@0 3182 prop->types.setNonDataPropertyIgnoringConstraints();
michael@0 3183 }
michael@0 3184
michael@0 3185 // This method is called during GC sweeping, so there is no write barrier
michael@0 3186 // that needs to be triggered.
michael@0 3187 js_free(addendum);
michael@0 3188 addendum.unsafeSet(nullptr);
michael@0 3189 }
michael@0 3190
michael@0 3191 void
michael@0 3192 TypeObject::clearTypedObjectAddendum(ExclusiveContext *cx)
michael@0 3193 {
michael@0 3194 }
michael@0 3195
michael@0 3196 void
michael@0 3197 TypeObject::print()
michael@0 3198 {
michael@0 3199 TaggedProto tagged(proto());
michael@0 3200 fprintf(stderr, "%s : %s",
michael@0 3201 TypeObjectString(this),
michael@0 3202 tagged.isObject() ? TypeString(Type::ObjectType(tagged.toObject()))
michael@0 3203 : (tagged.isLazy() ? "(lazy)" : "(null)"));
michael@0 3204
michael@0 3205 if (unknownProperties()) {
michael@0 3206 fprintf(stderr, " unknown");
michael@0 3207 } else {
michael@0 3208 if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES))
michael@0 3209 fprintf(stderr, " dense");
michael@0 3210 if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED))
michael@0 3211 fprintf(stderr, " packed");
michael@0 3212 if (!hasAnyFlags(OBJECT_FLAG_LENGTH_OVERFLOW))
michael@0 3213 fprintf(stderr, " noLengthOverflow");
michael@0 3214 if (hasAnyFlags(OBJECT_FLAG_ITERATED))
michael@0 3215 fprintf(stderr, " iterated");
michael@0 3216 if (interpretedFunction)
michael@0 3217 fprintf(stderr, " ifun");
michael@0 3218 }
michael@0 3219
michael@0 3220 unsigned count = getPropertyCount();
michael@0 3221
michael@0 3222 if (count == 0) {
michael@0 3223 fprintf(stderr, " {}\n");
michael@0 3224 return;
michael@0 3225 }
michael@0 3226
michael@0 3227 fprintf(stderr, " {");
michael@0 3228
michael@0 3229 for (unsigned i = 0; i < count; i++) {
michael@0 3230 Property *prop = getProperty(i);
michael@0 3231 if (prop) {
michael@0 3232 fprintf(stderr, "\n %s:", TypeIdString(prop->id));
michael@0 3233 prop->types.print();
michael@0 3234 }
michael@0 3235 }
michael@0 3236
michael@0 3237 fprintf(stderr, "\n}\n");
michael@0 3238 }
michael@0 3239
michael@0 3240 /////////////////////////////////////////////////////////////////////
michael@0 3241 // Type Analysis
michael@0 3242 /////////////////////////////////////////////////////////////////////
michael@0 3243
michael@0 3244 /*
michael@0 3245 * Persistent constraint clearing out newScript and definite properties from
michael@0 3246 * an object should a property on another object get a getter or setter.
michael@0 3247 */
michael@0 3248 class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
michael@0 3249 {
michael@0 3250 public:
michael@0 3251 TypeObject *object;
michael@0 3252
michael@0 3253 TypeConstraintClearDefiniteGetterSetter(TypeObject *object)
michael@0 3254 : object(object)
michael@0 3255 {}
michael@0 3256
michael@0 3257 const char *kind() { return "clearDefiniteGetterSetter"; }
michael@0 3258
michael@0 3259 void newPropertyState(JSContext *cx, TypeSet *source)
michael@0 3260 {
michael@0 3261 if (!object->hasNewScript())
michael@0 3262 return;
michael@0 3263 /*
michael@0 3264 * Clear out the newScript shape and definite property information from
michael@0 3265 * an object if the source type set could be a setter or could be
michael@0 3266 * non-writable.
michael@0 3267 */
michael@0 3268 if (!(object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED) &&
michael@0 3269 (source->nonDataProperty() || source->nonWritableProperty()))
michael@0 3270 {
michael@0 3271 object->clearAddendum(cx);
michael@0 3272 }
michael@0 3273 }
michael@0 3274
michael@0 3275 void newType(JSContext *cx, TypeSet *source, Type type) {}
michael@0 3276
michael@0 3277 bool sweep(TypeZone &zone, TypeConstraint **res) {
michael@0 3278 if (IsTypeObjectAboutToBeFinalized(&object))
michael@0 3279 return false;
michael@0 3280 *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object);
michael@0 3281 return true;
michael@0 3282 }
michael@0 3283 };
michael@0 3284
michael@0 3285 bool
michael@0 3286 types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, HandleId id)
michael@0 3287 {
michael@0 3288 /*
michael@0 3289 * Ensure that if the properties named here could have a getter, setter or
michael@0 3290 * a permanent property in any transitive prototype, the definite
michael@0 3291 * properties get cleared from the type.
michael@0 3292 */
michael@0 3293 RootedObject parent(cx, type->proto().toObjectOrNull());
michael@0 3294 while (parent) {
michael@0 3295 TypeObject *parentObject = parent->getType(cx);
michael@0 3296 if (!parentObject || parentObject->unknownProperties())
michael@0 3297 return false;
michael@0 3298 HeapTypeSet *parentTypes = parentObject->getProperty(cx, id);
michael@0 3299 if (!parentTypes || parentTypes->nonDataProperty() || parentTypes->nonWritableProperty())
michael@0 3300 return false;
michael@0 3301 if (!parentTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(type)))
michael@0 3302 return false;
michael@0 3303 parent = parent->getProto();
michael@0 3304 }
michael@0 3305 return true;
michael@0 3306 }
michael@0 3307
michael@0 3308 /*
michael@0 3309 * Constraint which clears definite properties on an object should a type set
michael@0 3310 * contain any types other than a single object.
michael@0 3311 */
michael@0 3312 class TypeConstraintClearDefiniteSingle : public TypeConstraint
michael@0 3313 {
michael@0 3314 public:
michael@0 3315 TypeObject *object;
michael@0 3316
michael@0 3317 TypeConstraintClearDefiniteSingle(TypeObject *object)
michael@0 3318 : object(object)
michael@0 3319 {}
michael@0 3320
michael@0 3321 const char *kind() { return "clearDefiniteSingle"; }
michael@0 3322
michael@0 3323 void newType(JSContext *cx, TypeSet *source, Type type) {
michael@0 3324 if (object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED)
michael@0 3325 return;
michael@0 3326
michael@0 3327 if (source->baseFlags() || source->getObjectCount() > 1)
michael@0 3328 object->clearAddendum(cx);
michael@0 3329 }
michael@0 3330
michael@0 3331 bool sweep(TypeZone &zone, TypeConstraint **res) {
michael@0 3332 if (IsTypeObjectAboutToBeFinalized(&object))
michael@0 3333 return false;
michael@0 3334 *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object);
michael@0 3335 return true;
michael@0 3336 }
michael@0 3337 };
michael@0 3338
michael@0 3339 bool
michael@0 3340 types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
michael@0 3341 JSScript *script, JSScript *calleeScript)
michael@0 3342 {
michael@0 3343 // Look for any uses of the specified calleeScript in type sets for
michael@0 3344 // |script|, and add constraints to ensure that if the type sets' contents
michael@0 3345 // change then the definite properties are cleared from the type.
michael@0 3346 // This ensures that the inlining performed when the definite properties
michael@0 3347 // analysis was done is stable.
michael@0 3348
michael@0 3349 TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey();
michael@0 3350
michael@0 3351 unsigned count = TypeScript::NumTypeSets(script);
michael@0 3352 StackTypeSet *typeArray = script->types->typeArray();
michael@0 3353
michael@0 3354 for (unsigned i = 0; i < count; i++) {
michael@0 3355 StackTypeSet *types = &typeArray[i];
michael@0 3356 if (!types->unknownObject() && types->getObjectCount() == 1) {
michael@0 3357 if (calleeKey != types->getObject(0)) {
michael@0 3358 // Also check if the object is the Function.call or
michael@0 3359 // Function.apply native. IonBuilder uses the presence of these
michael@0 3360 // functions during inlining.
michael@0 3361 JSObject *singleton = types->getSingleObject(0);
michael@0 3362 if (!singleton || !singleton->is<JSFunction>())
michael@0 3363 continue;
michael@0 3364 JSFunction *fun = &singleton->as<JSFunction>();
michael@0 3365 if (!fun->isNative())
michael@0 3366 continue;
michael@0 3367 if (fun->native() != js_fun_call && fun->native() != js_fun_apply)
michael@0 3368 continue;
michael@0 3369 }
michael@0 3370 // This is a type set that might have been used when inlining
michael@0 3371 // |calleeScript| into |script|.
michael@0 3372 if (!types->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type)))
michael@0 3373 return false;
michael@0 3374 }
michael@0 3375 }
michael@0 3376
michael@0 3377 return true;
michael@0 3378 }
michael@0 3379
michael@0 3380 /*
michael@0 3381 * Either make the newScript information for type when it is constructed
michael@0 3382 * by the specified script, or regenerate the constraints for an existing
michael@0 3383 * newScript on the type after they were cleared by a GC.
michael@0 3384 */
michael@0 3385 static void
michael@0 3386 CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
michael@0 3387 {
michael@0 3388 JS_ASSERT(cx->compartment()->activeAnalysis);
michael@0 3389
michael@0 3390 #ifdef JS_ION
michael@0 3391 if (type->unknownProperties())
michael@0 3392 return;
michael@0 3393
michael@0 3394 /* Strawman object to add properties to and watch for duplicates. */
michael@0 3395 RootedObject baseobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16));
michael@0 3396 if (!baseobj)
michael@0 3397 return;
michael@0 3398
michael@0 3399 Vector<TypeNewScript::Initializer> initializerList(cx);
michael@0 3400
michael@0 3401 if (!jit::AnalyzeNewScriptProperties(cx, fun, type, baseobj, &initializerList) ||
michael@0 3402 baseobj->slotSpan() == 0 ||
michael@0 3403 !!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED))
michael@0 3404 {
michael@0 3405 if (type->hasNewScript())
michael@0 3406 type->clearAddendum(cx);
michael@0 3407 return;
michael@0 3408 }
michael@0 3409
michael@0 3410 /*
michael@0 3411 * If the type already has a new script, we are just regenerating the type
michael@0 3412 * constraints and don't need to make another TypeNewScript. Make sure that
michael@0 3413 * the properties added to baseobj match the type's definite properties.
michael@0 3414 */
michael@0 3415 if (type->hasNewScript()) {
michael@0 3416 if (!type->matchDefiniteProperties(baseobj))
michael@0 3417 type->clearAddendum(cx);
michael@0 3418 return;
michael@0 3419 }
michael@0 3420 JS_ASSERT(!type->hasNewScript());
michael@0 3421 JS_ASSERT(!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED));
michael@0 3422
michael@0 3423 gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan());
michael@0 3424
michael@0 3425 /* We should not have overflowed the maximum number of fixed slots for an object. */
michael@0 3426 JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan());
michael@0 3427
michael@0 3428 TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0);
michael@0 3429
michael@0 3430 /*
michael@0 3431 * The base object may have been created with a different finalize kind
michael@0 3432 * than we will use for subsequent new objects. Generate an object with the
michael@0 3433 * appropriate final shape.
michael@0 3434 */
michael@0 3435 Rooted<TypeObject *> rootedType(cx, type);
michael@0 3436 RootedShape shape(cx, baseobj->lastProperty());
michael@0 3437 baseobj = NewReshapedObject(cx, rootedType, baseobj->getParent(), kind, shape, MaybeSingletonObject);
michael@0 3438 if (!baseobj ||
michael@0 3439 !type->addDefiniteProperties(cx, baseobj) ||
michael@0 3440 !initializerList.append(done))
michael@0 3441 {
michael@0 3442 return;
michael@0 3443 }
michael@0 3444
michael@0 3445 size_t numBytes = sizeof(TypeNewScript)
michael@0 3446 + (initializerList.length() * sizeof(TypeNewScript::Initializer));
michael@0 3447 TypeNewScript *newScript = (TypeNewScript *) cx->calloc_(numBytes);
michael@0 3448 if (!newScript)
michael@0 3449 return;
michael@0 3450
michael@0 3451 new (newScript) TypeNewScript();
michael@0 3452
michael@0 3453 type->setAddendum(newScript);
michael@0 3454
michael@0 3455 newScript->fun = fun;
michael@0 3456 newScript->templateObject = baseobj;
michael@0 3457
michael@0 3458 newScript->initializerList = (TypeNewScript::Initializer *)
michael@0 3459 ((char *) newScript + sizeof(TypeNewScript));
michael@0 3460 PodCopy(newScript->initializerList,
michael@0 3461 initializerList.begin(),
michael@0 3462 initializerList.length());
michael@0 3463 #endif // JS_ION
michael@0 3464 }
michael@0 3465
michael@0 3466 /////////////////////////////////////////////////////////////////////
michael@0 3467 // Interface functions
michael@0 3468 /////////////////////////////////////////////////////////////////////
michael@0 3469
michael@0 3470 void
michael@0 3471 types::TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args,
michael@0 3472 bool constructing)
michael@0 3473 {
michael@0 3474 unsigned nargs = callee->as<JSFunction>().nargs();
michael@0 3475 JSScript *script = callee->as<JSFunction>().nonLazyScript();
michael@0 3476
michael@0 3477 if (!constructing)
michael@0 3478 TypeScript::SetThis(cx, script, args.thisv());
michael@0 3479
michael@0 3480 /*
michael@0 3481 * Add constraints going up to the minimum of the actual and formal count.
michael@0 3482 * If there are more actuals than formals the later values can only be
michael@0 3483 * accessed through the arguments object, which is monitored.
michael@0 3484 */
michael@0 3485 unsigned arg = 0;
michael@0 3486 for (; arg < args.length() && arg < nargs; arg++)
michael@0 3487 TypeScript::SetArgument(cx, script, arg, args[arg]);
michael@0 3488
michael@0 3489 /* Watch for fewer actuals than formals to the call. */
michael@0 3490 for (; arg < nargs; arg++)
michael@0 3491 TypeScript::SetArgument(cx, script, arg, UndefinedValue());
michael@0 3492 }
michael@0 3493
michael@0 3494 static inline bool
michael@0 3495 IsAboutToBeFinalized(TypeObjectKey *key)
michael@0 3496 {
michael@0 3497 /* Mask out the low bit indicating whether this is a type or JS object. */
michael@0 3498 gc::Cell *tmp = reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1);
michael@0 3499 bool isAboutToBeFinalized = IsCellAboutToBeFinalized(&tmp);
michael@0 3500 JS_ASSERT(tmp == reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1));
michael@0 3501 return isAboutToBeFinalized;
michael@0 3502 }
michael@0 3503
michael@0 3504 void
michael@0 3505 types::FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap)
michael@0 3506 {
michael@0 3507 uint32_t added = 0;
michael@0 3508 for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
michael@0 3509 JSOp op = JSOp(*pc);
michael@0 3510 if (js_CodeSpec[op].format & JOF_TYPESET) {
michael@0 3511 bytecodeMap[added++] = script->pcToOffset(pc);
michael@0 3512 if (added == script->nTypeSets())
michael@0 3513 break;
michael@0 3514 }
michael@0 3515 }
michael@0 3516 JS_ASSERT(added == script->nTypeSets());
michael@0 3517 }
michael@0 3518
michael@0 3519 void
michael@0 3520 types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
michael@0 3521 {
michael@0 3522 /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
michael@0 3523 if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
michael@0 3524 return;
michael@0 3525
michael@0 3526 if (!script->hasBaselineScript())
michael@0 3527 return;
michael@0 3528
michael@0 3529 AutoEnterAnalysis enter(cx);
michael@0 3530
michael@0 3531 Type type = GetValueType(rval);
michael@0 3532 StackTypeSet *types = TypeScript::BytecodeTypes(script, pc);
michael@0 3533 if (types->hasType(type))
michael@0 3534 return;
michael@0 3535
michael@0 3536 InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s",
michael@0 3537 script->id(), script->pcToOffset(pc), TypeString(type));
michael@0 3538 types->addType(cx, type);
michael@0 3539 }
michael@0 3540
michael@0 3541 bool
michael@0 3542 types::UseNewTypeForClone(JSFunction *fun)
michael@0 3543 {
michael@0 3544 if (!fun->isInterpreted())
michael@0 3545 return false;
michael@0 3546
michael@0 3547 if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite())
michael@0 3548 return true;
michael@0 3549
michael@0 3550 if (fun->isArrow())
michael@0 3551 return false;
michael@0 3552
michael@0 3553 if (fun->hasSingletonType())
michael@0 3554 return false;
michael@0 3555
michael@0 3556 /*
michael@0 3557 * When a function is being used as a wrapper for another function, it
michael@0 3558 * improves precision greatly to distinguish between different instances of
michael@0 3559 * the wrapper; otherwise we will conflate much of the information about
michael@0 3560 * the wrapped functions.
michael@0 3561 *
michael@0 3562 * An important example is the Class.create function at the core of the
michael@0 3563 * Prototype.js library, which looks like:
michael@0 3564 *
michael@0 3565 * var Class = {
michael@0 3566 * create: function() {
michael@0 3567 * return function() {
michael@0 3568 * this.initialize.apply(this, arguments);
michael@0 3569 * }
michael@0 3570 * }
michael@0 3571 * };
michael@0 3572 *
michael@0 3573 * Each instance of the innermost function will have a different wrapped
michael@0 3574 * initialize method. We capture this, along with similar cases, by looking
michael@0 3575 * for short scripts which use both .apply and arguments. For such scripts,
michael@0 3576 * whenever creating a new instance of the function we both give that
michael@0 3577 * instance a singleton type and clone the underlying script.
michael@0 3578 */
michael@0 3579
michael@0 3580 uint32_t begin, end;
michael@0 3581 if (fun->hasScript()) {
michael@0 3582 if (!fun->nonLazyScript()->usesArgumentsAndApply())
michael@0 3583 return false;
michael@0 3584 begin = fun->nonLazyScript()->sourceStart();
michael@0 3585 end = fun->nonLazyScript()->sourceEnd();
michael@0 3586 } else {
michael@0 3587 if (!fun->lazyScript()->usesArgumentsAndApply())
michael@0 3588 return false;
michael@0 3589 begin = fun->lazyScript()->begin();
michael@0 3590 end = fun->lazyScript()->end();
michael@0 3591 }
michael@0 3592
michael@0 3593 return end - begin <= 100;
michael@0 3594 }
michael@0 3595 /////////////////////////////////////////////////////////////////////
michael@0 3596 // TypeScript
michael@0 3597 /////////////////////////////////////////////////////////////////////
michael@0 3598
michael@0 3599 bool
michael@0 3600 JSScript::makeTypes(JSContext *cx)
michael@0 3601 {
michael@0 3602 JS_ASSERT(!types);
michael@0 3603
michael@0 3604 AutoEnterAnalysis enter(cx);
michael@0 3605
michael@0 3606 unsigned count = TypeScript::NumTypeSets(this);
michael@0 3607
michael@0 3608 TypeScript *typeScript = (TypeScript *)
michael@0 3609 cx->calloc_(TypeScript::SizeIncludingTypeArray(count));
michael@0 3610 if (!typeScript)
michael@0 3611 return false;
michael@0 3612
michael@0 3613 new(typeScript) TypeScript();
michael@0 3614
michael@0 3615 TypeSet *typeArray = typeScript->typeArray();
michael@0 3616
michael@0 3617 for (unsigned i = 0; i < count; i++)
michael@0 3618 new (&typeArray[i]) StackTypeSet();
michael@0 3619
michael@0 3620 types = typeScript;
michael@0 3621
michael@0 3622 #ifdef DEBUG
michael@0 3623 for (unsigned i = 0; i < nTypeSets(); i++) {
michael@0 3624 InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
michael@0 3625 InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
michael@0 3626 i, id());
michael@0 3627 }
michael@0 3628 TypeSet *thisTypes = TypeScript::ThisTypes(this);
michael@0 3629 InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
michael@0 3630 InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
michael@0 3631 id());
michael@0 3632 unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0;
michael@0 3633 for (unsigned i = 0; i < nargs; i++) {
michael@0 3634 TypeSet *types = TypeScript::ArgTypes(this, i);
michael@0 3635 InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u",
michael@0 3636 InferSpewColor(types), types, InferSpewColorReset(),
michael@0 3637 i, id());
michael@0 3638 }
michael@0 3639 #endif
michael@0 3640
michael@0 3641 return true;
michael@0 3642 }
michael@0 3643
michael@0 3644 /* static */ bool
michael@0 3645 JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun,
michael@0 3646 bool singleton /* = false */)
michael@0 3647 {
michael@0 3648 if (singleton) {
michael@0 3649 if (!setSingletonType(cx, fun))
michael@0 3650 return false;
michael@0 3651 } else {
michael@0 3652 RootedObject funProto(cx, fun->getProto());
michael@0 3653 TypeObject *type =
michael@0 3654 cx->compartment()->types.newTypeObject(cx, &JSFunction::class_, funProto);
michael@0 3655 if (!type)
michael@0 3656 return false;
michael@0 3657
michael@0 3658 fun->setType(type);
michael@0 3659 type->interpretedFunction = fun;
michael@0 3660 }
michael@0 3661
michael@0 3662 return true;
michael@0 3663 }
michael@0 3664
michael@0 3665 /////////////////////////////////////////////////////////////////////
michael@0 3666 // JSObject
michael@0 3667 /////////////////////////////////////////////////////////////////////
michael@0 3668
michael@0 3669 bool
michael@0 3670 JSObject::shouldSplicePrototype(JSContext *cx)
michael@0 3671 {
michael@0 3672 /*
michael@0 3673 * During bootstrapping, if inference is enabled we need to make sure not
michael@0 3674 * to splice a new prototype in for Function.prototype or the global
michael@0 3675 * object if their __proto__ had previously been set to null, as this
michael@0 3676 * will change the prototype for all other objects with the same type.
michael@0 3677 */
michael@0 3678 if (getProto() != nullptr)
michael@0 3679 return false;
michael@0 3680 return hasSingletonType();
michael@0 3681 }
michael@0 3682
michael@0 3683 bool
michael@0 3684 JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle<TaggedProto> proto)
michael@0 3685 {
michael@0 3686 JS_ASSERT(cx->compartment() == compartment());
michael@0 3687
michael@0 3688 RootedObject self(cx, this);
michael@0 3689
michael@0 3690 /*
michael@0 3691 * For singleton types representing only a single JSObject, the proto
michael@0 3692 * can be rearranged as needed without destroying type information for
michael@0 3693 * the old or new types.
michael@0 3694 */
michael@0 3695 JS_ASSERT(self->hasSingletonType());
michael@0 3696
michael@0 3697 /* Inner objects may not appear on prototype chains. */
michael@0 3698 JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
michael@0 3699
michael@0 3700 /*
michael@0 3701 * Force type instantiation when splicing lazy types. This may fail,
michael@0 3702 * in which case inference will be disabled for the compartment.
michael@0 3703 */
michael@0 3704 Rooted<TypeObject*> type(cx, self->getType(cx));
michael@0 3705 if (!type)
michael@0 3706 return false;
michael@0 3707 Rooted<TypeObject*> protoType(cx, nullptr);
michael@0 3708 if (proto.isObject()) {
michael@0 3709 protoType = proto.toObject()->getType(cx);
michael@0 3710 if (!protoType)
michael@0 3711 return false;
michael@0 3712 }
michael@0 3713
michael@0 3714 type->setClasp(clasp);
michael@0 3715 type->setProto(cx, proto);
michael@0 3716 return true;
michael@0 3717 }
michael@0 3718
michael@0 3719 /* static */ TypeObject *
michael@0 3720 JSObject::makeLazyType(JSContext *cx, HandleObject obj)
michael@0 3721 {
michael@0 3722 JS_ASSERT(obj->hasLazyType());
michael@0 3723 JS_ASSERT(cx->compartment() == obj->compartment());
michael@0 3724
michael@0 3725 /* De-lazification of functions can GC, so we need to do it up here. */
michael@0 3726 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
michael@0 3727 RootedFunction fun(cx, &obj->as<JSFunction>());
michael@0 3728 if (!fun->getOrCreateScript(cx))
michael@0 3729 return nullptr;
michael@0 3730 }
michael@0 3731
michael@0 3732 // Find flags which need to be specified immediately on the object.
michael@0 3733 // Don't track whether singletons are packed.
michael@0 3734 TypeObjectFlags initialFlags = OBJECT_FLAG_NON_PACKED;
michael@0 3735
michael@0 3736 if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
michael@0 3737 initialFlags |= OBJECT_FLAG_ITERATED;
michael@0 3738
michael@0 3739 if (obj->isIndexed())
michael@0 3740 initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
michael@0 3741
michael@0 3742 if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
michael@0 3743 initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
michael@0 3744
michael@0 3745 Rooted<TaggedProto> proto(cx, obj->getTaggedProto());
michael@0 3746 TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto, initialFlags);
michael@0 3747 if (!type)
michael@0 3748 return nullptr;
michael@0 3749
michael@0 3750 AutoEnterAnalysis enter(cx);
michael@0 3751
michael@0 3752 /* Fill in the type according to the state of this object. */
michael@0 3753
michael@0 3754 type->initSingleton(obj);
michael@0 3755
michael@0 3756 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted())
michael@0 3757 type->interpretedFunction = &obj->as<JSFunction>();
michael@0 3758
michael@0 3759 obj->type_ = type;
michael@0 3760
michael@0 3761 return type;
michael@0 3762 }
michael@0 3763
michael@0 3764 /* static */ inline HashNumber
michael@0 3765 TypeObjectWithNewScriptEntry::hash(const Lookup &lookup)
michael@0 3766 {
michael@0 3767 return PointerHasher<JSObject *, 3>::hash(lookup.hashProto.raw()) ^
michael@0 3768 PointerHasher<const Class *, 3>::hash(lookup.clasp) ^
michael@0 3769 PointerHasher<JSFunction *, 3>::hash(lookup.newFunction);
michael@0 3770 }
michael@0 3771
michael@0 3772 /* static */ inline bool
michael@0 3773 TypeObjectWithNewScriptEntry::match(const TypeObjectWithNewScriptEntry &key, const Lookup &lookup)
michael@0 3774 {
michael@0 3775 return key.object->proto() == lookup.matchProto &&
michael@0 3776 key.object->clasp() == lookup.clasp &&
michael@0 3777 key.newFunction == lookup.newFunction;
michael@0 3778 }
michael@0 3779
michael@0 3780 #ifdef DEBUG
michael@0 3781 bool
michael@0 3782 JSObject::hasNewType(const Class *clasp, TypeObject *type)
michael@0 3783 {
michael@0 3784 TypeObjectWithNewScriptSet &table = compartment()->newTypeObjects;
michael@0 3785
michael@0 3786 if (!table.initialized())
michael@0 3787 return false;
michael@0 3788
michael@0 3789 TypeObjectWithNewScriptSet::Ptr p = table.lookup(TypeObjectWithNewScriptSet::Lookup(clasp, this, nullptr));
michael@0 3790 return p && p->object == type;
michael@0 3791 }
michael@0 3792 #endif /* DEBUG */
michael@0 3793
michael@0 3794 /* static */ bool
michael@0 3795 JSObject::setNewTypeUnknown(JSContext *cx, const Class *clasp, HandleObject obj)
michael@0 3796 {
michael@0 3797 if (!obj->setFlag(cx, js::BaseShape::NEW_TYPE_UNKNOWN))
michael@0 3798 return false;
michael@0 3799
michael@0 3800 /*
michael@0 3801 * If the object already has a new type, mark that type as unknown. It will
michael@0 3802 * not have the SETS_MARKED_UNKNOWN bit set, so may require a type set
michael@0 3803 * crawl if prototypes of the object change dynamically in the future.
michael@0 3804 */
michael@0 3805 TypeObjectWithNewScriptSet &table = cx->compartment()->newTypeObjects;
michael@0 3806 if (table.initialized()) {
michael@0 3807 if (TypeObjectWithNewScriptSet::Ptr p = table.lookup(TypeObjectWithNewScriptSet::Lookup(clasp, obj.get(), nullptr)))
michael@0 3808 MarkTypeObjectUnknownProperties(cx, p->object);
michael@0 3809 }
michael@0 3810
michael@0 3811 return true;
michael@0 3812 }
michael@0 3813
michael@0 3814 #ifdef JSGC_GENERATIONAL
michael@0 3815 /*
michael@0 3816 * This class is used to add a post barrier on the newTypeObjects set, as the
michael@0 3817 * key is calculated from a prototype object which may be moved by generational
michael@0 3818 * GC.
michael@0 3819 */
michael@0 3820 class NewTypeObjectsSetRef : public BufferableRef
michael@0 3821 {
michael@0 3822 TypeObjectWithNewScriptSet *set;
michael@0 3823 const Class *clasp;
michael@0 3824 JSObject *proto;
michael@0 3825 JSFunction *newFunction;
michael@0 3826
michael@0 3827 public:
michael@0 3828 NewTypeObjectsSetRef(TypeObjectWithNewScriptSet *s, const Class *clasp, JSObject *proto, JSFunction *newFunction)
michael@0 3829 : set(s), clasp(clasp), proto(proto), newFunction(newFunction)
michael@0 3830 {}
michael@0 3831
michael@0 3832 void mark(JSTracer *trc) {
michael@0 3833 JSObject *prior = proto;
michael@0 3834 trc->setTracingLocation(&*prior);
michael@0 3835 Mark(trc, &proto, "newTypeObjects set prototype");
michael@0 3836 if (prior == proto)
michael@0 3837 return;
michael@0 3838
michael@0 3839 TypeObjectWithNewScriptSet::Ptr p = set->lookup(TypeObjectWithNewScriptSet::Lookup(clasp, prior, proto, newFunction));
michael@0 3840 JS_ASSERT(p); // newTypeObjects set must still contain original entry.
michael@0 3841
michael@0 3842 set->rekeyAs(TypeObjectWithNewScriptSet::Lookup(clasp, prior, proto, newFunction),
michael@0 3843 TypeObjectWithNewScriptSet::Lookup(clasp, proto, newFunction), *p);
michael@0 3844 }
michael@0 3845 };
michael@0 3846 #endif
michael@0 3847
michael@0 3848 TypeObject *
michael@0 3849 ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction *fun)
michael@0 3850 {
michael@0 3851 JS_ASSERT_IF(fun, proto.isObject());
michael@0 3852 JS_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject()));
michael@0 3853
michael@0 3854 TypeObjectWithNewScriptSet &newTypeObjects = compartment()->newTypeObjects;
michael@0 3855
michael@0 3856 if (!newTypeObjects.initialized() && !newTypeObjects.init())
michael@0 3857 return nullptr;
michael@0 3858
michael@0 3859 // Canonicalize new functions to use the original one associated with its script.
michael@0 3860 if (fun) {
michael@0 3861 if (fun->hasScript())
michael@0 3862 fun = fun->nonLazyScript()->functionNonDelazifying();
michael@0 3863 else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
michael@0 3864 fun = fun->lazyScript()->functionNonDelazifying();
michael@0 3865 else
michael@0 3866 fun = nullptr;
michael@0 3867 }
michael@0 3868
michael@0 3869 TypeObjectWithNewScriptSet::AddPtr p =
michael@0 3870 newTypeObjects.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, fun));
michael@0 3871 if (p) {
michael@0 3872 TypeObject *type = p->object;
michael@0 3873 JS_ASSERT(type->clasp() == clasp);
michael@0 3874 JS_ASSERT(type->proto() == proto);
michael@0 3875 JS_ASSERT_IF(type->hasNewScript(), type->newScript()->fun == fun);
michael@0 3876 return type;
michael@0 3877 }
michael@0 3878
michael@0 3879 AutoEnterAnalysis enter(this);
michael@0 3880
michael@0 3881 if (proto.isObject() && !proto.toObject()->setDelegate(this))
michael@0 3882 return nullptr;
michael@0 3883
michael@0 3884 TypeObjectFlags initialFlags = 0;
michael@0 3885 if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)) {
michael@0 3886 // The new type is not present in any type sets, so mark the object as
michael@0 3887 // unknown in all type sets it appears in. This allows the prototype of
michael@0 3888 // such objects to mutate freely without triggering an expensive walk of
michael@0 3889 // the compartment's type sets. (While scripts normally don't mutate
michael@0 3890 // __proto__, the browser will for proxies and such, and we need to
michael@0 3891 // accommodate this behavior).
michael@0 3892 initialFlags = OBJECT_FLAG_UNKNOWN_MASK | OBJECT_FLAG_SETS_MARKED_UNKNOWN;
michael@0 3893 }
michael@0 3894
michael@0 3895 Rooted<TaggedProto> protoRoot(this, proto);
michael@0 3896 TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags);
michael@0 3897 if (!type)
michael@0 3898 return nullptr;
michael@0 3899
michael@0 3900 if (!newTypeObjects.add(p, TypeObjectWithNewScriptEntry(type, fun)))
michael@0 3901 return nullptr;
michael@0 3902
michael@0 3903 #ifdef JSGC_GENERATIONAL
michael@0 3904 if (proto.isObject() && hasNursery() && nursery().isInside(proto.toObject())) {
michael@0 3905 asJSContext()->runtime()->gcStoreBuffer.putGeneric(
michael@0 3906 NewTypeObjectsSetRef(&newTypeObjects, clasp, proto.toObject(), fun));
michael@0 3907 }
michael@0 3908 #endif
michael@0 3909
michael@0 3910 if (proto.isObject()) {
michael@0 3911 RootedObject obj(this, proto.toObject());
michael@0 3912
michael@0 3913 if (fun)
michael@0 3914 CheckNewScriptProperties(asJSContext(), type, fun);
michael@0 3915
michael@0 3916 /*
michael@0 3917 * Some builtin objects have slotful native properties baked in at
michael@0 3918 * creation via the Shape::{insert,get}initialShape mechanism. Since
michael@0 3919 * these properties are never explicitly defined on new objects, update
michael@0 3920 * the type information for them here.
michael@0 3921 */
michael@0 3922
michael@0 3923 if (obj->is<RegExpObject>()) {
michael@0 3924 AddTypePropertyId(this, type, NameToId(names().source), Type::StringType());
michael@0 3925 AddTypePropertyId(this, type, NameToId(names().global), Type::BooleanType());
michael@0 3926 AddTypePropertyId(this, type, NameToId(names().ignoreCase), Type::BooleanType());
michael@0 3927 AddTypePropertyId(this, type, NameToId(names().multiline), Type::BooleanType());
michael@0 3928 AddTypePropertyId(this, type, NameToId(names().sticky), Type::BooleanType());
michael@0 3929 AddTypePropertyId(this, type, NameToId(names().lastIndex), Type::Int32Type());
michael@0 3930 }
michael@0 3931
michael@0 3932 if (obj->is<StringObject>())
michael@0 3933 AddTypePropertyId(this, type, NameToId(names().length), Type::Int32Type());
michael@0 3934
michael@0 3935 if (obj->is<ErrorObject>()) {
michael@0 3936 AddTypePropertyId(this, type, NameToId(names().fileName), Type::StringType());
michael@0 3937 AddTypePropertyId(this, type, NameToId(names().lineNumber), Type::Int32Type());
michael@0 3938 AddTypePropertyId(this, type, NameToId(names().columnNumber), Type::Int32Type());
michael@0 3939 AddTypePropertyId(this, type, NameToId(names().stack), Type::StringType());
michael@0 3940 }
michael@0 3941 }
michael@0 3942
michael@0 3943 return type;
michael@0 3944 }
michael@0 3945
michael@0 3946 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
michael@0 3947 void
michael@0 3948 JSCompartment::checkNewTypeObjectTableAfterMovingGC()
michael@0 3949 {
michael@0 3950 /*
michael@0 3951 * Assert that the postbarriers have worked and that nothing is left in
michael@0 3952 * newTypeObjects that points into the nursery, and that the hash table
michael@0 3953 * entries are discoverable.
michael@0 3954 */
michael@0 3955 JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtimeFromMainThread());
michael@0 3956 for (TypeObjectWithNewScriptSet::Enum e(newTypeObjects); !e.empty(); e.popFront()) {
michael@0 3957 TypeObjectWithNewScriptEntry entry = e.front();
michael@0 3958 JS_ASSERT(!IsInsideNursery(rt, entry.newFunction));
michael@0 3959 TaggedProto proto = entry.object->proto();
michael@0 3960 JS_ASSERT_IF(proto.isObject(), !IsInsideNursery(rt, proto.toObject()));
michael@0 3961 TypeObjectWithNewScriptEntry::Lookup
michael@0 3962 lookup(entry.object->clasp(), proto, entry.newFunction);
michael@0 3963 TypeObjectWithNewScriptSet::Ptr ptr = newTypeObjects.lookup(lookup);
michael@0 3964 JS_ASSERT(ptr.found() && &*ptr == &e.front());
michael@0 3965 }
michael@0 3966 }
michael@0 3967 #endif
michael@0 3968
michael@0 3969 TypeObject *
michael@0 3970 ExclusiveContext::getSingletonType(const Class *clasp, TaggedProto proto)
michael@0 3971 {
michael@0 3972 JS_ASSERT_IF(proto.isObject(), compartment() == proto.toObject()->compartment());
michael@0 3973
michael@0 3974 AutoEnterAnalysis enter(this);
michael@0 3975
michael@0 3976 TypeObjectWithNewScriptSet &table = compartment()->lazyTypeObjects;
michael@0 3977
michael@0 3978 if (!table.initialized() && !table.init())
michael@0 3979 return nullptr;
michael@0 3980
michael@0 3981 TypeObjectWithNewScriptSet::AddPtr p = table.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, nullptr));
michael@0 3982 if (p) {
michael@0 3983 TypeObject *type = p->object;
michael@0 3984 JS_ASSERT(type->lazy());
michael@0 3985
michael@0 3986 return type;
michael@0 3987 }
michael@0 3988
michael@0 3989 Rooted<TaggedProto> protoRoot(this, proto);
michael@0 3990 TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot);
michael@0 3991 if (!type)
michael@0 3992 return nullptr;
michael@0 3993
michael@0 3994 if (!table.add(p, TypeObjectWithNewScriptEntry(type, nullptr)))
michael@0 3995 return nullptr;
michael@0 3996
michael@0 3997 type->initSingleton((JSObject *) TypeObject::LAZY_SINGLETON);
michael@0 3998 MOZ_ASSERT(type->singleton(), "created type must be a proper singleton");
michael@0 3999
michael@0 4000 return type;
michael@0 4001 }
michael@0 4002
michael@0 4003 /////////////////////////////////////////////////////////////////////
michael@0 4004 // Tracing
michael@0 4005 /////////////////////////////////////////////////////////////////////
michael@0 4006
michael@0 4007 void
michael@0 4008 ConstraintTypeSet::sweep(Zone *zone, bool *oom)
michael@0 4009 {
michael@0 4010 /*
michael@0 4011 * Purge references to type objects that are no longer live. Type sets hold
michael@0 4012 * only weak references. For type sets containing more than one object,
michael@0 4013 * live entries in the object hash need to be copied to the zone's
michael@0 4014 * new arena.
michael@0 4015 */
michael@0 4016 unsigned objectCount = baseObjectCount();
michael@0 4017 if (objectCount >= 2) {
michael@0 4018 unsigned oldCapacity = HashSetCapacity(objectCount);
michael@0 4019 TypeObjectKey **oldArray = objectSet;
michael@0 4020
michael@0 4021 clearObjects();
michael@0 4022 objectCount = 0;
michael@0 4023 for (unsigned i = 0; i < oldCapacity; i++) {
michael@0 4024 TypeObjectKey *object = oldArray[i];
michael@0 4025 if (object && !IsAboutToBeFinalized(object)) {
michael@0 4026 TypeObjectKey **pentry =
michael@0 4027 HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
michael@0 4028 (zone->types.typeLifoAlloc, objectSet, objectCount, object);
michael@0 4029 if (pentry) {
michael@0 4030 *pentry = object;
michael@0 4031 } else {
michael@0 4032 *oom = true;
michael@0 4033 flags |= TYPE_FLAG_ANYOBJECT;
michael@0 4034 clearObjects();
michael@0 4035 objectCount = 0;
michael@0 4036 break;
michael@0 4037 }
michael@0 4038 }
michael@0 4039 }
michael@0 4040 setBaseObjectCount(objectCount);
michael@0 4041 } else if (objectCount == 1) {
michael@0 4042 TypeObjectKey *object = (TypeObjectKey *) objectSet;
michael@0 4043 if (IsAboutToBeFinalized(object)) {
michael@0 4044 objectSet = nullptr;
michael@0 4045 setBaseObjectCount(0);
michael@0 4046 }
michael@0 4047 }
michael@0 4048
michael@0 4049 /*
michael@0 4050 * Type constraints only hold weak references. Copy constraints referring
michael@0 4051 * to data that is still live into the zone's new arena.
michael@0 4052 */
michael@0 4053 TypeConstraint *constraint = constraintList;
michael@0 4054 constraintList = nullptr;
michael@0 4055 while (constraint) {
michael@0 4056 TypeConstraint *copy;
michael@0 4057 if (constraint->sweep(zone->types, &copy)) {
michael@0 4058 if (copy) {
michael@0 4059 copy->next = constraintList;
michael@0 4060 constraintList = copy;
michael@0 4061 } else {
michael@0 4062 *oom = true;
michael@0 4063 }
michael@0 4064 }
michael@0 4065 constraint = constraint->next;
michael@0 4066 }
michael@0 4067 }
michael@0 4068
michael@0 4069 inline void
michael@0 4070 TypeObject::clearProperties()
michael@0 4071 {
michael@0 4072 setBasePropertyCount(0);
michael@0 4073 propertySet = nullptr;
michael@0 4074 }
michael@0 4075
michael@0 4076 /*
michael@0 4077 * Before sweeping the arenas themselves, scan all type objects in a
michael@0 4078 * compartment to fixup weak references: property type sets referencing dead
michael@0 4079 * JS and type objects, and singleton JS objects whose type is not referenced
michael@0 4080 * elsewhere. This also releases memory associated with dead type objects,
michael@0 4081 * so that type objects do not need later finalization.
michael@0 4082 */
michael@0 4083 inline void
michael@0 4084 TypeObject::sweep(FreeOp *fop, bool *oom)
michael@0 4085 {
michael@0 4086 if (!isMarked()) {
michael@0 4087 if (addendum)
michael@0 4088 fop->free_(addendum);
michael@0 4089 return;
michael@0 4090 }
michael@0 4091
michael@0 4092 LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc;
michael@0 4093
michael@0 4094 /*
michael@0 4095 * Properties were allocated from the old arena, and need to be copied over
michael@0 4096 * to the new one.
michael@0 4097 */
michael@0 4098 unsigned propertyCount = basePropertyCount();
michael@0 4099 if (propertyCount >= 2) {
michael@0 4100 unsigned oldCapacity = HashSetCapacity(propertyCount);
michael@0 4101 Property **oldArray = propertySet;
michael@0 4102
michael@0 4103 clearProperties();
michael@0 4104 propertyCount = 0;
michael@0 4105 for (unsigned i = 0; i < oldCapacity; i++) {
michael@0 4106 Property *prop = oldArray[i];
michael@0 4107 if (prop) {
michael@0 4108 if (singleton() && !prop->types.constraintList && !zone()->isPreservingCode()) {
michael@0 4109 /*
michael@0 4110 * Don't copy over properties of singleton objects when their
michael@0 4111 * presence will not be required by jitcode or type constraints
michael@0 4112 * (i.e. for the definite properties analysis). The contents of
michael@0 4113 * these type sets will be regenerated as necessary.
michael@0 4114 */
michael@0 4115 continue;
michael@0 4116 }
michael@0 4117
michael@0 4118 Property *newProp = typeLifoAlloc.new_<Property>(*prop);
michael@0 4119 if (newProp) {
michael@0 4120 Property **pentry =
michael@0 4121 HashSetInsert<jsid,Property,Property>
michael@0 4122 (typeLifoAlloc, propertySet, propertyCount, prop->id);
michael@0 4123 if (pentry) {
michael@0 4124 *pentry = newProp;
michael@0 4125 newProp->types.sweep(zone(), oom);
michael@0 4126 continue;
michael@0 4127 }
michael@0 4128 }
michael@0 4129
michael@0 4130 *oom = true;
michael@0 4131 addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
michael@0 4132 clearProperties();
michael@0 4133 return;
michael@0 4134 }
michael@0 4135 }
michael@0 4136 setBasePropertyCount(propertyCount);
michael@0 4137 } else if (propertyCount == 1) {
michael@0 4138 Property *prop = (Property *) propertySet;
michael@0 4139 if (singleton() && !prop->types.constraintList && !zone()->isPreservingCode()) {
michael@0 4140 // Skip, as above.
michael@0 4141 clearProperties();
michael@0 4142 } else {
michael@0 4143 Property *newProp = typeLifoAlloc.new_<Property>(*prop);
michael@0 4144 if (newProp) {
michael@0 4145 propertySet = (Property **) newProp;
michael@0 4146 newProp->types.sweep(zone(), oom);
michael@0 4147 } else {
michael@0 4148 *oom = true;
michael@0 4149 addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
michael@0 4150 clearProperties();
michael@0 4151 return;
michael@0 4152 }
michael@0 4153 }
michael@0 4154 }
michael@0 4155 }
michael@0 4156
michael@0 4157 void
michael@0 4158 TypeCompartment::clearTables()
michael@0 4159 {
michael@0 4160 if (allocationSiteTable && allocationSiteTable->initialized())
michael@0 4161 allocationSiteTable->clear();
michael@0 4162 if (arrayTypeTable && arrayTypeTable->initialized())
michael@0 4163 arrayTypeTable->clear();
michael@0 4164 if (objectTypeTable && objectTypeTable->initialized())
michael@0 4165 objectTypeTable->clear();
michael@0 4166 }
michael@0 4167
michael@0 4168 void
michael@0 4169 TypeCompartment::sweep(FreeOp *fop)
michael@0 4170 {
michael@0 4171 /*
michael@0 4172 * Iterate through the array/object type tables and remove all entries
michael@0 4173 * referencing collected data. These tables only hold weak references.
michael@0 4174 */
michael@0 4175
michael@0 4176 if (arrayTypeTable) {
michael@0 4177 for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
michael@0 4178 const ArrayTableKey &key = e.front().key();
michael@0 4179 JS_ASSERT(key.type.isUnknown() || !key.type.isSingleObject());
michael@0 4180
michael@0 4181 bool remove = false;
michael@0 4182 TypeObject *typeObject = nullptr;
michael@0 4183 if (!key.type.isUnknown() && key.type.isTypeObject()) {
michael@0 4184 typeObject = key.type.typeObject();
michael@0 4185 if (IsTypeObjectAboutToBeFinalized(&typeObject))
michael@0 4186 remove = true;
michael@0 4187 }
michael@0 4188 if (IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet()))
michael@0 4189 remove = true;
michael@0 4190
michael@0 4191 if (remove) {
michael@0 4192 e.removeFront();
michael@0 4193 } else if (typeObject && typeObject != key.type.typeObject()) {
michael@0 4194 ArrayTableKey newKey;
michael@0 4195 newKey.type = Type::ObjectType(typeObject);
michael@0 4196 newKey.proto = key.proto;
michael@0 4197 e.rekeyFront(newKey);
michael@0 4198 }
michael@0 4199 }
michael@0 4200 }
michael@0 4201
michael@0 4202 if (objectTypeTable) {
michael@0 4203 for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
michael@0 4204 const ObjectTableKey &key = e.front().key();
michael@0 4205 ObjectTableEntry &entry = e.front().value();
michael@0 4206
michael@0 4207 bool remove = false;
michael@0 4208 if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet()))
michael@0 4209 remove = true;
michael@0 4210 if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet()))
michael@0 4211 remove = true;
michael@0 4212 for (unsigned i = 0; !remove && i < key.nproperties; i++) {
michael@0 4213 if (JSID_IS_STRING(key.properties[i])) {
michael@0 4214 JSString *str = JSID_TO_STRING(key.properties[i]);
michael@0 4215 if (IsStringAboutToBeFinalized(&str))
michael@0 4216 remove = true;
michael@0 4217 JS_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]);
michael@0 4218 }
michael@0 4219 JS_ASSERT(!entry.types[i].isSingleObject());
michael@0 4220 TypeObject *typeObject = nullptr;
michael@0 4221 if (entry.types[i].isTypeObject()) {
michael@0 4222 typeObject = entry.types[i].typeObject();
michael@0 4223 if (IsTypeObjectAboutToBeFinalized(&typeObject))
michael@0 4224 remove = true;
michael@0 4225 else if (typeObject != entry.types[i].typeObject())
michael@0 4226 entry.types[i] = Type::ObjectType(typeObject);
michael@0 4227 }
michael@0 4228 }
michael@0 4229
michael@0 4230 if (remove) {
michael@0 4231 js_free(key.properties);
michael@0 4232 js_free(entry.types);
michael@0 4233 e.removeFront();
michael@0 4234 }
michael@0 4235 }
michael@0 4236 }
michael@0 4237
michael@0 4238 if (allocationSiteTable) {
michael@0 4239 for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
michael@0 4240 AllocationSiteKey key = e.front().key();
michael@0 4241 bool keyDying = IsScriptAboutToBeFinalized(&key.script);
michael@0 4242 bool valDying = IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet());
michael@0 4243 if (keyDying || valDying)
michael@0 4244 e.removeFront();
michael@0 4245 else if (key.script != e.front().key().script)
michael@0 4246 e.rekeyFront(key);
michael@0 4247 }
michael@0 4248 }
michael@0 4249 }
michael@0 4250
michael@0 4251 void
michael@0 4252 JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
michael@0 4253 {
michael@0 4254 gcstats::AutoPhase ap(runtimeFromMainThread()->gcStats,
michael@0 4255 gcstats::PHASE_SWEEP_TABLES_TYPE_OBJECT);
michael@0 4256
michael@0 4257 JS_ASSERT(zone()->isGCSweeping());
michael@0 4258 if (table.initialized()) {
michael@0 4259 for (TypeObjectWithNewScriptSet::Enum e(table); !e.empty(); e.popFront()) {
michael@0 4260 TypeObjectWithNewScriptEntry entry = e.front();
michael@0 4261 if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet())) {
michael@0 4262 e.removeFront();
michael@0 4263 } else if (entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)) {
michael@0 4264 e.removeFront();
michael@0 4265 } else if (entry.object != e.front().object) {
michael@0 4266 TypeObjectWithNewScriptSet::Lookup lookup(entry.object->clasp(),
michael@0 4267 entry.object->proto(),
michael@0 4268 entry.newFunction);
michael@0 4269 e.rekeyFront(lookup, entry);
michael@0 4270 }
michael@0 4271 }
michael@0 4272 }
michael@0 4273 }
michael@0 4274
michael@0 4275 TypeCompartment::~TypeCompartment()
michael@0 4276 {
michael@0 4277 js_delete(arrayTypeTable);
michael@0 4278 js_delete(objectTypeTable);
michael@0 4279 js_delete(allocationSiteTable);
michael@0 4280 }
michael@0 4281
michael@0 4282 /* static */ void
michael@0 4283 TypeScript::Sweep(FreeOp *fop, JSScript *script, bool *oom)
michael@0 4284 {
michael@0 4285 JSCompartment *compartment = script->compartment();
michael@0 4286 JS_ASSERT(compartment->zone()->isGCSweeping());
michael@0 4287
michael@0 4288 unsigned num = NumTypeSets(script);
michael@0 4289 StackTypeSet *typeArray = script->types->typeArray();
michael@0 4290
michael@0 4291 /* Remove constraints and references to dead objects from the persistent type sets. */
michael@0 4292 for (unsigned i = 0; i < num; i++)
michael@0 4293 typeArray[i].sweep(compartment->zone(), oom);
michael@0 4294 }
michael@0 4295
michael@0 4296 void
michael@0 4297 TypeScript::destroy()
michael@0 4298 {
michael@0 4299 js_free(this);
michael@0 4300 }
michael@0 4301
michael@0 4302 void
michael@0 4303 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
michael@0 4304 size_t *typePool,
michael@0 4305 size_t *baselineStubsOptimized)
michael@0 4306 {
michael@0 4307 *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
michael@0 4308 #ifdef JS_ION
michael@0 4309 if (jitZone()) {
michael@0 4310 *baselineStubsOptimized +=
michael@0 4311 jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf);
michael@0 4312 }
michael@0 4313 #endif
michael@0 4314 }
michael@0 4315
michael@0 4316 void
michael@0 4317 TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
michael@0 4318 size_t *allocationSiteTables,
michael@0 4319 size_t *arrayTypeTables,
michael@0 4320 size_t *objectTypeTables)
michael@0 4321 {
michael@0 4322 if (allocationSiteTable)
michael@0 4323 *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
michael@0 4324
michael@0 4325 if (arrayTypeTable)
michael@0 4326 *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf);
michael@0 4327
michael@0 4328 if (objectTypeTable) {
michael@0 4329 *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf);
michael@0 4330
michael@0 4331 for (ObjectTypeTable::Enum e(*objectTypeTable);
michael@0 4332 !e.empty();
michael@0 4333 e.popFront())
michael@0 4334 {
michael@0 4335 const ObjectTableKey &key = e.front().key();
michael@0 4336 const ObjectTableEntry &value = e.front().value();
michael@0 4337
michael@0 4338 /* key.ids and values.types have the same length. */
michael@0 4339 *objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
michael@0 4340 }
michael@0 4341 }
michael@0 4342 }
michael@0 4343
michael@0 4344 size_t
michael@0 4345 TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 4346 {
michael@0 4347 return mallocSizeOf(addendum);
michael@0 4348 }
michael@0 4349
michael@0 4350 TypeZone::TypeZone(Zone *zone)
michael@0 4351 : zone_(zone),
michael@0 4352 typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
michael@0 4353 compilerOutputs(nullptr),
michael@0 4354 pendingRecompiles(nullptr)
michael@0 4355 {
michael@0 4356 }
michael@0 4357
michael@0 4358 TypeZone::~TypeZone()
michael@0 4359 {
michael@0 4360 js_delete(compilerOutputs);
michael@0 4361 js_delete(pendingRecompiles);
michael@0 4362 }
michael@0 4363
michael@0 4364 void
michael@0 4365 TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
michael@0 4366 {
michael@0 4367 JS_ASSERT(zone()->isGCSweeping());
michael@0 4368
michael@0 4369 JSRuntime *rt = fop->runtime();
michael@0 4370
michael@0 4371 /*
michael@0 4372 * Clear the analysis pool, but don't release its data yet. While
michael@0 4373 * sweeping types any live data will be allocated into the pool.
michael@0 4374 */
michael@0 4375 LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
michael@0 4376 oldAlloc.steal(&typeLifoAlloc);
michael@0 4377
michael@0 4378 /* Sweep and find compressed indexes for each compiler output. */
michael@0 4379 size_t newCompilerOutputCount = 0;
michael@0 4380
michael@0 4381 #ifdef JS_ION
michael@0 4382 if (compilerOutputs) {
michael@0 4383 for (size_t i = 0; i < compilerOutputs->length(); i++) {
michael@0 4384 CompilerOutput &output = (*compilerOutputs)[i];
michael@0 4385 if (output.isValid()) {
michael@0 4386 JSScript *script = output.script();
michael@0 4387 if (IsScriptAboutToBeFinalized(&script)) {
michael@0 4388 jit::GetIonScript(script, output.mode())->recompileInfoRef() = uint32_t(-1);
michael@0 4389 output.invalidate();
michael@0 4390 } else {
michael@0 4391 output.setSweepIndex(newCompilerOutputCount++);
michael@0 4392 }
michael@0 4393 }
michael@0 4394 }
michael@0 4395 }
michael@0 4396 #endif
michael@0 4397
michael@0 4398 {
michael@0 4399 gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
michael@0 4400
michael@0 4401 for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
michael@0 4402 JSScript *script = i.get<JSScript>();
michael@0 4403 if (script->types) {
michael@0 4404 types::TypeScript::Sweep(fop, script, oom);
michael@0 4405
michael@0 4406 if (releaseTypes) {
michael@0 4407 if (script->hasParallelIonScript()) {
michael@0 4408 #ifdef JS_ION
michael@0 4409 // It's possible that we preserved the parallel
michael@0 4410 // IonScript. The heuristic for their preservation is
michael@0 4411 // independent of general JIT code preservation.
michael@0 4412 MOZ_ASSERT(jit::ShouldPreserveParallelJITCode(rt, script));
michael@0 4413 script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
michael@0 4414 #else
michael@0 4415 MOZ_CRASH();
michael@0 4416 #endif
michael@0 4417 } else {
michael@0 4418 script->types->destroy();
michael@0 4419 script->types = nullptr;
michael@0 4420
michael@0 4421 /*
michael@0 4422 * Freeze constraints on stack type sets need to be
michael@0 4423 * regenerated the next time the script is analyzed.
michael@0 4424 */
michael@0 4425 script->clearHasFreezeConstraints();
michael@0 4426 }
michael@0 4427
michael@0 4428 JS_ASSERT(!script->hasIonScript());
michael@0 4429 } else {
michael@0 4430 /* Update the recompile indexes in any IonScripts still on the script. */
michael@0 4431 if (script->hasIonScript())
michael@0 4432 script->ionScript()->recompileInfoRef().shouldSweep(*this);
michael@0 4433 if (script->hasParallelIonScript())
michael@0 4434 script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
michael@0 4435 }
michael@0 4436 }
michael@0 4437 }
michael@0 4438 }
michael@0 4439
michael@0 4440 {
michael@0 4441 gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES);
michael@0 4442
michael@0 4443 for (gc::CellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT);
michael@0 4444 !iter.done(); iter.next())
michael@0 4445 {
michael@0 4446 TypeObject *object = iter.get<TypeObject>();
michael@0 4447 object->sweep(fop, oom);
michael@0 4448 }
michael@0 4449
michael@0 4450 for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
michael@0 4451 comp->types.sweep(fop);
michael@0 4452 }
michael@0 4453
michael@0 4454 if (compilerOutputs) {
michael@0 4455 size_t sweepIndex = 0;
michael@0 4456 for (size_t i = 0; i < compilerOutputs->length(); i++) {
michael@0 4457 CompilerOutput output = (*compilerOutputs)[i];
michael@0 4458 if (output.isValid()) {
michael@0 4459 JS_ASSERT(sweepIndex == output.sweepIndex());
michael@0 4460 output.invalidateSweepIndex();
michael@0 4461 (*compilerOutputs)[sweepIndex++] = output;
michael@0 4462 }
michael@0 4463 }
michael@0 4464 JS_ASSERT(sweepIndex == newCompilerOutputCount);
michael@0 4465 JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
michael@0 4466 }
michael@0 4467
michael@0 4468 {
michael@0 4469 gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA);
michael@0 4470 rt->freeLifoAlloc.transferFrom(&oldAlloc);
michael@0 4471 }
michael@0 4472 }
michael@0 4473
michael@0 4474 void
michael@0 4475 TypeZone::clearAllNewScriptAddendumsOnOOM()
michael@0 4476 {
michael@0 4477 for (gc::CellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT);
michael@0 4478 !iter.done(); iter.next())
michael@0 4479 {
michael@0 4480 TypeObject *object = iter.get<TypeObject>();
michael@0 4481 object->maybeClearNewScriptAddendumOnOOM();
michael@0 4482 }
michael@0 4483 }
michael@0 4484
michael@0 4485 #ifdef DEBUG
michael@0 4486 void
michael@0 4487 TypeScript::printTypes(JSContext *cx, HandleScript script) const
michael@0 4488 {
michael@0 4489 JS_ASSERT(script->types == this);
michael@0 4490
michael@0 4491 if (!script->hasBaselineScript())
michael@0 4492 return;
michael@0 4493
michael@0 4494 AutoEnterAnalysis enter(nullptr, script->compartment());
michael@0 4495
michael@0 4496 if (script->functionNonDelazifying())
michael@0 4497 fprintf(stderr, "Function");
michael@0 4498 else if (script->isForEval())
michael@0 4499 fprintf(stderr, "Eval");
michael@0 4500 else
michael@0 4501 fprintf(stderr, "Main");
michael@0 4502 fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno());
michael@0 4503
michael@0 4504 if (script->functionNonDelazifying()) {
michael@0 4505 if (js::PropertyName *name = script->functionNonDelazifying()->name()) {
michael@0 4506 const jschar *chars = name->getChars(nullptr);
michael@0 4507 JSString::dumpChars(chars, name->length());
michael@0 4508 }
michael@0 4509 }
michael@0 4510
michael@0 4511 fprintf(stderr, "\n this:");
michael@0 4512 TypeScript::ThisTypes(script)->print();
michael@0 4513
michael@0 4514 for (unsigned i = 0;
michael@0 4515 script->functionNonDelazifying() && i < script->functionNonDelazifying()->nargs();
michael@0 4516 i++)
michael@0 4517 {
michael@0 4518 fprintf(stderr, "\n arg%u:", i);
michael@0 4519 TypeScript::ArgTypes(script, i)->print();
michael@0 4520 }
michael@0 4521 fprintf(stderr, "\n");
michael@0 4522
michael@0 4523 for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
michael@0 4524 PrintBytecode(cx, script, pc);
michael@0 4525
michael@0 4526 if (js_CodeSpec[*pc].format & JOF_TYPESET) {
michael@0 4527 StackTypeSet *types = TypeScript::BytecodeTypes(script, pc);
michael@0 4528 fprintf(stderr, " typeset %u:", unsigned(types - typeArray()));
michael@0 4529 types->print();
michael@0 4530 fprintf(stderr, "\n");
michael@0 4531 }
michael@0 4532 }
michael@0 4533
michael@0 4534 fprintf(stderr, "\n");
michael@0 4535 }
michael@0 4536 #endif /* DEBUG */
michael@0 4537
michael@0 4538 /////////////////////////////////////////////////////////////////////
michael@0 4539 // Binary data
michael@0 4540 /////////////////////////////////////////////////////////////////////
michael@0 4541
michael@0 4542 void
michael@0 4543 TypeObject::setAddendum(TypeObjectAddendum *addendum)
michael@0 4544 {
michael@0 4545 this->addendum = addendum;
michael@0 4546 }
michael@0 4547
michael@0 4548 bool
michael@0 4549 TypeObject::addTypedObjectAddendum(JSContext *cx, Handle<TypeDescr*> descr)
michael@0 4550 {
michael@0 4551 // Type descriptors are always pre-tenured. This is both because
michael@0 4552 // we expect them to live a long time and so that they can be
michael@0 4553 // safely accessed during ion compilation.
michael@0 4554 JS_ASSERT(!IsInsideNursery(cx->runtime(), descr));
michael@0 4555 JS_ASSERT(descr);
michael@0 4556
michael@0 4557 if (flags() & OBJECT_FLAG_ADDENDUM_CLEARED)
michael@0 4558 return true;
michael@0 4559
michael@0 4560 JS_ASSERT(!unknownProperties());
michael@0 4561
michael@0 4562 if (addendum) {
michael@0 4563 JS_ASSERT(hasTypedObject());
michael@0 4564 JS_ASSERT(&typedObject()->descr() == descr);
michael@0 4565 return true;
michael@0 4566 }
michael@0 4567
michael@0 4568 TypeTypedObject *typedObject = js_new<TypeTypedObject>(descr);
michael@0 4569 if (!typedObject)
michael@0 4570 return false;
michael@0 4571 addendum = typedObject;
michael@0 4572 return true;
michael@0 4573 }
michael@0 4574
michael@0 4575 /////////////////////////////////////////////////////////////////////
michael@0 4576 // Type object addenda constructor
michael@0 4577 /////////////////////////////////////////////////////////////////////
michael@0 4578
michael@0 4579 TypeObjectAddendum::TypeObjectAddendum(Kind kind)
michael@0 4580 : kind(kind)
michael@0 4581 {}
michael@0 4582
michael@0 4583 TypeNewScript::TypeNewScript()
michael@0 4584 : TypeObjectAddendum(NewScript)
michael@0 4585 {}
michael@0 4586
michael@0 4587 TypeTypedObject::TypeTypedObject(Handle<TypeDescr*> descr)
michael@0 4588 : TypeObjectAddendum(TypedObject),
michael@0 4589 descr_(descr)
michael@0 4590 {
michael@0 4591 }
michael@0 4592
michael@0 4593 TypeDescr &
michael@0 4594 js::types::TypeTypedObject::descr() {
michael@0 4595 return descr_->as<TypeDescr>();
michael@0 4596 }

mercurial