js/src/jsinferinlines.h

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

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

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

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /* Inline members for javascript type inference. */
     9 #ifndef jsinferinlines_h
    10 #define jsinferinlines_h
    12 #include "jsinfer.h"
    14 #include "mozilla/PodOperations.h"
    16 #include "jsanalyze.h"
    18 #include "vm/ArrayObject.h"
    19 #include "vm/BooleanObject.h"
    20 #include "vm/NumberObject.h"
    21 #include "vm/SharedArrayObject.h"
    22 #include "vm/StringObject.h"
    23 #include "vm/TypedArrayObject.h"
    25 #include "jscntxtinlines.h"
    27 #include "jit/ExecutionMode-inl.h"
    29 namespace js {
    30 namespace types {
    32 /////////////////////////////////////////////////////////////////////
    33 // CompilerOutput & RecompileInfo
    34 /////////////////////////////////////////////////////////////////////
    36 inline jit::IonScript *
    37 CompilerOutput::ion() const
    38 {
    39 #ifdef JS_ION
    40     // Note: If type constraints are generated before compilation has finished
    41     // (i.e. after IonBuilder but before CodeGenerator::link) then a valid
    42     // CompilerOutput may not yet have an associated IonScript.
    43     JS_ASSERT(isValid());
    44     jit::IonScript *ion = jit::GetIonScript(script(), mode());
    45     JS_ASSERT(ion != ION_COMPILING_SCRIPT);
    46     return ion;
    47 #endif
    48     MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput");
    49 }
    51 inline CompilerOutput*
    52 RecompileInfo::compilerOutput(TypeZone &types) const
    53 {
    54     if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length())
    55         return nullptr;
    56     return &(*types.compilerOutputs)[outputIndex];
    57 }
    59 inline CompilerOutput*
    60 RecompileInfo::compilerOutput(JSContext *cx) const
    61 {
    62     return compilerOutput(cx->zone()->types);
    63 }
    65 inline bool
    66 RecompileInfo::shouldSweep(TypeZone &types)
    67 {
    68     CompilerOutput *output = compilerOutput(types);
    69     if (!output || !output->isValid())
    70         return true;
    72     // Update this info for the output's new index in the zone's compiler outputs.
    73     outputIndex = output->sweepIndex();
    74     return false;
    75 }
    77 /////////////////////////////////////////////////////////////////////
    78 // Types
    79 /////////////////////////////////////////////////////////////////////
    81 /* static */ inline Type
    82 Type::ObjectType(JSObject *obj)
    83 {
    84     if (obj->hasSingletonType())
    85         return Type(uintptr_t(obj) | 1);
    86     return Type(uintptr_t(obj->type()));
    87 }
    89 /* static */ inline Type
    90 Type::ObjectType(TypeObject *obj)
    91 {
    92     if (obj->singleton())
    93         return Type(uintptr_t(obj->singleton()) | 1);
    94     return Type(uintptr_t(obj));
    95 }
    97 /* static */ inline Type
    98 Type::ObjectType(TypeObjectKey *obj)
    99 {
   100     return Type(uintptr_t(obj));
   101 }
   103 inline Type
   104 GetValueType(const Value &val)
   105 {
   106     if (val.isDouble())
   107         return Type::DoubleType();
   108     if (val.isObject())
   109         return Type::ObjectType(&val.toObject());
   110     return Type::PrimitiveType(val.extractNonDoubleType());
   111 }
   113 inline Type
   114 GetMaybeOptimizedOutValueType(const Value &val)
   115 {
   116     if (val.isMagic() && val.whyMagic() == JS_OPTIMIZED_OUT)
   117         return Type::UnknownType();
   118     return GetValueType(val);
   119 }
   121 inline TypeFlags
   122 PrimitiveTypeFlag(JSValueType type)
   123 {
   124     switch (type) {
   125       case JSVAL_TYPE_UNDEFINED:
   126         return TYPE_FLAG_UNDEFINED;
   127       case JSVAL_TYPE_NULL:
   128         return TYPE_FLAG_NULL;
   129       case JSVAL_TYPE_BOOLEAN:
   130         return TYPE_FLAG_BOOLEAN;
   131       case JSVAL_TYPE_INT32:
   132         return TYPE_FLAG_INT32;
   133       case JSVAL_TYPE_DOUBLE:
   134         return TYPE_FLAG_DOUBLE;
   135       case JSVAL_TYPE_STRING:
   136         return TYPE_FLAG_STRING;
   137       case JSVAL_TYPE_MAGIC:
   138         return TYPE_FLAG_LAZYARGS;
   139       default:
   140         MOZ_ASSUME_UNREACHABLE("Bad type");
   141     }
   142 }
   144 inline JSValueType
   145 TypeFlagPrimitive(TypeFlags flags)
   146 {
   147     switch (flags) {
   148       case TYPE_FLAG_UNDEFINED:
   149         return JSVAL_TYPE_UNDEFINED;
   150       case TYPE_FLAG_NULL:
   151         return JSVAL_TYPE_NULL;
   152       case TYPE_FLAG_BOOLEAN:
   153         return JSVAL_TYPE_BOOLEAN;
   154       case TYPE_FLAG_INT32:
   155         return JSVAL_TYPE_INT32;
   156       case TYPE_FLAG_DOUBLE:
   157         return JSVAL_TYPE_DOUBLE;
   158       case TYPE_FLAG_STRING:
   159         return JSVAL_TYPE_STRING;
   160       case TYPE_FLAG_LAZYARGS:
   161         return JSVAL_TYPE_MAGIC;
   162       default:
   163         MOZ_ASSUME_UNREACHABLE("Bad type");
   164     }
   165 }
   167 /*
   168  * Get the canonical representation of an id to use when doing inference.  This
   169  * maintains the constraint that if two different jsids map to the same property
   170  * in JS (e.g. 3 and "3"), they have the same type representation.
   171  */
   172 inline jsid
   173 IdToTypeId(jsid id)
   174 {
   175     JS_ASSERT(!JSID_IS_EMPTY(id));
   177     /*
   178      * All integers must map to the aggregate property for index types, including
   179      * negative integers.
   180      */
   181     if (JSID_IS_INT(id))
   182         return JSID_VOID;
   184     /*
   185      * Check for numeric strings, as in js_StringIsIndex, but allow negative
   186      * and overflowing integers.
   187      */
   188     if (JSID_IS_STRING(id)) {
   189         JSAtom *atom = JSID_TO_ATOM(id);
   190         JS::TwoByteChars cp = atom->range();
   191         if (cp.length() > 0 && (JS7_ISDEC(cp[0]) || cp[0] == '-')) {
   192             for (size_t i = 1; i < cp.length(); ++i) {
   193                 if (!JS7_ISDEC(cp[i]))
   194                     return id;
   195             }
   196             return JSID_VOID;
   197         }
   198         return id;
   199     }
   201     return JSID_VOID;
   202 }
   204 const char * TypeIdStringImpl(jsid id);
   206 /* Convert an id for printing during debug. */
   207 static inline const char *
   208 TypeIdString(jsid id)
   209 {
   210 #ifdef DEBUG
   211     return TypeIdStringImpl(id);
   212 #else
   213     return "(missing)";
   214 #endif
   215 }
   217 /*
   218  * Structure for type inference entry point functions. All functions which can
   219  * change type information must use this, and functions which depend on
   220  * intermediate types (i.e. JITs) can use this to ensure that intermediate
   221  * information is not collected and does not change.
   222  *
   223  * Pins inference results so that intermediate type information, TypeObjects
   224  * and JSScripts won't be collected during GC. Does additional sanity checking
   225  * that inference is not reentrant and that recompilations occur properly.
   226  */
   227 struct AutoEnterAnalysis
   228 {
   229     /* Prevent GC activity in the middle of analysis. */
   230     gc::AutoSuppressGC suppressGC;
   232     FreeOp *freeOp;
   233     JSCompartment *compartment;
   234     bool oldActiveAnalysis;
   236     AutoEnterAnalysis(ExclusiveContext *cx)
   237       : suppressGC(cx)
   238     {
   239         init(cx->defaultFreeOp(), cx->compartment());
   240     }
   242     AutoEnterAnalysis(FreeOp *fop, JSCompartment *comp)
   243       : suppressGC(comp)
   244     {
   245         init(fop, comp);
   246     }
   248     ~AutoEnterAnalysis()
   249     {
   250         compartment->activeAnalysis = oldActiveAnalysis;
   252         /*
   253          * If there are no more type inference activations on the stack,
   254          * process any triggered recompilations. Note that we should not be
   255          * invoking any scripted code while type inference is running.
   256          */
   257         if (!compartment->activeAnalysis) {
   258             TypeZone &types = compartment->zone()->types;
   259             if (types.pendingRecompiles)
   260                 types.processPendingRecompiles(freeOp);
   261         }
   262     }
   264   private:
   265     void init(FreeOp *fop, JSCompartment *comp) {
   266         freeOp = fop;
   267         compartment = comp;
   268         oldActiveAnalysis = compartment->activeAnalysis;
   269         compartment->activeAnalysis = true;
   270     }
   271 };
   273 /////////////////////////////////////////////////////////////////////
   274 // Interface functions
   275 /////////////////////////////////////////////////////////////////////
   277 inline const Class *
   278 GetClassForProtoKey(JSProtoKey key)
   279 {
   280     switch (key) {
   281       case JSProto_Object:
   282         return &JSObject::class_;
   283       case JSProto_Array:
   284         return &ArrayObject::class_;
   286       case JSProto_Number:
   287         return &NumberObject::class_;
   288       case JSProto_Boolean:
   289         return &BooleanObject::class_;
   290       case JSProto_String:
   291         return &StringObject::class_;
   292       case JSProto_RegExp:
   293         return &RegExpObject::class_;
   295       case JSProto_Int8Array:
   296       case JSProto_Uint8Array:
   297       case JSProto_Int16Array:
   298       case JSProto_Uint16Array:
   299       case JSProto_Int32Array:
   300       case JSProto_Uint32Array:
   301       case JSProto_Float32Array:
   302       case JSProto_Float64Array:
   303       case JSProto_Uint8ClampedArray:
   304         return &TypedArrayObject::classes[key - JSProto_Int8Array];
   306       case JSProto_ArrayBuffer:
   307         return &ArrayBufferObject::class_;
   309       case JSProto_SharedArrayBuffer:
   310         return &SharedArrayBufferObject::class_;
   312       case JSProto_DataView:
   313         return &DataViewObject::class_;
   315       default:
   316         MOZ_ASSUME_UNREACHABLE("Bad proto key");
   317     }
   318 }
   320 /*
   321  * Get the default 'new' object for a given standard class, per the currently
   322  * active global.
   323  */
   324 inline TypeObject *
   325 GetTypeNewObject(JSContext *cx, JSProtoKey key)
   326 {
   327     RootedObject proto(cx);
   328     if (!GetBuiltinPrototype(cx, key, &proto))
   329         return nullptr;
   330     return cx->getNewType(GetClassForProtoKey(key), proto.get());
   331 }
   333 /* Get a type object for the immediate allocation site within a native. */
   334 inline TypeObject *
   335 GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
   336 {
   337     jsbytecode *pc;
   338     RootedScript script(cx, cx->currentScript(&pc));
   339     if (script)
   340         return TypeScript::InitObject(cx, script, pc, key);
   341     return GetTypeNewObject(cx, key);
   342 }
   344 void MarkIteratorUnknownSlow(JSContext *cx);
   346 void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args,
   347                          bool constructing);
   349 /*
   350  * Monitor a javascript call, either on entry to the interpreter or made
   351  * from within the interpreter.
   352  */
   353 inline void
   354 TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
   355 {
   356     if (args.callee().is<JSFunction>()) {
   357         JSFunction *fun = &args.callee().as<JSFunction>();
   358         if (fun->isInterpreted() && fun->nonLazyScript()->types)
   359             TypeMonitorCallSlow(cx, &args.callee(), args, constructing);
   360     }
   361 }
   363 inline bool
   364 TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id)
   365 {
   366     if (obj->hasLazyType() || obj->type()->unknownProperties())
   367         return false;
   369     if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id))
   370         return false;
   372     return true;
   373 }
   375 inline void
   376 EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
   377 {
   378     id = IdToTypeId(id);
   380     if (obj->hasSingletonType()) {
   381         AutoEnterAnalysis enter(cx);
   382         if (obj->hasLazyType() && !obj->getType(cx)) {
   383             CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes");
   384             return;
   385         }
   386         if (!obj->type()->unknownProperties() && !obj->type()->getProperty(cx, id)) {
   387             MOZ_ASSERT(obj->type()->unknownProperties());
   388             return;
   389         }
   390     }
   392     JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id));
   393 }
   395 inline bool
   396 CanHaveEmptyPropertyTypesForOwnProperty(JSObject *obj)
   397 {
   398     // Per the comment on TypeSet::propertySet, property type sets for global
   399     // objects may be empty for 'own' properties if the global property still
   400     // has its initial undefined value.
   401     return obj->is<GlobalObject>();
   402 }
   404 inline bool
   405 HasTypePropertyId(JSObject *obj, jsid id, Type type)
   406 {
   407     if (obj->hasLazyType())
   408         return true;
   410     if (obj->type()->unknownProperties())
   411         return true;
   413     if (HeapTypeSet *types = obj->type()->maybeGetProperty(IdToTypeId(id)))
   414         return types->hasType(type);
   416     return false;
   417 }
   419 inline bool
   420 HasTypePropertyId(JSObject *obj, jsid id, const Value &value)
   421 {
   422     return HasTypePropertyId(obj, id, GetValueType(value));
   423 }
   425 /* Add a possible type for a property of obj. */
   426 inline void
   427 AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, Type type)
   428 {
   429     id = IdToTypeId(id);
   430     if (TrackPropertyTypes(cx, obj, id))
   431         obj->type()->addPropertyType(cx, id, type);
   432 }
   434 inline void
   435 AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, const Value &value)
   436 {
   437     id = IdToTypeId(id);
   438     if (TrackPropertyTypes(cx, obj, id))
   439         obj->type()->addPropertyType(cx, id, value);
   440 }
   442 inline void
   443 AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type)
   444 {
   445     if (!obj->unknownProperties())
   446         obj->addPropertyType(cx, id, type);
   447 }
   449 inline void
   450 AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, const Value &value)
   451 {
   452     if (!obj->unknownProperties())
   453         obj->addPropertyType(cx, id, value);
   454 }
   456 /* Set one or more dynamic flags on a type object. */
   457 inline void
   458 MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags)
   459 {
   460     if (!obj->hasLazyType() && !obj->type()->hasAllFlags(flags))
   461         obj->type()->setFlags(cx, flags);
   462 }
   464 /*
   465  * Mark all properties of a type object as unknown. If markSetsUnknown is set,
   466  * scan the entire compartment and mark all type sets containing it as having
   467  * an unknown object. This is needed for correctness in dealing with mutable
   468  * __proto__, which can change the type of an object dynamically.
   469  */
   470 inline void
   471 MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
   472                                 bool markSetsUnknown = false)
   473 {
   474     if (!obj->unknownProperties())
   475         obj->markUnknown(cx);
   476     if (markSetsUnknown && !(obj->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
   477         cx->compartment()->types.markSetsUnknown(cx, obj);
   478 }
   480 inline void
   481 MarkTypePropertyNonData(ExclusiveContext *cx, JSObject *obj, jsid id)
   482 {
   483     id = IdToTypeId(id);
   484     if (TrackPropertyTypes(cx, obj, id))
   485         obj->type()->markPropertyNonData(cx, id);
   486 }
   488 inline void
   489 MarkTypePropertyNonWritable(ExclusiveContext *cx, JSObject *obj, jsid id)
   490 {
   491     id = IdToTypeId(id);
   492     if (TrackPropertyTypes(cx, obj, id))
   493         obj->type()->markPropertyNonWritable(cx, id);
   494 }
   496 inline bool
   497 IsTypePropertyIdMarkedNonData(JSObject *obj, jsid id)
   498 {
   499     return obj->type()->isPropertyNonData(id);
   500 }
   502 inline bool
   503 IsTypePropertyIdMarkedNonWritable(JSObject *obj, jsid id)
   504 {
   505     return obj->type()->isPropertyNonWritable(id);
   506 }
   508 /* Mark a state change on a particular object. */
   509 inline void
   510 MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj)
   511 {
   512     if (!obj->hasLazyType() && !obj->type()->unknownProperties())
   513         obj->type()->markStateChange(cx);
   514 }
   516 /*
   517  * For an array or object which has not yet escaped and been referenced elsewhere,
   518  * pick a new type based on the object's current contents.
   519  */
   521 inline void
   522 FixArrayType(ExclusiveContext *cx, HandleObject obj)
   523 {
   524     cx->compartment()->types.fixArrayType(cx, obj);
   525 }
   527 inline void
   528 FixObjectType(ExclusiveContext *cx, HandleObject obj)
   529 {
   530     cx->compartment()->types.fixObjectType(cx, obj);
   531 }
   533 /* Interface helpers for JSScript*. */
   534 extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc,
   535                               const js::Value &rval);
   536 extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc,
   537                               js::types::Type type);
   539 /////////////////////////////////////////////////////////////////////
   540 // Script interface functions
   541 /////////////////////////////////////////////////////////////////////
   543 /* static */ inline unsigned
   544 TypeScript::NumTypeSets(JSScript *script)
   545 {
   546     return script->nTypeSets() + analyze::LocalSlot(script, 0);
   547 }
   549 /* static */ inline StackTypeSet *
   550 TypeScript::ThisTypes(JSScript *script)
   551 {
   552     return script->types->typeArray() + script->nTypeSets() + analyze::ThisSlot();
   553 }
   555 /*
   556  * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
   557  * only the initial type of the variable (e.g. passed values for argTypes,
   558  * or undefined for localTypes) and not types from subsequent assignments.
   559  */
   561 /* static */ inline StackTypeSet *
   562 TypeScript::ArgTypes(JSScript *script, unsigned i)
   563 {
   564     JS_ASSERT(i < script->functionNonDelazifying()->nargs());
   565     return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i);
   566 }
   568 template <typename TYPESET>
   569 /* static */ inline TYPESET *
   570 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
   571                           uint32_t *hint, TYPESET *typeArray)
   572 {
   573     JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
   574     uint32_t offset = script->pcToOffset(pc);
   576     // See if this pc is the next typeset opcode after the last one looked up.
   577     if ((*hint + 1) < script->nTypeSets() && bytecodeMap[*hint + 1] == offset) {
   578         (*hint)++;
   579         return typeArray + *hint;
   580     }
   582     // See if this pc is the same as the last one looked up.
   583     if (bytecodeMap[*hint] == offset)
   584         return typeArray + *hint;
   586     // Fall back to a binary search.
   587     size_t bottom = 0;
   588     size_t top = script->nTypeSets() - 1;
   589     size_t mid = bottom + (top - bottom) / 2;
   590     while (mid < top) {
   591         if (bytecodeMap[mid] < offset)
   592             bottom = mid + 1;
   593         else if (bytecodeMap[mid] > offset)
   594             top = mid;
   595         else
   596             break;
   597         mid = bottom + (top - bottom) / 2;
   598     }
   600     // We should have have zeroed in on either the exact offset, unless there
   601     // are more JOF_TYPESET opcodes than nTypeSets in the script (as can happen
   602     // if the script is very long).
   603     JS_ASSERT(bytecodeMap[mid] == offset || mid == top);
   605     *hint = mid;
   606     return typeArray + *hint;
   607 }
   609 /* static */ inline StackTypeSet *
   610 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
   611 {
   612     JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
   613 #ifdef JS_ION
   614     uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
   615     return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
   616                          hint, script->types->typeArray());
   617 #else
   618     MOZ_CRASH();
   619 #endif
   620 }
   622 struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
   623     JSScript *script;
   625     uint32_t offset : 24;
   626     JSProtoKey kind : 8;
   628     static const uint32_t OFFSET_LIMIT = (1 << 23);
   630     AllocationSiteKey() { mozilla::PodZero(this); }
   632     static inline uint32_t hash(AllocationSiteKey key) {
   633         return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind);
   634     }
   636     static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
   637         return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
   638     }
   639 };
   641 /* Whether to use a new type object for an initializer opcode at script/pc. */
   642 js::NewObjectKind
   643 UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key);
   645 js::NewObjectKind
   646 UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp);
   648 /* static */ inline TypeObject *
   649 TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind)
   650 {
   651     JS_ASSERT(!UseNewTypeForInitializer(script, pc, kind));
   653     /* :XXX: Limit script->length so we don't need to check the offset up front? */
   654     uint32_t offset = script->pcToOffset(pc);
   656     if (!script->compileAndGo() || offset >= AllocationSiteKey::OFFSET_LIMIT)
   657         return GetTypeNewObject(cx, kind);
   659     AllocationSiteKey key;
   660     key.script = script;
   661     key.offset = offset;
   662     key.kind = kind;
   664     if (!cx->compartment()->types.allocationSiteTable)
   665         return cx->compartment()->types.addAllocationSiteTypeObject(cx, key);
   667     AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key);
   669     if (p)
   670         return p->value();
   671     return cx->compartment()->types.addAllocationSiteTypeObject(cx, key);
   672 }
   674 /* Set the type to use for obj according to the site it was allocated at. */
   675 static inline bool
   676 SetInitializerObjectType(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, NewObjectKind kind)
   677 {
   678     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
   679     JS_ASSERT(key != JSProto_Null);
   680     JS_ASSERT(kind == UseNewTypeForInitializer(script, pc, key));
   682     if (kind == SingletonObject) {
   683         JS_ASSERT(obj->hasSingletonType());
   685         /*
   686          * Inference does not account for types of run-once initializer
   687          * objects, as these may not be created until after the script
   688          * has been analyzed.
   689          */
   690         TypeScript::Monitor(cx, script, pc, ObjectValue(*obj));
   691     } else {
   692         types::TypeObject *type = TypeScript::InitObject(cx, script, pc, key);
   693         if (!type)
   694             return false;
   695         obj->uninlinedSetType(type);
   696     }
   698     return true;
   699 }
   701 /* static */ inline void
   702 TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
   703 {
   704     TypeMonitorResult(cx, script, pc, rval);
   705 }
   707 /* static */ inline void
   708 TypeScript::Monitor(JSContext *cx, const js::Value &rval)
   709 {
   710     jsbytecode *pc;
   711     RootedScript script(cx, cx->currentScript(&pc));
   712     Monitor(cx, script, pc, rval);
   713 }
   715 /* static */ inline void
   716 TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id)
   717 {
   718     if (!obj->hasSingletonType()) {
   719         /*
   720          * Mark as unknown any object which has had dynamic assignments to
   721          * non-integer properties at SETELEM opcodes. This avoids making large
   722          * numbers of type properties for hashmap-style objects. We don't need
   723          * to do this for objects with singleton type, because type properties
   724          * are only constructed for them when analyzed scripts depend on those
   725          * specific properties.
   726          */
   727         uint32_t i;
   728         if (js_IdIsIndex(id, &i))
   729             return;
   731         // But if we don't have too many properties yet, don't do anything.  The
   732         // idea here is that normal object initialization should not trigger
   733         // deoptimization in most cases, while actual usage as a hashmap should.
   734         TypeObject* type = obj->type();
   735         if (type->getPropertyCount() < 8)
   736             return;
   737         MarkTypeObjectUnknownProperties(cx, type);
   738     }
   739 }
   741 /* static */ inline void
   742 TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
   743 {
   744     if (!script->types)
   745         return;
   747     if (!ThisTypes(script)->hasType(type)) {
   748         AutoEnterAnalysis enter(cx);
   750         InferSpew(ISpewOps, "externalType: setThis #%u: %s",
   751                   script->id(), TypeString(type));
   752         ThisTypes(script)->addType(cx, type);
   753     }
   754 }
   756 /* static */ inline void
   757 TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
   758 {
   759     SetThis(cx, script, GetValueType(value));
   760 }
   762 /* static */ inline void
   763 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
   764 {
   765     if (!script->types)
   766         return;
   768     if (!ArgTypes(script, arg)->hasType(type)) {
   769         AutoEnterAnalysis enter(cx);
   771         InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
   772                   script->id(), arg, TypeString(type));
   773         ArgTypes(script, arg)->addType(cx, type);
   774     }
   775 }
   777 /* static */ inline void
   778 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
   779 {
   780     Type type = GetValueType(value);
   781     SetArgument(cx, script, arg, type);
   782 }
   784 /////////////////////////////////////////////////////////////////////
   785 // TypeCompartment
   786 /////////////////////////////////////////////////////////////////////
   788 inline JSCompartment *
   789 TypeCompartment::compartment()
   790 {
   791     return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
   792 }
   794 /////////////////////////////////////////////////////////////////////
   795 // TypeSet
   796 /////////////////////////////////////////////////////////////////////
   798 /*
   799  * The sets of objects and scripts in a type set grow monotonically, are usually
   800  * empty, almost always small, and sometimes big.  For empty or singleton sets,
   801  * the pointer refers directly to the value.  For sets fitting into SET_ARRAY_SIZE,
   802  * an array of this length is used to store the elements.  For larger sets, a hash
   803  * table filled to 25%-50% of capacity is used, with collisions resolved by linear
   804  * probing.  TODO: replace these with jshashtables.
   805  */
   806 const unsigned SET_ARRAY_SIZE = 8;
   807 const unsigned SET_CAPACITY_OVERFLOW = 1u << 30;
   809 /* Get the capacity of a set with the given element count. */
   810 static inline unsigned
   811 HashSetCapacity(unsigned count)
   812 {
   813     JS_ASSERT(count >= 2);
   814     JS_ASSERT(count < SET_CAPACITY_OVERFLOW);
   816     if (count <= SET_ARRAY_SIZE)
   817         return SET_ARRAY_SIZE;
   819     return 1u << (mozilla::FloorLog2(count) + 2);
   820 }
   822 /* Compute the FNV hash for the low 32 bits of v. */
   823 template <class T, class KEY>
   824 static inline uint32_t
   825 HashKey(T v)
   826 {
   827     uint32_t nv = KEY::keyBits(v);
   829     uint32_t hash = 84696351 ^ (nv & 0xff);
   830     hash = (hash * 16777619) ^ ((nv >> 8) & 0xff);
   831     hash = (hash * 16777619) ^ ((nv >> 16) & 0xff);
   832     return (hash * 16777619) ^ ((nv >> 24) & 0xff);
   833 }
   835 /*
   836  * Insert space for an element into the specified set and grow its capacity if needed.
   837  * returned value is an existing or new entry (nullptr if new).
   838  */
   839 template <class T, class U, class KEY>
   840 static U **
   841 HashSetInsertTry(LifoAlloc &alloc, U **&values, unsigned &count, T key)
   842 {
   843     unsigned capacity = HashSetCapacity(count);
   844     unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
   846     /* Whether we are converting from a fixed array to hashtable. */
   847     bool converting = (count == SET_ARRAY_SIZE);
   849     if (!converting) {
   850         while (values[insertpos] != nullptr) {
   851             if (KEY::getKey(values[insertpos]) == key)
   852                 return &values[insertpos];
   853             insertpos = (insertpos + 1) & (capacity - 1);
   854         }
   855     }
   857     if (count >= SET_CAPACITY_OVERFLOW)
   858         return nullptr;
   860     count++;
   861     unsigned newCapacity = HashSetCapacity(count);
   863     if (newCapacity == capacity) {
   864         JS_ASSERT(!converting);
   865         return &values[insertpos];
   866     }
   868     U **newValues = alloc.newArray<U*>(newCapacity);
   869     if (!newValues)
   870         return nullptr;
   871     mozilla::PodZero(newValues, newCapacity);
   873     for (unsigned i = 0; i < capacity; i++) {
   874         if (values[i]) {
   875             unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
   876             while (newValues[pos] != nullptr)
   877                 pos = (pos + 1) & (newCapacity - 1);
   878             newValues[pos] = values[i];
   879         }
   880     }
   882     values = newValues;
   884     insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
   885     while (values[insertpos] != nullptr)
   886         insertpos = (insertpos + 1) & (newCapacity - 1);
   887     return &values[insertpos];
   888 }
   890 /*
   891  * Insert an element into the specified set if it is not already there, returning
   892  * an entry which is nullptr if the element was not there.
   893  */
   894 template <class T, class U, class KEY>
   895 static inline U **
   896 HashSetInsert(LifoAlloc &alloc, U **&values, unsigned &count, T key)
   897 {
   898     if (count == 0) {
   899         JS_ASSERT(values == nullptr);
   900         count++;
   901         return (U **) &values;
   902     }
   904     if (count == 1) {
   905         U *oldData = (U*) values;
   906         if (KEY::getKey(oldData) == key)
   907             return (U **) &values;
   909         values = alloc.newArray<U*>(SET_ARRAY_SIZE);
   910         if (!values) {
   911             values = (U **) oldData;
   912             return nullptr;
   913         }
   914         mozilla::PodZero(values, SET_ARRAY_SIZE);
   915         count++;
   917         values[0] = oldData;
   918         return &values[1];
   919     }
   921     if (count <= SET_ARRAY_SIZE) {
   922         for (unsigned i = 0; i < count; i++) {
   923             if (KEY::getKey(values[i]) == key)
   924                 return &values[i];
   925         }
   927         if (count < SET_ARRAY_SIZE) {
   928             count++;
   929             return &values[count - 1];
   930         }
   931     }
   933     return HashSetInsertTry<T,U,KEY>(alloc, values, count, key);
   934 }
   936 /* Lookup an entry in a hash set, return nullptr if it does not exist. */
   937 template <class T, class U, class KEY>
   938 static inline U *
   939 HashSetLookup(U **values, unsigned count, T key)
   940 {
   941     if (count == 0)
   942         return nullptr;
   944     if (count == 1)
   945         return (KEY::getKey((U *) values) == key) ? (U *) values : nullptr;
   947     if (count <= SET_ARRAY_SIZE) {
   948         for (unsigned i = 0; i < count; i++) {
   949             if (KEY::getKey(values[i]) == key)
   950                 return values[i];
   951         }
   952         return nullptr;
   953     }
   955     unsigned capacity = HashSetCapacity(count);
   956     unsigned pos = HashKey<T,KEY>(key) & (capacity - 1);
   958     while (values[pos] != nullptr) {
   959         if (KEY::getKey(values[pos]) == key)
   960             return values[pos];
   961         pos = (pos + 1) & (capacity - 1);
   962     }
   964     return nullptr;
   965 }
   967 inline TypeObjectKey *
   968 Type::objectKey() const
   969 {
   970     JS_ASSERT(isObject());
   971     if (isTypeObject())
   972         TypeObject::readBarrier((TypeObject *) data);
   973     else
   974         JSObject::readBarrier((JSObject *) (data ^ 1));
   975     return (TypeObjectKey *) data;
   976 }
   978 inline JSObject *
   979 Type::singleObject() const
   980 {
   981     JS_ASSERT(isSingleObject());
   982     JSObject::readBarrier((JSObject *) (data ^ 1));
   983     return (JSObject *) (data ^ 1);
   984 }
   986 inline TypeObject *
   987 Type::typeObject() const
   988 {
   989     JS_ASSERT(isTypeObject());
   990     TypeObject::readBarrier((TypeObject *) data);
   991     return (TypeObject *) data;
   992 }
   994 inline bool
   995 TypeSet::hasType(Type type) const
   996 {
   997     if (unknown())
   998         return true;
  1000     if (type.isUnknown()) {
  1001         return false;
  1002     } else if (type.isPrimitive()) {
  1003         return !!(flags & PrimitiveTypeFlag(type.primitive()));
  1004     } else if (type.isAnyObject()) {
  1005         return !!(flags & TYPE_FLAG_ANYOBJECT);
  1006     } else {
  1007         return !!(flags & TYPE_FLAG_ANYOBJECT) ||
  1008             HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
  1009             (objectSet, baseObjectCount(), type.objectKey()) != nullptr;
  1013 inline void
  1014 TypeSet::setBaseObjectCount(uint32_t count)
  1016     JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT);
  1017     flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK)
  1018           | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
  1021 inline void
  1022 HeapTypeSet::newPropertyState(ExclusiveContext *cxArg)
  1024     /* Propagate the change to all constraints. */
  1025     if (JSContext *cx = cxArg->maybeJSContext()) {
  1026         TypeConstraint *constraint = constraintList;
  1027         while (constraint) {
  1028             constraint->newPropertyState(cx, this);
  1029             constraint = constraint->next;
  1031     } else {
  1032         JS_ASSERT(!constraintList);
  1036 inline void
  1037 HeapTypeSet::setNonDataPropertyIgnoringConstraints()
  1039     flags |= TYPE_FLAG_NON_DATA_PROPERTY;
  1042 inline void
  1043 HeapTypeSet::setNonDataProperty(ExclusiveContext *cx)
  1045     if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
  1046         return;
  1048     setNonDataPropertyIgnoringConstraints();
  1049     newPropertyState(cx);
  1052 inline void
  1053 HeapTypeSet::setNonWritableProperty(ExclusiveContext *cx)
  1055     if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY)
  1056         return;
  1058     flags |= TYPE_FLAG_NON_WRITABLE_PROPERTY;
  1059     newPropertyState(cx);
  1062 inline unsigned
  1063 TypeSet::getObjectCount() const
  1065     JS_ASSERT(!unknownObject());
  1066     uint32_t count = baseObjectCount();
  1067     if (count > SET_ARRAY_SIZE)
  1068         return HashSetCapacity(count);
  1069     return count;
  1072 inline TypeObjectKey *
  1073 TypeSet::getObject(unsigned i) const
  1075     JS_ASSERT(i < getObjectCount());
  1076     if (baseObjectCount() == 1) {
  1077         JS_ASSERT(i == 0);
  1078         return (TypeObjectKey *) objectSet;
  1080     return objectSet[i];
  1083 inline JSObject *
  1084 TypeSet::getSingleObject(unsigned i) const
  1086     TypeObjectKey *key = getObject(i);
  1087     return (key && key->isSingleObject()) ? key->asSingleObject() : nullptr;
  1090 inline TypeObject *
  1091 TypeSet::getTypeObject(unsigned i) const
  1093     TypeObjectKey *key = getObject(i);
  1094     return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr;
  1097 inline const Class *
  1098 TypeSet::getObjectClass(unsigned i) const
  1100     if (JSObject *object = getSingleObject(i))
  1101         return object->getClass();
  1102     if (TypeObject *object = getTypeObject(i))
  1103         return object->clasp();
  1104     return nullptr;
  1107 /////////////////////////////////////////////////////////////////////
  1108 // TypeObject
  1109 /////////////////////////////////////////////////////////////////////
  1111 inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags)
  1113     mozilla::PodZero(this);
  1115     /* Inner objects may not appear on prototype chains. */
  1116     JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
  1118     this->clasp_ = clasp;
  1119     this->proto_ = proto.raw();
  1120     this->flags_ = initialFlags;
  1122     InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
  1125 inline uint32_t
  1126 TypeObject::basePropertyCount() const
  1128     return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
  1131 inline void
  1132 TypeObject::setBasePropertyCount(uint32_t count)
  1134     // Note: Callers must ensure they are performing threadsafe operations.
  1135     JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
  1136     flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
  1137            | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
  1140 inline HeapTypeSet *
  1141 TypeObject::getProperty(ExclusiveContext *cx, jsid id)
  1143     JS_ASSERT(cx->compartment()->activeAnalysis);
  1145     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
  1146     JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
  1147     JS_ASSERT(!unknownProperties());
  1149     if (HeapTypeSet *types = maybeGetProperty(id))
  1150         return types;
  1152     Property *base = cx->typeLifoAlloc().new_<Property>(id);
  1153     if (!base) {
  1154         markUnknown(cx);
  1155         return nullptr;
  1158     uint32_t propertyCount = basePropertyCount();
  1159     Property **pprop = HashSetInsert<jsid,Property,Property>
  1160         (cx->typeLifoAlloc(), propertySet, propertyCount, id);
  1161     if (!pprop) {
  1162         markUnknown(cx);
  1163         return nullptr;
  1166     JS_ASSERT(!*pprop);
  1168     setBasePropertyCount(propertyCount);
  1169     *pprop = base;
  1171     updateNewPropertyTypes(cx, id, &base->types);
  1173     if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
  1174         // We hit the maximum number of properties the object can have, mark
  1175         // the object unknown so that new properties will not be added in the
  1176         // future.
  1177         markUnknown(cx);
  1180     return &base->types;
  1183 inline HeapTypeSet *
  1184 TypeObject::maybeGetProperty(jsid id)
  1186     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
  1187     JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
  1188     JS_ASSERT(!unknownProperties());
  1190     Property *prop = HashSetLookup<jsid,Property,Property>
  1191         (propertySet, basePropertyCount(), id);
  1193     return prop ? &prop->types : nullptr;
  1196 inline unsigned
  1197 TypeObject::getPropertyCount()
  1199     uint32_t count = basePropertyCount();
  1200     if (count > SET_ARRAY_SIZE)
  1201         return HashSetCapacity(count);
  1202     return count;
  1205 inline Property *
  1206 TypeObject::getProperty(unsigned i)
  1208     JS_ASSERT(i < getPropertyCount());
  1209     if (basePropertyCount() == 1) {
  1210         JS_ASSERT(i == 0);
  1211         return (Property *) propertySet;
  1213     return propertySet[i];
  1216 inline void
  1217 TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type)
  1219 #ifdef JSGC_INCREMENTAL
  1220     if (!type)
  1221         return;
  1223     switch (type->kind) {
  1224       case NewScript:
  1225         return TypeNewScript::writeBarrierPre(type->asNewScript());
  1227       case TypedObject:
  1228         return TypeTypedObject::writeBarrierPre(type->asTypedObject());
  1230 #endif
  1233 inline void
  1234 TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
  1236 #ifdef JSGC_INCREMENTAL
  1237     if (!newScript || !newScript->fun->runtimeFromAnyThread()->needsBarrier())
  1238         return;
  1240     JS::Zone *zone = newScript->fun->zoneFromAnyThread();
  1241     if (zone->needsBarrier()) {
  1242         MarkObject(zone->barrierTracer(), &newScript->fun, "write barrier");
  1243         MarkObject(zone->barrierTracer(), &newScript->templateObject, "write barrier");
  1245 #endif
  1248 } } /* namespace js::types */
  1250 inline bool
  1251 JSScript::ensureHasTypes(JSContext *cx)
  1253     return types || makeTypes(cx);
  1256 namespace js {
  1258 template <>
  1259 struct GCMethods<const types::Type>
  1261     static types::Type initial() { return types::Type::UnknownType(); }
  1262     static ThingRootKind kind() { return THING_ROOT_TYPE; }
  1263     static bool poisoned(const types::Type &v) {
  1264         return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
  1265             || (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));
  1267 };
  1269 template <>
  1270 struct GCMethods<types::Type>
  1272     static types::Type initial() { return types::Type::UnknownType(); }
  1273     static ThingRootKind kind() { return THING_ROOT_TYPE; }
  1274     static bool poisoned(const types::Type &v) {
  1275         return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
  1276             || (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));
  1278 };
  1280 } // namespace js
  1282 namespace JS {
  1283 template<> class AnchorPermitted<js::types::TypeObject *> { };
  1284 }  // namespace JS
  1286 #endif /* jsinferinlines_h */

mercurial