js/src/jsinfer.cpp

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

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

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

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

mercurial