PageRenderTime 135ms CodeModel.GetById 59ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 0ms

/js/src/jsinferinlines.h

http://github.com/zpao/v8monkey
C Header | 1444 lines | 1058 code | 210 blank | 176 comment | 220 complexity | 1c5c1d75de0a9d2c5a0af2941ae3caf9 MD5 | raw file
   1/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
   2/* vim: set ts=40 sw=4 et tw=99: */
   3/* ***** BEGIN LICENSE BLOCK *****
   4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
   5 *
   6 * The contents of this file are subject to the Mozilla Public License Version
   7 * 1.1 (the "License"); you may not use this file except in compliance with
   8 * the License. You may obtain a copy of the License at
   9 * http://www.mozilla.org/MPL/
  10 *
  11 * Software distributed under the License is distributed on an "AS IS" basis,
  12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13 * for the specific language governing rights and limitations under the
  14 * License.
  15 *
  16 * The Original Code is the Mozilla SpiderMonkey bytecode type inference
  17 *
  18 * The Initial Developer of the Original Code is
  19 *   Mozilla Foundation
  20 * Portions created by the Initial Developer are Copyright (C) 2010
  21 * the Initial Developer. All Rights Reserved.
  22 *
  23 * Contributor(s):
  24 *   Brian Hackett <bhackett@mozilla.com>
  25 *
  26 * Alternatively, the contents of this file may be used under the terms of
  27 * either of the GNU General Public License Version 2 or later (the "GPL"),
  28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29 * in which case the provisions of the GPL or the LGPL are applicable instead
  30 * of those above. If you wish to allow use of your version of this file only
  31 * under the terms of either the GPL or the LGPL, and not to allow others to
  32 * use your version of this file under the terms of the MPL, indicate your
  33 * decision by deleting the provisions above and replace them with the notice
  34 * and other provisions required by the GPL or the LGPL. If you do not delete
  35 * the provisions above, a recipient may use your version of this file under
  36 * the terms of any one of the MPL, the GPL or the LGPL.
  37 *
  38 * ***** END LICENSE BLOCK ***** */
  39
  40/* Inline members for javascript type inference. */
  41
  42#include "jsarray.h"
  43#include "jsanalyze.h"
  44#include "jscompartment.h"
  45#include "jsgcmark.h"
  46#include "jsinfer.h"
  47#include "jsprf.h"
  48#include "vm/GlobalObject.h"
  49
  50#include "vm/Stack-inl.h"
  51
  52#ifndef jsinferinlines_h___
  53#define jsinferinlines_h___
  54
  55/////////////////////////////////////////////////////////////////////
  56// Types
  57/////////////////////////////////////////////////////////////////////
  58
  59namespace js {
  60namespace types {
  61
  62/* static */ inline Type
  63Type::ObjectType(JSObject *obj)
  64{
  65    if (obj->hasSingletonType())
  66        return Type(uintptr_t(obj) | 1);
  67    return Type(uintptr_t(obj->type()));
  68}
  69
  70/* static */ inline Type
  71Type::ObjectType(TypeObject *obj)
  72{
  73    if (obj->singleton)
  74        return Type(uintptr_t(obj->singleton.get()) | 1);
  75    return Type(uintptr_t(obj));
  76}
  77
  78/* static */ inline Type
  79Type::ObjectType(TypeObjectKey *obj)
  80{
  81    return Type(uintptr_t(obj));
  82}
  83
  84inline Type
  85GetValueType(JSContext *cx, const Value &val)
  86{
  87    JS_ASSERT(cx->typeInferenceEnabled());
  88    if (val.isDouble())
  89        return Type::DoubleType();
  90    if (val.isObject())
  91        return Type::ObjectType(&val.toObject());
  92    return Type::PrimitiveType(val.extractNonDoubleType());
  93}
  94
  95inline TypeFlags
  96PrimitiveTypeFlag(JSValueType type)
  97{
  98    switch (type) {
  99      case JSVAL_TYPE_UNDEFINED:
 100        return TYPE_FLAG_UNDEFINED;
 101      case JSVAL_TYPE_NULL:
 102        return TYPE_FLAG_NULL;
 103      case JSVAL_TYPE_BOOLEAN:
 104        return TYPE_FLAG_BOOLEAN;
 105      case JSVAL_TYPE_INT32:
 106        return TYPE_FLAG_INT32;
 107      case JSVAL_TYPE_DOUBLE:
 108        return TYPE_FLAG_DOUBLE;
 109      case JSVAL_TYPE_STRING:
 110        return TYPE_FLAG_STRING;
 111      case JSVAL_TYPE_MAGIC:
 112        return TYPE_FLAG_LAZYARGS;
 113      default:
 114        JS_NOT_REACHED("Bad type");
 115        return 0;
 116    }
 117}
 118
 119inline JSValueType
 120TypeFlagPrimitive(TypeFlags flags)
 121{
 122    switch (flags) {
 123      case TYPE_FLAG_UNDEFINED:
 124        return JSVAL_TYPE_UNDEFINED;
 125      case TYPE_FLAG_NULL:
 126        return JSVAL_TYPE_NULL;
 127      case TYPE_FLAG_BOOLEAN:
 128        return JSVAL_TYPE_BOOLEAN;
 129      case TYPE_FLAG_INT32:
 130        return JSVAL_TYPE_INT32;
 131      case TYPE_FLAG_DOUBLE:
 132        return JSVAL_TYPE_DOUBLE;
 133      case TYPE_FLAG_STRING:
 134        return JSVAL_TYPE_STRING;
 135      case TYPE_FLAG_LAZYARGS:
 136        return JSVAL_TYPE_MAGIC;
 137      default:
 138        JS_NOT_REACHED("Bad type");
 139        return (JSValueType) 0;
 140    }
 141}
 142
 143/*
 144 * Get the canonical representation of an id to use when doing inference.  This
 145 * maintains the constraint that if two different jsids map to the same property
 146 * in JS (e.g. 3 and "3"), they have the same type representation.
 147 */
 148inline jsid
 149MakeTypeId(JSContext *cx, jsid id)
 150{
 151    JS_ASSERT(!JSID_IS_EMPTY(id));
 152
 153    /*
 154     * All integers must map to the aggregate property for index types, including
 155     * negative integers.
 156     */
 157    if (JSID_IS_INT(id))
 158        return JSID_VOID;
 159
 160    /*
 161     * Check for numeric strings, as in js_StringIsIndex, but allow negative
 162     * and overflowing integers.
 163     */
 164    if (JSID_IS_STRING(id)) {
 165        JSFlatString *str = JSID_TO_FLAT_STRING(id);
 166        const jschar *cp = str->getCharsZ(cx);
 167        if (JS7_ISDEC(*cp) || *cp == '-') {
 168            cp++;
 169            while (JS7_ISDEC(*cp))
 170                cp++;
 171            if (*cp == 0)
 172                return JSID_VOID;
 173        }
 174        return id;
 175    }
 176
 177    return JSID_VOID;
 178}
 179
 180const char * TypeIdStringImpl(jsid id);
 181
 182/* Convert an id for printing during debug. */
 183static inline const char *
 184TypeIdString(jsid id)
 185{
 186#ifdef DEBUG
 187    return TypeIdStringImpl(id);
 188#else
 189    return "(missing)";
 190#endif
 191}
 192
 193/*
 194 * Structure for type inference entry point functions. All functions which can
 195 * change type information must use this, and functions which depend on
 196 * intermediate types (i.e. JITs) can use this to ensure that intermediate
 197 * information is not collected and does not change.
 198 *
 199 * Pins inference results so that intermediate type information, TypeObjects
 200 * and JSScripts won't be collected during GC. Does additional sanity checking
 201 * that inference is not reentrant and that recompilations occur properly.
 202 */
 203struct AutoEnterTypeInference
 204{
 205    JSContext *cx;
 206    bool oldActiveAnalysis;
 207    bool oldActiveInference;
 208
 209    AutoEnterTypeInference(JSContext *cx, bool compiling = false)
 210        : cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis),
 211          oldActiveInference(cx->compartment->activeInference)
 212    {
 213        JS_ASSERT_IF(!compiling, cx->compartment->types.inferenceEnabled);
 214        cx->compartment->activeAnalysis = true;
 215        cx->compartment->activeInference = true;
 216    }
 217
 218    ~AutoEnterTypeInference()
 219    {
 220        cx->compartment->activeAnalysis = oldActiveAnalysis;
 221        cx->compartment->activeInference = oldActiveInference;
 222
 223        /*
 224         * If there are no more type inference activations on the stack,
 225         * process any triggered recompilations. Note that we should not be
 226         * invoking any scripted code while type inference is running.
 227         * :TODO: assert this.
 228         */
 229        if (!cx->compartment->activeInference) {
 230            TypeCompartment *types = &cx->compartment->types;
 231            if (types->pendingNukeTypes)
 232                types->nukeTypes(cx);
 233            else if (types->pendingRecompiles)
 234                types->processPendingRecompiles(cx);
 235        }
 236    }
 237};
 238
 239/*
 240 * Structure marking the currently compiled script, for constraints which can
 241 * trigger recompilation.
 242 */
 243struct AutoEnterCompilation
 244{
 245    RecompileInfo &info;
 246
 247    AutoEnterCompilation(JSContext *cx, JSScript *script, bool constructing, unsigned chunkIndex)
 248        : info(cx->compartment->types.compiledInfo)
 249    {
 250        JS_ASSERT(!info.script);
 251        info.script = script;
 252        info.constructing = constructing;
 253        info.chunkIndex = chunkIndex;
 254    }
 255
 256    ~AutoEnterCompilation()
 257    {
 258        JS_ASSERT(info.script);
 259        info.script = NULL;
 260        info.constructing = false;
 261        info.chunkIndex = 0;
 262    }
 263};
 264
 265/////////////////////////////////////////////////////////////////////
 266// Interface functions
 267/////////////////////////////////////////////////////////////////////
 268
 269/*
 270 * These functions check whether inference is enabled before performing some
 271 * action on the type state. To avoid checking cx->typeInferenceEnabled()
 272 * everywhere, it is generally preferred to use one of these functions or
 273 * a type function on JSScript to perform inference operations.
 274 */
 275
 276/*
 277 * Get the default 'new' object for a given standard class, per the currently
 278 * active global.
 279 */
 280inline TypeObject *
 281GetTypeNewObject(JSContext *cx, JSProtoKey key)
 282{
 283    JSObject *proto;
 284    if (!js_GetClassPrototype(cx, NULL, key, &proto, NULL))
 285        return NULL;
 286    return proto->getNewType(cx);
 287}
 288
 289/* Get a type object for the immediate allocation site within a native. */
 290inline TypeObject *
 291GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
 292{
 293    if (cx->typeInferenceEnabled()) {
 294        jsbytecode *pc;
 295        JSScript *script = cx->stack.currentScript(&pc);
 296        if (script)
 297            return TypeScript::InitObject(cx, script, pc, key);
 298    }
 299    return GetTypeNewObject(cx, key);
 300}
 301
 302/*
 303 * When using a custom iterator within the initialization of a 'for in' loop,
 304 * mark the iterator values as unknown.
 305 */
 306inline void
 307MarkIteratorUnknown(JSContext *cx)
 308{
 309    extern void MarkIteratorUnknownSlow(JSContext *cx);
 310
 311    if (cx->typeInferenceEnabled())
 312        MarkIteratorUnknownSlow(cx);
 313}
 314
 315/*
 316 * Monitor a javascript call, either on entry to the interpreter or made
 317 * from within the interpreter.
 318 */
 319inline void
 320TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
 321{
 322    extern void TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
 323                                    const CallArgs &args, bool constructing);
 324
 325    JSObject *callee = &args.callee();
 326    if (callee->isFunction()) {
 327        JSFunction *fun = callee->toFunction();
 328        if (fun->isInterpreted()) {
 329            JSScript *script = fun->script();
 330            if (!script->ensureRanAnalysis(cx, fun->environment()))
 331                return;
 332            if (cx->typeInferenceEnabled())
 333                TypeMonitorCallSlow(cx, callee, args, constructing);
 334        }
 335    }
 336}
 337
 338inline bool
 339TrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
 340{
 341    if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties())
 342        return false;
 343
 344    if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(cx, id))
 345        return false;
 346
 347    return true;
 348}
 349
 350/* Add a possible type for a property of obj. */
 351inline void
 352AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, Type type)
 353{
 354    if (cx->typeInferenceEnabled())
 355        id = MakeTypeId(cx, id);
 356    if (TrackPropertyTypes(cx, obj, id))
 357        obj->type()->addPropertyType(cx, id, type);
 358}
 359
 360inline void
 361AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, const Value &value)
 362{
 363    if (cx->typeInferenceEnabled())
 364        id = MakeTypeId(cx, id);
 365    if (TrackPropertyTypes(cx, obj, id))
 366        obj->type()->addPropertyType(cx, id, value);
 367}
 368
 369inline void
 370AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type)
 371{
 372    if (cx->typeInferenceEnabled() && !obj->unknownProperties())
 373        obj->addPropertyType(cx, name, type);
 374}
 375
 376inline void
 377AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value)
 378{
 379    if (cx->typeInferenceEnabled() && !obj->unknownProperties())
 380        obj->addPropertyType(cx, name, value);
 381}
 382
 383/* Set one or more dynamic flags on a type object. */
 384inline void
 385MarkTypeObjectFlags(JSContext *cx, JSObject *obj, TypeObjectFlags flags)
 386{
 387    if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->hasAllFlags(flags))
 388        obj->type()->setFlags(cx, flags);
 389}
 390
 391/*
 392 * Mark all properties of a type object as unknown. If markSetsUnknown is set,
 393 * scan the entire compartment and mark all type sets containing it as having
 394 * an unknown object. This is needed for correctness in dealing with mutable
 395 * __proto__, which can change the type of an object dynamically.
 396 */
 397inline void
 398MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
 399                                bool markSetsUnknown = false)
 400{
 401    if (cx->typeInferenceEnabled()) {
 402        if (!obj->unknownProperties())
 403            obj->markUnknown(cx);
 404        if (markSetsUnknown && !(obj->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
 405            cx->compartment->types.markSetsUnknown(cx, obj);
 406    }
 407}
 408
 409/*
 410 * Mark any property which has been deleted or configured to be non-writable or
 411 * have a getter/setter.
 412 */
 413inline void
 414MarkTypePropertyConfigured(JSContext *cx, JSObject *obj, jsid id)
 415{
 416    if (cx->typeInferenceEnabled())
 417        id = MakeTypeId(cx, id);
 418    if (TrackPropertyTypes(cx, obj, id))
 419        obj->type()->markPropertyConfigured(cx, id);
 420}
 421
 422/* Mark a state change on a particular object. */
 423inline void
 424MarkObjectStateChange(JSContext *cx, JSObject *obj)
 425{
 426    if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties())
 427        obj->type()->markStateChange(cx);
 428}
 429
 430/*
 431 * For an array or object which has not yet escaped and been referenced elsewhere,
 432 * pick a new type based on the object's current contents.
 433 */
 434
 435inline void
 436FixArrayType(JSContext *cx, JSObject *obj)
 437{
 438    if (cx->typeInferenceEnabled())
 439        cx->compartment->types.fixArrayType(cx, obj);
 440}
 441
 442inline void
 443FixObjectType(JSContext *cx, JSObject *obj)
 444{
 445    if (cx->typeInferenceEnabled())
 446        cx->compartment->types.fixObjectType(cx, obj);
 447}
 448
 449/* Interface helpers for JSScript */
 450extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval);
 451extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::Type type);
 452
 453inline bool
 454UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
 455{
 456    return fp->isConstructing() && cx->typeInferenceEnabled() &&
 457           fp->prev() && fp->prev()->isScriptFrame() &&
 458           UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp));
 459}
 460
 461/////////////////////////////////////////////////////////////////////
 462// Script interface functions
 463/////////////////////////////////////////////////////////////////////
 464
 465inline
 466TypeScript::TypeScript()
 467{
 468    this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE;
 469}
 470
 471/* static */ inline unsigned
 472TypeScript::NumTypeSets(JSScript *script)
 473{
 474    return script->nTypeSets + analyze::TotalSlots(script);
 475}
 476
 477/* static */ inline TypeSet *
 478TypeScript::ReturnTypes(JSScript *script)
 479{
 480    return script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot();
 481}
 482
 483/* static */ inline TypeSet *
 484TypeScript::ThisTypes(JSScript *script)
 485{
 486    return script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
 487}
 488
 489/*
 490 * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
 491 * only the initial type of the variable (e.g. passed values for argTypes,
 492 * or undefined for localTypes) and not types from subsequent assignments.
 493 */
 494
 495/* static */ inline TypeSet *
 496TypeScript::ArgTypes(JSScript *script, unsigned i)
 497{
 498    JS_ASSERT(i < script->function()->nargs);
 499    return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
 500}
 501
 502/* static */ inline TypeSet *
 503TypeScript::LocalTypes(JSScript *script, unsigned i)
 504{
 505    JS_ASSERT(i < script->nfixed);
 506    return script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i);
 507}
 508
 509/* static */ inline TypeSet *
 510TypeScript::SlotTypes(JSScript *script, unsigned slot)
 511{
 512    JS_ASSERT(slot < js::analyze::TotalSlots(script));
 513    return script->types->typeArray() + script->nTypeSets + slot;
 514}
 515
 516/* static */ inline TypeObject *
 517TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
 518{
 519    JSObject *proto;
 520    if (!js_GetClassPrototype(cx, script->global(), key, &proto, NULL))
 521        return NULL;
 522    return proto->getNewType(cx);
 523}
 524
 525struct AllocationSiteKey {
 526    JSScript *script;
 527
 528    uint32_t offset : 24;
 529    JSProtoKey kind : 8;
 530
 531    static const uint32_t OFFSET_LIMIT = (1 << 23);
 532
 533    AllocationSiteKey() { PodZero(this); }
 534
 535    typedef AllocationSiteKey Lookup;
 536
 537    static inline uint32_t hash(AllocationSiteKey key) {
 538        return uint32_t(size_t(key.script->code + key.offset)) ^ key.kind;
 539    }
 540
 541    static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
 542        return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
 543    }
 544};
 545
 546/* static */ inline TypeObject *
 547TypeScript::InitObject(JSContext *cx, JSScript *script, const jsbytecode *pc, JSProtoKey kind)
 548{
 549    /* :XXX: Limit script->length so we don't need to check the offset up front? */
 550    uint32_t offset = pc - script->code;
 551
 552    if (!cx->typeInferenceEnabled() || !script->hasGlobal() || offset >= AllocationSiteKey::OFFSET_LIMIT)
 553        return GetTypeNewObject(cx, kind);
 554
 555    AllocationSiteKey key;
 556    key.script = script;
 557    key.offset = offset;
 558    key.kind = kind;
 559
 560    if (!cx->compartment->types.allocationSiteTable)
 561        return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
 562
 563    AllocationSiteTable::Ptr p = cx->compartment->types.allocationSiteTable->lookup(key);
 564
 565    if (p)
 566        return p->value;
 567    return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
 568}
 569
 570/* static */ inline void
 571TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 572{
 573    if (cx->typeInferenceEnabled())
 574        TypeMonitorResult(cx, script, pc, rval);
 575}
 576
 577/* static */ inline void
 578TypeScript::MonitorOverflow(JSContext *cx, JSScript *script, jsbytecode *pc)
 579{
 580    if (cx->typeInferenceEnabled())
 581        TypeDynamicResult(cx, script, pc, Type::DoubleType());
 582}
 583
 584/* static */ inline void
 585TypeScript::MonitorString(JSContext *cx, JSScript *script, jsbytecode *pc)
 586{
 587    if (cx->typeInferenceEnabled())
 588        TypeDynamicResult(cx, script, pc, Type::StringType());
 589}
 590
 591/* static */ inline void
 592TypeScript::MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc)
 593{
 594    if (cx->typeInferenceEnabled())
 595        TypeDynamicResult(cx, script, pc, Type::UnknownType());
 596}
 597
 598/* static */ inline void
 599TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc)
 600{
 601    *script = cx->fp()->script();
 602    *pc = cx->regs().pc;
 603}
 604
 605/* static */ inline void
 606TypeScript::MonitorOverflow(JSContext *cx)
 607{
 608    JSScript *script;
 609    jsbytecode *pc;
 610    GetPcScript(cx, &script, &pc);
 611    MonitorOverflow(cx, script, pc);
 612}
 613
 614/* static */ inline void
 615TypeScript::MonitorString(JSContext *cx)
 616{
 617    JSScript *script;
 618    jsbytecode *pc;
 619    GetPcScript(cx, &script, &pc);
 620    MonitorString(cx, script, pc);
 621}
 622
 623/* static */ inline void
 624TypeScript::MonitorUnknown(JSContext *cx)
 625{
 626    JSScript *script;
 627    jsbytecode *pc;
 628    GetPcScript(cx, &script, &pc);
 629    MonitorUnknown(cx, script, pc);
 630}
 631
 632/* static */ inline void
 633TypeScript::Monitor(JSContext *cx, const js::Value &rval)
 634{
 635    JSScript *script;
 636    jsbytecode *pc;
 637    GetPcScript(cx, &script, &pc);
 638    Monitor(cx, script, pc, rval);
 639}
 640
 641/* static */ inline void
 642TypeScript::MonitorAssign(JSContext *cx, JSScript *script, jsbytecode *pc,
 643                          JSObject *obj, jsid id, const js::Value &rval)
 644{
 645    if (cx->typeInferenceEnabled() && !obj->hasSingletonType()) {
 646        /*
 647         * Mark as unknown any object which has had dynamic assignments to
 648         * non-integer properties at SETELEM opcodes. This avoids making large
 649         * numbers of type properties for hashmap-style objects. We don't need
 650         * to do this for objects with singleton type, because type properties
 651         * are only constructed for them when analyzed scripts depend on those
 652         * specific properties.
 653         */
 654        uint32_t i;
 655        if (js_IdIsIndex(id, &i))
 656            return;
 657        MarkTypeObjectUnknownProperties(cx, obj->type());
 658    }
 659}
 660
 661/* static */ inline void
 662TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
 663{
 664    if (!cx->typeInferenceEnabled())
 665        return;
 666    JS_ASSERT(script->types);
 667
 668    /* Analyze the script regardless if -a was used. */
 669    bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS);
 670
 671    if (!ThisTypes(script)->hasType(type) || analyze) {
 672        AutoEnterTypeInference enter(cx);
 673
 674        InferSpew(ISpewOps, "externalType: setThis #%u: %s",
 675                  script->id(), TypeString(type));
 676        ThisTypes(script)->addType(cx, type);
 677
 678        if (analyze && script->types->hasScope())
 679            script->ensureRanInference(cx);
 680    }
 681}
 682
 683/* static */ inline void
 684TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
 685{
 686    if (cx->typeInferenceEnabled())
 687        SetThis(cx, script, GetValueType(cx, value));
 688}
 689
 690/* static */ inline void
 691TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type)
 692{
 693    if (!cx->typeInferenceEnabled())
 694        return;
 695    JS_ASSERT(script->types);
 696
 697    if (!LocalTypes(script, local)->hasType(type)) {
 698        AutoEnterTypeInference enter(cx);
 699
 700        InferSpew(ISpewOps, "externalType: setLocal #%u %u: %s",
 701                  script->id(), local, TypeString(type));
 702        LocalTypes(script, local)->addType(cx, type);
 703    }
 704}
 705
 706/* static */ inline void
 707TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value)
 708{
 709    if (cx->typeInferenceEnabled()) {
 710        Type type = GetValueType(cx, value);
 711        SetLocal(cx, script, local, type);
 712    }
 713}
 714
 715/* static */ inline void
 716TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
 717{
 718    if (!cx->typeInferenceEnabled())
 719        return;
 720    JS_ASSERT(script->types);
 721
 722    if (!ArgTypes(script, arg)->hasType(type)) {
 723        AutoEnterTypeInference enter(cx);
 724
 725        InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
 726                  script->id(), arg, TypeString(type));
 727        ArgTypes(script, arg)->addType(cx, type);
 728    }
 729}
 730
 731/* static */ inline void
 732TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
 733{
 734    if (cx->typeInferenceEnabled()) {
 735        Type type = GetValueType(cx, value);
 736        SetArgument(cx, script, arg, type);
 737    }
 738}
 739
 740void
 741TypeScript::trace(JSTracer *trc)
 742{
 743    if (hasScope() && global)
 744        gc::MarkObject(trc, global, "script_global");
 745
 746    /* Note: nesting does not keep anything alive. */
 747}
 748
 749/////////////////////////////////////////////////////////////////////
 750// TypeCompartment
 751/////////////////////////////////////////////////////////////////////
 752
 753inline JSCompartment *
 754TypeCompartment::compartment()
 755{
 756    return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
 757}
 758
 759inline void
 760TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, Type type)
 761{
 762    JS_ASSERT(this == &cx->compartment->types);
 763    JS_ASSERT(!cx->runtime->gcRunning);
 764
 765    InferSpew(ISpewOps, "pending: %sC%p%s %s",
 766              InferSpewColor(constraint), constraint, InferSpewColorReset(),
 767              TypeString(type));
 768
 769    if ((pendingCount == pendingCapacity) && !growPendingArray(cx))
 770        return;
 771
 772    PendingWork &pending = pendingArray[pendingCount++];
 773    pending.constraint = constraint;
 774    pending.source = source;
 775    pending.type = type;
 776}
 777
 778inline void
 779TypeCompartment::resolvePending(JSContext *cx)
 780{
 781    JS_ASSERT(this == &cx->compartment->types);
 782
 783    if (resolving) {
 784        /* There is an active call further up resolving the worklist. */
 785        return;
 786    }
 787
 788    resolving = true;
 789
 790    /* Handle all pending type registrations. */
 791    while (pendingCount) {
 792        const PendingWork &pending = pendingArray[--pendingCount];
 793        InferSpew(ISpewOps, "resolve: %sC%p%s %s",
 794                  InferSpewColor(pending.constraint), pending.constraint,
 795                  InferSpewColorReset(), TypeString(pending.type));
 796        pending.constraint->newType(cx, pending.source, pending.type);
 797    }
 798
 799    resolving = false;
 800}
 801
 802/////////////////////////////////////////////////////////////////////
 803// TypeSet
 804/////////////////////////////////////////////////////////////////////
 805
 806/*
 807 * The sets of objects and scripts in a type set grow monotonically, are usually
 808 * empty, almost always small, and sometimes big.  For empty or singleton sets,
 809 * the pointer refers directly to the value.  For sets fitting into SET_ARRAY_SIZE,
 810 * an array of this length is used to store the elements.  For larger sets, a hash
 811 * table filled to 25%-50% of capacity is used, with collisions resolved by linear
 812 * probing.  TODO: replace these with jshashtables.
 813 */
 814const unsigned SET_ARRAY_SIZE = 8;
 815
 816/* Get the capacity of a set with the given element count. */
 817static inline unsigned
 818HashSetCapacity(unsigned count)
 819{
 820    JS_ASSERT(count >= 2);
 821
 822    if (count <= SET_ARRAY_SIZE)
 823        return SET_ARRAY_SIZE;
 824
 825    unsigned log2;
 826    JS_FLOOR_LOG2(log2, count);
 827    return 1 << (log2 + 2);
 828}
 829
 830/* Compute the FNV hash for the low 32 bits of v. */
 831template <class T, class KEY>
 832static inline uint32_t
 833HashKey(T v)
 834{
 835    uint32_t nv = KEY::keyBits(v);
 836
 837    uint32_t hash = 84696351 ^ (nv & 0xff);
 838    hash = (hash * 16777619) ^ ((nv >> 8) & 0xff);
 839    hash = (hash * 16777619) ^ ((nv >> 16) & 0xff);
 840    return (hash * 16777619) ^ ((nv >> 24) & 0xff);
 841}
 842
 843/*
 844 * Insert space for an element into the specified set and grow its capacity if needed.
 845 * returned value is an existing or new entry (NULL if new).
 846 */
 847template <class T, class U, class KEY>
 848static U **
 849HashSetInsertTry(JSCompartment *compartment, U **&values, unsigned &count, T key)
 850{
 851    unsigned capacity = HashSetCapacity(count);
 852    unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
 853
 854    /* Whether we are converting from a fixed array to hashtable. */
 855    bool converting = (count == SET_ARRAY_SIZE);
 856
 857    if (!converting) {
 858        while (values[insertpos] != NULL) {
 859            if (KEY::getKey(values[insertpos]) == key)
 860                return &values[insertpos];
 861            insertpos = (insertpos + 1) & (capacity - 1);
 862        }
 863    }
 864
 865    count++;
 866    unsigned newCapacity = HashSetCapacity(count);
 867
 868    if (newCapacity == capacity) {
 869        JS_ASSERT(!converting);
 870        return &values[insertpos];
 871    }
 872
 873    U **newValues = compartment->typeLifoAlloc.newArray<U*>(newCapacity);
 874    if (!newValues)
 875        return NULL;
 876    PodZero(newValues, newCapacity);
 877
 878    for (unsigned i = 0; i < capacity; i++) {
 879        if (values[i]) {
 880            unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
 881            while (newValues[pos] != NULL)
 882                pos = (pos + 1) & (newCapacity - 1);
 883            newValues[pos] = values[i];
 884        }
 885    }
 886
 887    values = newValues;
 888
 889    insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
 890    while (values[insertpos] != NULL)
 891        insertpos = (insertpos + 1) & (newCapacity - 1);
 892    return &values[insertpos];
 893}
 894
 895/*
 896 * Insert an element into the specified set if it is not already there, returning
 897 * an entry which is NULL if the element was not there.
 898 */
 899template <class T, class U, class KEY>
 900static inline U **
 901HashSetInsert(JSCompartment *compartment, U **&values, unsigned &count, T key)
 902{
 903    if (count == 0) {
 904        JS_ASSERT(values == NULL);
 905        count++;
 906        return (U **) &values;
 907    }
 908
 909    if (count == 1) {
 910        U *oldData = (U*) values;
 911        if (KEY::getKey(oldData) == key)
 912            return (U **) &values;
 913
 914        values = compartment->typeLifoAlloc.newArray<U*>(SET_ARRAY_SIZE);
 915        if (!values) {
 916            values = (U **) oldData;
 917            return NULL;
 918        }
 919        PodZero(values, SET_ARRAY_SIZE);
 920        count++;
 921
 922        values[0] = oldData;
 923        return &values[1];
 924    }
 925
 926    if (count <= SET_ARRAY_SIZE) {
 927        for (unsigned i = 0; i < count; i++) {
 928            if (KEY::getKey(values[i]) == key)
 929                return &values[i];
 930        }
 931
 932        if (count < SET_ARRAY_SIZE) {
 933            count++;
 934            return &values[count - 1];
 935        }
 936    }
 937
 938    return HashSetInsertTry<T,U,KEY>(compartment, values, count, key);
 939}
 940
 941/* Lookup an entry in a hash set, return NULL if it does not exist. */
 942template <class T, class U, class KEY>
 943static inline U *
 944HashSetLookup(U **values, unsigned count, T key)
 945{
 946    if (count == 0)
 947        return NULL;
 948
 949    if (count == 1)
 950        return (KEY::getKey((U *) values) == key) ? (U *) values : NULL;
 951
 952    if (count <= SET_ARRAY_SIZE) {
 953        for (unsigned i = 0; i < count; i++) {
 954            if (KEY::getKey(values[i]) == key)
 955                return values[i];
 956        }
 957        return NULL;
 958    }
 959
 960    unsigned capacity = HashSetCapacity(count);
 961    unsigned pos = HashKey<T,KEY>(key) & (capacity - 1);
 962
 963    while (values[pos] != NULL) {
 964        if (KEY::getKey(values[pos]) == key)
 965            return values[pos];
 966        pos = (pos + 1) & (capacity - 1);
 967    }
 968
 969    return NULL;
 970}
 971
 972inline bool
 973TypeSet::hasType(Type type)
 974{
 975    if (unknown())
 976        return true;
 977
 978    if (type.isUnknown()) {
 979        return false;
 980    } else if (type.isPrimitive()) {
 981        return !!(flags & PrimitiveTypeFlag(type.primitive()));
 982    } else if (type.isAnyObject()) {
 983        return !!(flags & TYPE_FLAG_ANYOBJECT);
 984    } else {
 985        return !!(flags & TYPE_FLAG_ANYOBJECT) ||
 986            HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
 987            (objectSet, baseObjectCount(), type.objectKey()) != NULL;
 988    }
 989}
 990
 991inline void
 992TypeSet::setBaseObjectCount(uint32_t count)
 993{
 994    JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT);
 995    flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK)
 996          | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
 997}
 998
 999inline void
1000TypeSet::clearObjects()
1001{
1002    setBaseObjectCount(0);
1003    objectSet = NULL;
1004}
1005
1006inline void
1007TypeSet::addType(JSContext *cx, Type type)
1008{
1009    JS_ASSERT(cx->compartment->activeInference);
1010
1011    if (unknown())
1012        return;
1013
1014    if (type.isUnknown()) {
1015        flags |= TYPE_FLAG_BASE_MASK;
1016        clearObjects();
1017        JS_ASSERT(unknown());
1018    } else if (type.isPrimitive()) {
1019        TypeFlags flag = PrimitiveTypeFlag(type.primitive());
1020        if (flags & flag)
1021            return;
1022
1023        /* If we add float to a type set it is also considered to contain int. */
1024        if (flag == TYPE_FLAG_DOUBLE)
1025            flag |= TYPE_FLAG_INT32;
1026
1027        flags |= flag;
1028    } else {
1029        if (flags & TYPE_FLAG_ANYOBJECT)
1030            return;
1031        if (type.isAnyObject())
1032            goto unknownObject;
1033        uint32_t objectCount = baseObjectCount();
1034        TypeObjectKey *object = type.objectKey();
1035        TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
1036                                     (cx->compartment, objectSet, objectCount, object);
1037        if (!pentry) {
1038            cx->compartment->types.setPendingNukeTypes(cx);
1039            return;
1040        }
1041        if (*pentry)
1042            return;
1043        *pentry = object;
1044
1045        setBaseObjectCount(objectCount);
1046
1047        if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
1048            goto unknownObject;
1049
1050        if (type.isTypeObject()) {
1051            TypeObject *nobject = type.typeObject();
1052            JS_ASSERT(!nobject->singleton);
1053            if (nobject->unknownProperties())
1054                goto unknownObject;
1055            if (objectCount > 1) {
1056                nobject->contribution += (objectCount - 1) * (objectCount - 1);
1057                if (nobject->contribution >= TypeObject::CONTRIBUTION_LIMIT) {
1058                    InferSpew(ISpewOps, "limitUnknown: %sT%p%s",
1059                              InferSpewColor(this), this, InferSpewColorReset());
1060                    goto unknownObject;
1061                }
1062            }
1063        }
1064    }
1065
1066    if (false) {
1067    unknownObject:
1068        type = Type::AnyObjectType();
1069        flags |= TYPE_FLAG_ANYOBJECT;
1070        clearObjects();
1071    }
1072
1073    InferSpew(ISpewOps, "addType: %sT%p%s %s",
1074              InferSpewColor(this), this, InferSpewColorReset(),
1075              TypeString(type));
1076
1077    /* Propagate the type to all constraints. */
1078    TypeConstraint *constraint = constraintList;
1079    while (constraint) {
1080        cx->compartment->types.addPending(cx, constraint, this, type);
1081        constraint = constraint->next;
1082    }
1083
1084    cx->compartment->types.resolvePending(cx);
1085}
1086
1087inline void
1088TypeSet::setOwnProperty(JSContext *cx, bool configured)
1089{
1090    TypeFlags nflags = TYPE_FLAG_OWN_PROPERTY | (configured ? TYPE_FLAG_CONFIGURED_PROPERTY : 0);
1091
1092    if ((flags & nflags) == nflags)
1093        return;
1094
1095    flags |= nflags;
1096
1097    /* Propagate the change to all constraints. */
1098    TypeConstraint *constraint = constraintList;
1099    while (constraint) {
1100        constraint->newPropertyState(cx, this);
1101        constraint = constraint->next;
1102    }
1103}
1104
1105inline unsigned
1106TypeSet::getObjectCount()
1107{
1108    JS_ASSERT(!unknownObject());
1109    uint32_t count = baseObjectCount();
1110    if (count > SET_ARRAY_SIZE)
1111        return HashSetCapacity(count);
1112    return count;
1113}
1114
1115inline TypeObjectKey *
1116TypeSet::getObject(unsigned i)
1117{
1118    JS_ASSERT(i < getObjectCount());
1119    if (baseObjectCount() == 1) {
1120        JS_ASSERT(i == 0);
1121        return (TypeObjectKey *) objectSet;
1122    }
1123    return objectSet[i];
1124}
1125
1126inline JSObject *
1127TypeSet::getSingleObject(unsigned i)
1128{
1129    TypeObjectKey *key = getObject(i);
1130    return (uintptr_t(key) & 1) ? (JSObject *)(uintptr_t(key) ^ 1) : NULL;
1131}
1132
1133inline TypeObject *
1134TypeSet::getTypeObject(unsigned i)
1135{
1136    TypeObjectKey *key = getObject(i);
1137    return (key && !(uintptr_t(key) & 1)) ? (TypeObject *) key : NULL;
1138}
1139
1140/////////////////////////////////////////////////////////////////////
1141// TypeCallsite
1142/////////////////////////////////////////////////////////////////////
1143
1144inline
1145TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
1146                           bool isNew, unsigned argumentCount)
1147    : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
1148      thisTypes(NULL), returnTypes(NULL)
1149{
1150    /* Caller must check for failure. */
1151    argumentTypes = cx->typeLifoAlloc().newArray<TypeSet*>(argumentCount);
1152}
1153
1154/////////////////////////////////////////////////////////////////////
1155// TypeObject
1156/////////////////////////////////////////////////////////////////////
1157
1158inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
1159{
1160    PodZero(this);
1161
1162    /* Inner objects may not appear on prototype chains. */
1163    JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject);
1164
1165    this->proto = proto;
1166
1167    if (function)
1168        flags |= OBJECT_FLAG_FUNCTION;
1169    if (unknown)
1170        flags |= OBJECT_FLAG_UNKNOWN_MASK;
1171
1172    InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
1173}
1174
1175inline uint32_t
1176TypeObject::basePropertyCount() const
1177{
1178    return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
1179}
1180
1181inline void
1182TypeObject::setBasePropertyCount(uint32_t count)
1183{
1184    JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
1185    flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
1186          | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
1187}
1188
1189inline TypeSet *
1190TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
1191{
1192    JS_ASSERT(cx->compartment->activeInference);
1193    JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
1194    JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
1195    JS_ASSERT(!unknownProperties());
1196
1197    uint32_t propertyCount = basePropertyCount();
1198    Property **pprop = HashSetInsert<jsid,Property,Property>
1199                           (cx->compartment, propertySet, propertyCount, id);
1200    if (!pprop) {
1201        cx->compartment->types.setPendingNukeTypes(cx);
1202        return NULL;
1203    }
1204
1205    if (!*pprop) {
1206        setBasePropertyCount(propertyCount);
1207        if (!addProperty(cx, id, pprop))
1208            return NULL;
1209        if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
1210            markUnknown(cx);
1211            TypeSet *types = TypeSet::make(cx, "propertyOverflow");
1212            types->addType(cx, Type::UnknownType());
1213            return types;
1214        }
1215    }
1216
1217    TypeSet *types = &(*pprop)->types;
1218
1219    if (assign)
1220        types->setOwnProperty(cx, false);
1221
1222    return types;
1223}
1224
1225inline TypeSet *
1226TypeObject::maybeGetProperty(JSContext *cx, jsid id)
1227{
1228    JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
1229    JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
1230    JS_ASSERT(!unknownProperties());
1231
1232    Property *prop = HashSetLookup<jsid,Property,Property>
1233        (propertySet, basePropertyCount(), id);
1234
1235    return prop ? &prop->types : NULL;
1236}
1237
1238inline unsigned
1239TypeObject::getPropertyCount()
1240{
1241    uint32_t count = basePropertyCount();
1242    if (count > SET_ARRAY_SIZE)
1243        return HashSetCapacity(count);
1244    return count;
1245}
1246
1247inline Property *
1248TypeObject::getProperty(unsigned i)
1249{
1250    JS_ASSERT(i < getPropertyCount());
1251    if (basePropertyCount() == 1) {
1252        JS_ASSERT(i == 0);
1253        return (Property *) propertySet;
1254    }
1255    return propertySet[i];
1256}
1257
1258inline void
1259TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
1260{
1261    TypeObjectFlags flags = 0;
1262
1263    switch (key) {
1264      case JSProto_Function:
1265        JS_ASSERT(isFunction());
1266        /* FALLTHROUGH */
1267
1268      case JSProto_Object:
1269        flags = OBJECT_FLAG_NON_DENSE_ARRAY
1270              | OBJECT_FLAG_NON_PACKED_ARRAY
1271              | OBJECT_FLAG_NON_TYPED_ARRAY;
1272        break;
1273
1274      case JSProto_Array:
1275        flags = OBJECT_FLAG_NON_TYPED_ARRAY;
1276        break;
1277
1278      default:
1279        /* :XXX: abstract */
1280        JS_ASSERT(key == JSProto_Int8Array ||
1281                  key == JSProto_Uint8Array ||
1282                  key == JSProto_Int16Array ||
1283                  key == JSProto_Uint16Array ||
1284                  key == JSProto_Int32Array ||
1285                  key == JSProto_Uint32Array ||
1286                  key == JSProto_Float32Array ||
1287                  key == JSProto_Float64Array ||
1288                  key == JSProto_Uint8ClampedArray);
1289        flags = OBJECT_FLAG_NON_DENSE_ARRAY
1290              | OBJECT_FLAG_NON_PACKED_ARRAY;
1291        break;
1292    }
1293
1294    if (!hasAllFlags(flags))
1295        setFlags(cx, flags);
1296}
1297
1298inline JSObject *
1299TypeObject::getGlobal()
1300{
1301    if (singleton)
1302        return &singleton->global();
1303    if (interpretedFunction && interpretedFunction->script()->compileAndGo)
1304        return &interpretedFunction->global();
1305    return NULL;
1306}
1307
1308inline void
1309TypeObject::writeBarrierPre(TypeObject *type)
1310{
1311#ifdef JSGC_INCREMENTAL
1312    if (!type)
1313        return;
1314
1315    JSCompartment *comp = type->compartment();
1316    if (comp->needsBarrier())
1317        MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "write barrier");
1318#endif
1319}
1320
1321inline void
1322TypeObject::writeBarrierPost(TypeObject *type, void *addr)
1323{
1324}
1325
1326inline void
1327TypeObject::readBarrier(TypeObject *type)
1328{
1329#ifdef JSGC_INCREMENTAL
1330    JSCompartment *comp = type->compartment();
1331    if (comp->needsBarrier())
1332        MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier");
1333#endif
1334}
1335
1336inline void
1337TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
1338{
1339#ifdef JSGC_INCREMENTAL
1340    if (!newScript)
1341        return;
1342
1343    JSCompartment *comp = newScript->fun->compartment();
1344    if (comp->needsBarrier()) {
1345        MarkObjectUnbarriered(comp->barrierTracer(), newScript->fun, "write barrier");
1346        MarkShape(comp->barrierTracer(), newScript->shape, "write barrier");
1347    }
1348#endif
1349}
1350
1351inline void
1352TypeNewScript::writeBarrierPost(TypeNewScript *newScript, void *addr)
1353{
1354}
1355
1356inline
1357Property::Property(jsid id)
1358  : id(id)
1359{
1360}
1361
1362inline
1363Property::Property(const Property &o)
1364  : id(o.id.get()), types(o.types)
1365{
1366}
1367
1368} } /* namespace js::types */
1369
1370inline bool
1371JSScript::ensureHasTypes(JSContext *cx)
1372{
1373    return types || makeTypes(cx);
1374}
1375
1376inline bool
1377JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope)
1378{
1379    JSScript *self = this;
1380
1381    if (!self->ensureHasTypes(cx))
1382        return false;
1383    if (!self->types->hasScope()) {
1384        js::CheckRoot root(cx, &self);
1385        js::RootObject objRoot(cx, &scope);
1386        if (!js::types::TypeScript::SetScope(cx, self, scope))
1387            return false;
1388    }
1389    if (!self->hasAnalysis() && !self->makeAnalysis(cx))
1390        return false;
1391    JS_ASSERT(self->analysis()->ranBytecode());
1392    return true;
1393}
1394
1395inline bool
1396JSScript::ensureRanInference(JSContext *cx)
1397{
1398    if (!ensureRanAnalysis(cx, NULL))
1399        return false;
1400    if (!analysis()->ranInference()) {
1401        js::types::AutoEnterTypeInference enter(cx);
1402        analysis()->analyzeTypes(cx);
1403    }
1404    return !analysis()->OOM() &&
1405        !cx->compartment->types.pendingNukeTypes;
1406}
1407
1408inline bool
1409JSScript::hasAnalysis()
1410{
1411    return types && types->analysis;
1412}
1413
1414inline js::analyze::ScriptAnalysis *
1415JSScript::analysis()
1416{
1417    JS_ASSERT(hasAnalysis());
1418    return types->analysis;
1419}
1420
1421inline void
1422JSScript::clearAnalysis()
1423{
1424    if (types)
1425        types->analysis = NULL;
1426}
1427
1428inline void
1429js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32_t offset, uint32_t which,
1430                                           js::types::Type type)
1431{
1432    js::types::TypeSet *pushed = pushedTypes(offset, which);
1433    pushed->addType(cx, type);
1434}
1435
1436inline js::types::TypeObject *
1437JSCompartment::getEmptyType(JSContext *cx)
1438{
1439    if (!emptyTypeObject)
1440        emptyTypeObject = types.newTypeObject(cx, NULL, JSProto_Object, NULL, true);
1441    return emptyTypeObject;
1442}
1443
1444#endif // jsinferinlines_h___