js/src/jsinfer.cpp

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

mercurial