1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsinferinlines.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1286 @@ 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 +/* Inline members for javascript type inference. */ 1.11 + 1.12 +#ifndef jsinferinlines_h 1.13 +#define jsinferinlines_h 1.14 + 1.15 +#include "jsinfer.h" 1.16 + 1.17 +#include "mozilla/PodOperations.h" 1.18 + 1.19 +#include "jsanalyze.h" 1.20 + 1.21 +#include "vm/ArrayObject.h" 1.22 +#include "vm/BooleanObject.h" 1.23 +#include "vm/NumberObject.h" 1.24 +#include "vm/SharedArrayObject.h" 1.25 +#include "vm/StringObject.h" 1.26 +#include "vm/TypedArrayObject.h" 1.27 + 1.28 +#include "jscntxtinlines.h" 1.29 + 1.30 +#include "jit/ExecutionMode-inl.h" 1.31 + 1.32 +namespace js { 1.33 +namespace types { 1.34 + 1.35 +///////////////////////////////////////////////////////////////////// 1.36 +// CompilerOutput & RecompileInfo 1.37 +///////////////////////////////////////////////////////////////////// 1.38 + 1.39 +inline jit::IonScript * 1.40 +CompilerOutput::ion() const 1.41 +{ 1.42 +#ifdef JS_ION 1.43 + // Note: If type constraints are generated before compilation has finished 1.44 + // (i.e. after IonBuilder but before CodeGenerator::link) then a valid 1.45 + // CompilerOutput may not yet have an associated IonScript. 1.46 + JS_ASSERT(isValid()); 1.47 + jit::IonScript *ion = jit::GetIonScript(script(), mode()); 1.48 + JS_ASSERT(ion != ION_COMPILING_SCRIPT); 1.49 + return ion; 1.50 +#endif 1.51 + MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput"); 1.52 +} 1.53 + 1.54 +inline CompilerOutput* 1.55 +RecompileInfo::compilerOutput(TypeZone &types) const 1.56 +{ 1.57 + if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length()) 1.58 + return nullptr; 1.59 + return &(*types.compilerOutputs)[outputIndex]; 1.60 +} 1.61 + 1.62 +inline CompilerOutput* 1.63 +RecompileInfo::compilerOutput(JSContext *cx) const 1.64 +{ 1.65 + return compilerOutput(cx->zone()->types); 1.66 +} 1.67 + 1.68 +inline bool 1.69 +RecompileInfo::shouldSweep(TypeZone &types) 1.70 +{ 1.71 + CompilerOutput *output = compilerOutput(types); 1.72 + if (!output || !output->isValid()) 1.73 + return true; 1.74 + 1.75 + // Update this info for the output's new index in the zone's compiler outputs. 1.76 + outputIndex = output->sweepIndex(); 1.77 + return false; 1.78 +} 1.79 + 1.80 +///////////////////////////////////////////////////////////////////// 1.81 +// Types 1.82 +///////////////////////////////////////////////////////////////////// 1.83 + 1.84 +/* static */ inline Type 1.85 +Type::ObjectType(JSObject *obj) 1.86 +{ 1.87 + if (obj->hasSingletonType()) 1.88 + return Type(uintptr_t(obj) | 1); 1.89 + return Type(uintptr_t(obj->type())); 1.90 +} 1.91 + 1.92 +/* static */ inline Type 1.93 +Type::ObjectType(TypeObject *obj) 1.94 +{ 1.95 + if (obj->singleton()) 1.96 + return Type(uintptr_t(obj->singleton()) | 1); 1.97 + return Type(uintptr_t(obj)); 1.98 +} 1.99 + 1.100 +/* static */ inline Type 1.101 +Type::ObjectType(TypeObjectKey *obj) 1.102 +{ 1.103 + return Type(uintptr_t(obj)); 1.104 +} 1.105 + 1.106 +inline Type 1.107 +GetValueType(const Value &val) 1.108 +{ 1.109 + if (val.isDouble()) 1.110 + return Type::DoubleType(); 1.111 + if (val.isObject()) 1.112 + return Type::ObjectType(&val.toObject()); 1.113 + return Type::PrimitiveType(val.extractNonDoubleType()); 1.114 +} 1.115 + 1.116 +inline Type 1.117 +GetMaybeOptimizedOutValueType(const Value &val) 1.118 +{ 1.119 + if (val.isMagic() && val.whyMagic() == JS_OPTIMIZED_OUT) 1.120 + return Type::UnknownType(); 1.121 + return GetValueType(val); 1.122 +} 1.123 + 1.124 +inline TypeFlags 1.125 +PrimitiveTypeFlag(JSValueType type) 1.126 +{ 1.127 + switch (type) { 1.128 + case JSVAL_TYPE_UNDEFINED: 1.129 + return TYPE_FLAG_UNDEFINED; 1.130 + case JSVAL_TYPE_NULL: 1.131 + return TYPE_FLAG_NULL; 1.132 + case JSVAL_TYPE_BOOLEAN: 1.133 + return TYPE_FLAG_BOOLEAN; 1.134 + case JSVAL_TYPE_INT32: 1.135 + return TYPE_FLAG_INT32; 1.136 + case JSVAL_TYPE_DOUBLE: 1.137 + return TYPE_FLAG_DOUBLE; 1.138 + case JSVAL_TYPE_STRING: 1.139 + return TYPE_FLAG_STRING; 1.140 + case JSVAL_TYPE_MAGIC: 1.141 + return TYPE_FLAG_LAZYARGS; 1.142 + default: 1.143 + MOZ_ASSUME_UNREACHABLE("Bad type"); 1.144 + } 1.145 +} 1.146 + 1.147 +inline JSValueType 1.148 +TypeFlagPrimitive(TypeFlags flags) 1.149 +{ 1.150 + switch (flags) { 1.151 + case TYPE_FLAG_UNDEFINED: 1.152 + return JSVAL_TYPE_UNDEFINED; 1.153 + case TYPE_FLAG_NULL: 1.154 + return JSVAL_TYPE_NULL; 1.155 + case TYPE_FLAG_BOOLEAN: 1.156 + return JSVAL_TYPE_BOOLEAN; 1.157 + case TYPE_FLAG_INT32: 1.158 + return JSVAL_TYPE_INT32; 1.159 + case TYPE_FLAG_DOUBLE: 1.160 + return JSVAL_TYPE_DOUBLE; 1.161 + case TYPE_FLAG_STRING: 1.162 + return JSVAL_TYPE_STRING; 1.163 + case TYPE_FLAG_LAZYARGS: 1.164 + return JSVAL_TYPE_MAGIC; 1.165 + default: 1.166 + MOZ_ASSUME_UNREACHABLE("Bad type"); 1.167 + } 1.168 +} 1.169 + 1.170 +/* 1.171 + * Get the canonical representation of an id to use when doing inference. This 1.172 + * maintains the constraint that if two different jsids map to the same property 1.173 + * in JS (e.g. 3 and "3"), they have the same type representation. 1.174 + */ 1.175 +inline jsid 1.176 +IdToTypeId(jsid id) 1.177 +{ 1.178 + JS_ASSERT(!JSID_IS_EMPTY(id)); 1.179 + 1.180 + /* 1.181 + * All integers must map to the aggregate property for index types, including 1.182 + * negative integers. 1.183 + */ 1.184 + if (JSID_IS_INT(id)) 1.185 + return JSID_VOID; 1.186 + 1.187 + /* 1.188 + * Check for numeric strings, as in js_StringIsIndex, but allow negative 1.189 + * and overflowing integers. 1.190 + */ 1.191 + if (JSID_IS_STRING(id)) { 1.192 + JSAtom *atom = JSID_TO_ATOM(id); 1.193 + JS::TwoByteChars cp = atom->range(); 1.194 + if (cp.length() > 0 && (JS7_ISDEC(cp[0]) || cp[0] == '-')) { 1.195 + for (size_t i = 1; i < cp.length(); ++i) { 1.196 + if (!JS7_ISDEC(cp[i])) 1.197 + return id; 1.198 + } 1.199 + return JSID_VOID; 1.200 + } 1.201 + return id; 1.202 + } 1.203 + 1.204 + return JSID_VOID; 1.205 +} 1.206 + 1.207 +const char * TypeIdStringImpl(jsid id); 1.208 + 1.209 +/* Convert an id for printing during debug. */ 1.210 +static inline const char * 1.211 +TypeIdString(jsid id) 1.212 +{ 1.213 +#ifdef DEBUG 1.214 + return TypeIdStringImpl(id); 1.215 +#else 1.216 + return "(missing)"; 1.217 +#endif 1.218 +} 1.219 + 1.220 +/* 1.221 + * Structure for type inference entry point functions. All functions which can 1.222 + * change type information must use this, and functions which depend on 1.223 + * intermediate types (i.e. JITs) can use this to ensure that intermediate 1.224 + * information is not collected and does not change. 1.225 + * 1.226 + * Pins inference results so that intermediate type information, TypeObjects 1.227 + * and JSScripts won't be collected during GC. Does additional sanity checking 1.228 + * that inference is not reentrant and that recompilations occur properly. 1.229 + */ 1.230 +struct AutoEnterAnalysis 1.231 +{ 1.232 + /* Prevent GC activity in the middle of analysis. */ 1.233 + gc::AutoSuppressGC suppressGC; 1.234 + 1.235 + FreeOp *freeOp; 1.236 + JSCompartment *compartment; 1.237 + bool oldActiveAnalysis; 1.238 + 1.239 + AutoEnterAnalysis(ExclusiveContext *cx) 1.240 + : suppressGC(cx) 1.241 + { 1.242 + init(cx->defaultFreeOp(), cx->compartment()); 1.243 + } 1.244 + 1.245 + AutoEnterAnalysis(FreeOp *fop, JSCompartment *comp) 1.246 + : suppressGC(comp) 1.247 + { 1.248 + init(fop, comp); 1.249 + } 1.250 + 1.251 + ~AutoEnterAnalysis() 1.252 + { 1.253 + compartment->activeAnalysis = oldActiveAnalysis; 1.254 + 1.255 + /* 1.256 + * If there are no more type inference activations on the stack, 1.257 + * process any triggered recompilations. Note that we should not be 1.258 + * invoking any scripted code while type inference is running. 1.259 + */ 1.260 + if (!compartment->activeAnalysis) { 1.261 + TypeZone &types = compartment->zone()->types; 1.262 + if (types.pendingRecompiles) 1.263 + types.processPendingRecompiles(freeOp); 1.264 + } 1.265 + } 1.266 + 1.267 + private: 1.268 + void init(FreeOp *fop, JSCompartment *comp) { 1.269 + freeOp = fop; 1.270 + compartment = comp; 1.271 + oldActiveAnalysis = compartment->activeAnalysis; 1.272 + compartment->activeAnalysis = true; 1.273 + } 1.274 +}; 1.275 + 1.276 +///////////////////////////////////////////////////////////////////// 1.277 +// Interface functions 1.278 +///////////////////////////////////////////////////////////////////// 1.279 + 1.280 +inline const Class * 1.281 +GetClassForProtoKey(JSProtoKey key) 1.282 +{ 1.283 + switch (key) { 1.284 + case JSProto_Object: 1.285 + return &JSObject::class_; 1.286 + case JSProto_Array: 1.287 + return &ArrayObject::class_; 1.288 + 1.289 + case JSProto_Number: 1.290 + return &NumberObject::class_; 1.291 + case JSProto_Boolean: 1.292 + return &BooleanObject::class_; 1.293 + case JSProto_String: 1.294 + return &StringObject::class_; 1.295 + case JSProto_RegExp: 1.296 + return &RegExpObject::class_; 1.297 + 1.298 + case JSProto_Int8Array: 1.299 + case JSProto_Uint8Array: 1.300 + case JSProto_Int16Array: 1.301 + case JSProto_Uint16Array: 1.302 + case JSProto_Int32Array: 1.303 + case JSProto_Uint32Array: 1.304 + case JSProto_Float32Array: 1.305 + case JSProto_Float64Array: 1.306 + case JSProto_Uint8ClampedArray: 1.307 + return &TypedArrayObject::classes[key - JSProto_Int8Array]; 1.308 + 1.309 + case JSProto_ArrayBuffer: 1.310 + return &ArrayBufferObject::class_; 1.311 + 1.312 + case JSProto_SharedArrayBuffer: 1.313 + return &SharedArrayBufferObject::class_; 1.314 + 1.315 + case JSProto_DataView: 1.316 + return &DataViewObject::class_; 1.317 + 1.318 + default: 1.319 + MOZ_ASSUME_UNREACHABLE("Bad proto key"); 1.320 + } 1.321 +} 1.322 + 1.323 +/* 1.324 + * Get the default 'new' object for a given standard class, per the currently 1.325 + * active global. 1.326 + */ 1.327 +inline TypeObject * 1.328 +GetTypeNewObject(JSContext *cx, JSProtoKey key) 1.329 +{ 1.330 + RootedObject proto(cx); 1.331 + if (!GetBuiltinPrototype(cx, key, &proto)) 1.332 + return nullptr; 1.333 + return cx->getNewType(GetClassForProtoKey(key), proto.get()); 1.334 +} 1.335 + 1.336 +/* Get a type object for the immediate allocation site within a native. */ 1.337 +inline TypeObject * 1.338 +GetTypeCallerInitObject(JSContext *cx, JSProtoKey key) 1.339 +{ 1.340 + jsbytecode *pc; 1.341 + RootedScript script(cx, cx->currentScript(&pc)); 1.342 + if (script) 1.343 + return TypeScript::InitObject(cx, script, pc, key); 1.344 + return GetTypeNewObject(cx, key); 1.345 +} 1.346 + 1.347 +void MarkIteratorUnknownSlow(JSContext *cx); 1.348 + 1.349 +void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args, 1.350 + bool constructing); 1.351 + 1.352 +/* 1.353 + * Monitor a javascript call, either on entry to the interpreter or made 1.354 + * from within the interpreter. 1.355 + */ 1.356 +inline void 1.357 +TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing) 1.358 +{ 1.359 + if (args.callee().is<JSFunction>()) { 1.360 + JSFunction *fun = &args.callee().as<JSFunction>(); 1.361 + if (fun->isInterpreted() && fun->nonLazyScript()->types) 1.362 + TypeMonitorCallSlow(cx, &args.callee(), args, constructing); 1.363 + } 1.364 +} 1.365 + 1.366 +inline bool 1.367 +TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id) 1.368 +{ 1.369 + if (obj->hasLazyType() || obj->type()->unknownProperties()) 1.370 + return false; 1.371 + 1.372 + if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id)) 1.373 + return false; 1.374 + 1.375 + return true; 1.376 +} 1.377 + 1.378 +inline void 1.379 +EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id) 1.380 +{ 1.381 + id = IdToTypeId(id); 1.382 + 1.383 + if (obj->hasSingletonType()) { 1.384 + AutoEnterAnalysis enter(cx); 1.385 + if (obj->hasLazyType() && !obj->getType(cx)) { 1.386 + CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes"); 1.387 + return; 1.388 + } 1.389 + if (!obj->type()->unknownProperties() && !obj->type()->getProperty(cx, id)) { 1.390 + MOZ_ASSERT(obj->type()->unknownProperties()); 1.391 + return; 1.392 + } 1.393 + } 1.394 + 1.395 + JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id)); 1.396 +} 1.397 + 1.398 +inline bool 1.399 +CanHaveEmptyPropertyTypesForOwnProperty(JSObject *obj) 1.400 +{ 1.401 + // Per the comment on TypeSet::propertySet, property type sets for global 1.402 + // objects may be empty for 'own' properties if the global property still 1.403 + // has its initial undefined value. 1.404 + return obj->is<GlobalObject>(); 1.405 +} 1.406 + 1.407 +inline bool 1.408 +HasTypePropertyId(JSObject *obj, jsid id, Type type) 1.409 +{ 1.410 + if (obj->hasLazyType()) 1.411 + return true; 1.412 + 1.413 + if (obj->type()->unknownProperties()) 1.414 + return true; 1.415 + 1.416 + if (HeapTypeSet *types = obj->type()->maybeGetProperty(IdToTypeId(id))) 1.417 + return types->hasType(type); 1.418 + 1.419 + return false; 1.420 +} 1.421 + 1.422 +inline bool 1.423 +HasTypePropertyId(JSObject *obj, jsid id, const Value &value) 1.424 +{ 1.425 + return HasTypePropertyId(obj, id, GetValueType(value)); 1.426 +} 1.427 + 1.428 +/* Add a possible type for a property of obj. */ 1.429 +inline void 1.430 +AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, Type type) 1.431 +{ 1.432 + id = IdToTypeId(id); 1.433 + if (TrackPropertyTypes(cx, obj, id)) 1.434 + obj->type()->addPropertyType(cx, id, type); 1.435 +} 1.436 + 1.437 +inline void 1.438 +AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, const Value &value) 1.439 +{ 1.440 + id = IdToTypeId(id); 1.441 + if (TrackPropertyTypes(cx, obj, id)) 1.442 + obj->type()->addPropertyType(cx, id, value); 1.443 +} 1.444 + 1.445 +inline void 1.446 +AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type) 1.447 +{ 1.448 + if (!obj->unknownProperties()) 1.449 + obj->addPropertyType(cx, id, type); 1.450 +} 1.451 + 1.452 +inline void 1.453 +AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, const Value &value) 1.454 +{ 1.455 + if (!obj->unknownProperties()) 1.456 + obj->addPropertyType(cx, id, value); 1.457 +} 1.458 + 1.459 +/* Set one or more dynamic flags on a type object. */ 1.460 +inline void 1.461 +MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags) 1.462 +{ 1.463 + if (!obj->hasLazyType() && !obj->type()->hasAllFlags(flags)) 1.464 + obj->type()->setFlags(cx, flags); 1.465 +} 1.466 + 1.467 +/* 1.468 + * Mark all properties of a type object as unknown. If markSetsUnknown is set, 1.469 + * scan the entire compartment and mark all type sets containing it as having 1.470 + * an unknown object. This is needed for correctness in dealing with mutable 1.471 + * __proto__, which can change the type of an object dynamically. 1.472 + */ 1.473 +inline void 1.474 +MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj, 1.475 + bool markSetsUnknown = false) 1.476 +{ 1.477 + if (!obj->unknownProperties()) 1.478 + obj->markUnknown(cx); 1.479 + if (markSetsUnknown && !(obj->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN)) 1.480 + cx->compartment()->types.markSetsUnknown(cx, obj); 1.481 +} 1.482 + 1.483 +inline void 1.484 +MarkTypePropertyNonData(ExclusiveContext *cx, JSObject *obj, jsid id) 1.485 +{ 1.486 + id = IdToTypeId(id); 1.487 + if (TrackPropertyTypes(cx, obj, id)) 1.488 + obj->type()->markPropertyNonData(cx, id); 1.489 +} 1.490 + 1.491 +inline void 1.492 +MarkTypePropertyNonWritable(ExclusiveContext *cx, JSObject *obj, jsid id) 1.493 +{ 1.494 + id = IdToTypeId(id); 1.495 + if (TrackPropertyTypes(cx, obj, id)) 1.496 + obj->type()->markPropertyNonWritable(cx, id); 1.497 +} 1.498 + 1.499 +inline bool 1.500 +IsTypePropertyIdMarkedNonData(JSObject *obj, jsid id) 1.501 +{ 1.502 + return obj->type()->isPropertyNonData(id); 1.503 +} 1.504 + 1.505 +inline bool 1.506 +IsTypePropertyIdMarkedNonWritable(JSObject *obj, jsid id) 1.507 +{ 1.508 + return obj->type()->isPropertyNonWritable(id); 1.509 +} 1.510 + 1.511 +/* Mark a state change on a particular object. */ 1.512 +inline void 1.513 +MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj) 1.514 +{ 1.515 + if (!obj->hasLazyType() && !obj->type()->unknownProperties()) 1.516 + obj->type()->markStateChange(cx); 1.517 +} 1.518 + 1.519 +/* 1.520 + * For an array or object which has not yet escaped and been referenced elsewhere, 1.521 + * pick a new type based on the object's current contents. 1.522 + */ 1.523 + 1.524 +inline void 1.525 +FixArrayType(ExclusiveContext *cx, HandleObject obj) 1.526 +{ 1.527 + cx->compartment()->types.fixArrayType(cx, obj); 1.528 +} 1.529 + 1.530 +inline void 1.531 +FixObjectType(ExclusiveContext *cx, HandleObject obj) 1.532 +{ 1.533 + cx->compartment()->types.fixObjectType(cx, obj); 1.534 +} 1.535 + 1.536 +/* Interface helpers for JSScript*. */ 1.537 +extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, 1.538 + const js::Value &rval); 1.539 +extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, 1.540 + js::types::Type type); 1.541 + 1.542 +///////////////////////////////////////////////////////////////////// 1.543 +// Script interface functions 1.544 +///////////////////////////////////////////////////////////////////// 1.545 + 1.546 +/* static */ inline unsigned 1.547 +TypeScript::NumTypeSets(JSScript *script) 1.548 +{ 1.549 + return script->nTypeSets() + analyze::LocalSlot(script, 0); 1.550 +} 1.551 + 1.552 +/* static */ inline StackTypeSet * 1.553 +TypeScript::ThisTypes(JSScript *script) 1.554 +{ 1.555 + return script->types->typeArray() + script->nTypeSets() + analyze::ThisSlot(); 1.556 +} 1.557 + 1.558 +/* 1.559 + * Note: for non-escaping arguments and locals, argTypes/localTypes reflect 1.560 + * only the initial type of the variable (e.g. passed values for argTypes, 1.561 + * or undefined for localTypes) and not types from subsequent assignments. 1.562 + */ 1.563 + 1.564 +/* static */ inline StackTypeSet * 1.565 +TypeScript::ArgTypes(JSScript *script, unsigned i) 1.566 +{ 1.567 + JS_ASSERT(i < script->functionNonDelazifying()->nargs()); 1.568 + return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i); 1.569 +} 1.570 + 1.571 +template <typename TYPESET> 1.572 +/* static */ inline TYPESET * 1.573 +TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap, 1.574 + uint32_t *hint, TYPESET *typeArray) 1.575 +{ 1.576 + JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET); 1.577 + uint32_t offset = script->pcToOffset(pc); 1.578 + 1.579 + // See if this pc is the next typeset opcode after the last one looked up. 1.580 + if ((*hint + 1) < script->nTypeSets() && bytecodeMap[*hint + 1] == offset) { 1.581 + (*hint)++; 1.582 + return typeArray + *hint; 1.583 + } 1.584 + 1.585 + // See if this pc is the same as the last one looked up. 1.586 + if (bytecodeMap[*hint] == offset) 1.587 + return typeArray + *hint; 1.588 + 1.589 + // Fall back to a binary search. 1.590 + size_t bottom = 0; 1.591 + size_t top = script->nTypeSets() - 1; 1.592 + size_t mid = bottom + (top - bottom) / 2; 1.593 + while (mid < top) { 1.594 + if (bytecodeMap[mid] < offset) 1.595 + bottom = mid + 1; 1.596 + else if (bytecodeMap[mid] > offset) 1.597 + top = mid; 1.598 + else 1.599 + break; 1.600 + mid = bottom + (top - bottom) / 2; 1.601 + } 1.602 + 1.603 + // We should have have zeroed in on either the exact offset, unless there 1.604 + // are more JOF_TYPESET opcodes than nTypeSets in the script (as can happen 1.605 + // if the script is very long). 1.606 + JS_ASSERT(bytecodeMap[mid] == offset || mid == top); 1.607 + 1.608 + *hint = mid; 1.609 + return typeArray + *hint; 1.610 +} 1.611 + 1.612 +/* static */ inline StackTypeSet * 1.613 +TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc) 1.614 +{ 1.615 + JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread())); 1.616 +#ifdef JS_ION 1.617 + uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets(); 1.618 + return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(), 1.619 + hint, script->types->typeArray()); 1.620 +#else 1.621 + MOZ_CRASH(); 1.622 +#endif 1.623 +} 1.624 + 1.625 +struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> { 1.626 + JSScript *script; 1.627 + 1.628 + uint32_t offset : 24; 1.629 + JSProtoKey kind : 8; 1.630 + 1.631 + static const uint32_t OFFSET_LIMIT = (1 << 23); 1.632 + 1.633 + AllocationSiteKey() { mozilla::PodZero(this); } 1.634 + 1.635 + static inline uint32_t hash(AllocationSiteKey key) { 1.636 + return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); 1.637 + } 1.638 + 1.639 + static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) { 1.640 + return a.script == b.script && a.offset == b.offset && a.kind == b.kind; 1.641 + } 1.642 +}; 1.643 + 1.644 +/* Whether to use a new type object for an initializer opcode at script/pc. */ 1.645 +js::NewObjectKind 1.646 +UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key); 1.647 + 1.648 +js::NewObjectKind 1.649 +UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp); 1.650 + 1.651 +/* static */ inline TypeObject * 1.652 +TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind) 1.653 +{ 1.654 + JS_ASSERT(!UseNewTypeForInitializer(script, pc, kind)); 1.655 + 1.656 + /* :XXX: Limit script->length so we don't need to check the offset up front? */ 1.657 + uint32_t offset = script->pcToOffset(pc); 1.658 + 1.659 + if (!script->compileAndGo() || offset >= AllocationSiteKey::OFFSET_LIMIT) 1.660 + return GetTypeNewObject(cx, kind); 1.661 + 1.662 + AllocationSiteKey key; 1.663 + key.script = script; 1.664 + key.offset = offset; 1.665 + key.kind = kind; 1.666 + 1.667 + if (!cx->compartment()->types.allocationSiteTable) 1.668 + return cx->compartment()->types.addAllocationSiteTypeObject(cx, key); 1.669 + 1.670 + AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key); 1.671 + 1.672 + if (p) 1.673 + return p->value(); 1.674 + return cx->compartment()->types.addAllocationSiteTypeObject(cx, key); 1.675 +} 1.676 + 1.677 +/* Set the type to use for obj according to the site it was allocated at. */ 1.678 +static inline bool 1.679 +SetInitializerObjectType(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, NewObjectKind kind) 1.680 +{ 1.681 + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); 1.682 + JS_ASSERT(key != JSProto_Null); 1.683 + JS_ASSERT(kind == UseNewTypeForInitializer(script, pc, key)); 1.684 + 1.685 + if (kind == SingletonObject) { 1.686 + JS_ASSERT(obj->hasSingletonType()); 1.687 + 1.688 + /* 1.689 + * Inference does not account for types of run-once initializer 1.690 + * objects, as these may not be created until after the script 1.691 + * has been analyzed. 1.692 + */ 1.693 + TypeScript::Monitor(cx, script, pc, ObjectValue(*obj)); 1.694 + } else { 1.695 + types::TypeObject *type = TypeScript::InitObject(cx, script, pc, key); 1.696 + if (!type) 1.697 + return false; 1.698 + obj->uninlinedSetType(type); 1.699 + } 1.700 + 1.701 + return true; 1.702 +} 1.703 + 1.704 +/* static */ inline void 1.705 +TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) 1.706 +{ 1.707 + TypeMonitorResult(cx, script, pc, rval); 1.708 +} 1.709 + 1.710 +/* static */ inline void 1.711 +TypeScript::Monitor(JSContext *cx, const js::Value &rval) 1.712 +{ 1.713 + jsbytecode *pc; 1.714 + RootedScript script(cx, cx->currentScript(&pc)); 1.715 + Monitor(cx, script, pc, rval); 1.716 +} 1.717 + 1.718 +/* static */ inline void 1.719 +TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id) 1.720 +{ 1.721 + if (!obj->hasSingletonType()) { 1.722 + /* 1.723 + * Mark as unknown any object which has had dynamic assignments to 1.724 + * non-integer properties at SETELEM opcodes. This avoids making large 1.725 + * numbers of type properties for hashmap-style objects. We don't need 1.726 + * to do this for objects with singleton type, because type properties 1.727 + * are only constructed for them when analyzed scripts depend on those 1.728 + * specific properties. 1.729 + */ 1.730 + uint32_t i; 1.731 + if (js_IdIsIndex(id, &i)) 1.732 + return; 1.733 + 1.734 + // But if we don't have too many properties yet, don't do anything. The 1.735 + // idea here is that normal object initialization should not trigger 1.736 + // deoptimization in most cases, while actual usage as a hashmap should. 1.737 + TypeObject* type = obj->type(); 1.738 + if (type->getPropertyCount() < 8) 1.739 + return; 1.740 + MarkTypeObjectUnknownProperties(cx, type); 1.741 + } 1.742 +} 1.743 + 1.744 +/* static */ inline void 1.745 +TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) 1.746 +{ 1.747 + if (!script->types) 1.748 + return; 1.749 + 1.750 + if (!ThisTypes(script)->hasType(type)) { 1.751 + AutoEnterAnalysis enter(cx); 1.752 + 1.753 + InferSpew(ISpewOps, "externalType: setThis #%u: %s", 1.754 + script->id(), TypeString(type)); 1.755 + ThisTypes(script)->addType(cx, type); 1.756 + } 1.757 +} 1.758 + 1.759 +/* static */ inline void 1.760 +TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value) 1.761 +{ 1.762 + SetThis(cx, script, GetValueType(value)); 1.763 +} 1.764 + 1.765 +/* static */ inline void 1.766 +TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type) 1.767 +{ 1.768 + if (!script->types) 1.769 + return; 1.770 + 1.771 + if (!ArgTypes(script, arg)->hasType(type)) { 1.772 + AutoEnterAnalysis enter(cx); 1.773 + 1.774 + InferSpew(ISpewOps, "externalType: setArg #%u %u: %s", 1.775 + script->id(), arg, TypeString(type)); 1.776 + ArgTypes(script, arg)->addType(cx, type); 1.777 + } 1.778 +} 1.779 + 1.780 +/* static */ inline void 1.781 +TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value) 1.782 +{ 1.783 + Type type = GetValueType(value); 1.784 + SetArgument(cx, script, arg, type); 1.785 +} 1.786 + 1.787 +///////////////////////////////////////////////////////////////////// 1.788 +// TypeCompartment 1.789 +///////////////////////////////////////////////////////////////////// 1.790 + 1.791 +inline JSCompartment * 1.792 +TypeCompartment::compartment() 1.793 +{ 1.794 + return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); 1.795 +} 1.796 + 1.797 +///////////////////////////////////////////////////////////////////// 1.798 +// TypeSet 1.799 +///////////////////////////////////////////////////////////////////// 1.800 + 1.801 +/* 1.802 + * The sets of objects and scripts in a type set grow monotonically, are usually 1.803 + * empty, almost always small, and sometimes big. For empty or singleton sets, 1.804 + * the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE, 1.805 + * an array of this length is used to store the elements. For larger sets, a hash 1.806 + * table filled to 25%-50% of capacity is used, with collisions resolved by linear 1.807 + * probing. TODO: replace these with jshashtables. 1.808 + */ 1.809 +const unsigned SET_ARRAY_SIZE = 8; 1.810 +const unsigned SET_CAPACITY_OVERFLOW = 1u << 30; 1.811 + 1.812 +/* Get the capacity of a set with the given element count. */ 1.813 +static inline unsigned 1.814 +HashSetCapacity(unsigned count) 1.815 +{ 1.816 + JS_ASSERT(count >= 2); 1.817 + JS_ASSERT(count < SET_CAPACITY_OVERFLOW); 1.818 + 1.819 + if (count <= SET_ARRAY_SIZE) 1.820 + return SET_ARRAY_SIZE; 1.821 + 1.822 + return 1u << (mozilla::FloorLog2(count) + 2); 1.823 +} 1.824 + 1.825 +/* Compute the FNV hash for the low 32 bits of v. */ 1.826 +template <class T, class KEY> 1.827 +static inline uint32_t 1.828 +HashKey(T v) 1.829 +{ 1.830 + uint32_t nv = KEY::keyBits(v); 1.831 + 1.832 + uint32_t hash = 84696351 ^ (nv & 0xff); 1.833 + hash = (hash * 16777619) ^ ((nv >> 8) & 0xff); 1.834 + hash = (hash * 16777619) ^ ((nv >> 16) & 0xff); 1.835 + return (hash * 16777619) ^ ((nv >> 24) & 0xff); 1.836 +} 1.837 + 1.838 +/* 1.839 + * Insert space for an element into the specified set and grow its capacity if needed. 1.840 + * returned value is an existing or new entry (nullptr if new). 1.841 + */ 1.842 +template <class T, class U, class KEY> 1.843 +static U ** 1.844 +HashSetInsertTry(LifoAlloc &alloc, U **&values, unsigned &count, T key) 1.845 +{ 1.846 + unsigned capacity = HashSetCapacity(count); 1.847 + unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1); 1.848 + 1.849 + /* Whether we are converting from a fixed array to hashtable. */ 1.850 + bool converting = (count == SET_ARRAY_SIZE); 1.851 + 1.852 + if (!converting) { 1.853 + while (values[insertpos] != nullptr) { 1.854 + if (KEY::getKey(values[insertpos]) == key) 1.855 + return &values[insertpos]; 1.856 + insertpos = (insertpos + 1) & (capacity - 1); 1.857 + } 1.858 + } 1.859 + 1.860 + if (count >= SET_CAPACITY_OVERFLOW) 1.861 + return nullptr; 1.862 + 1.863 + count++; 1.864 + unsigned newCapacity = HashSetCapacity(count); 1.865 + 1.866 + if (newCapacity == capacity) { 1.867 + JS_ASSERT(!converting); 1.868 + return &values[insertpos]; 1.869 + } 1.870 + 1.871 + U **newValues = alloc.newArray<U*>(newCapacity); 1.872 + if (!newValues) 1.873 + return nullptr; 1.874 + mozilla::PodZero(newValues, newCapacity); 1.875 + 1.876 + for (unsigned i = 0; i < capacity; i++) { 1.877 + if (values[i]) { 1.878 + unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1); 1.879 + while (newValues[pos] != nullptr) 1.880 + pos = (pos + 1) & (newCapacity - 1); 1.881 + newValues[pos] = values[i]; 1.882 + } 1.883 + } 1.884 + 1.885 + values = newValues; 1.886 + 1.887 + insertpos = HashKey<T,KEY>(key) & (newCapacity - 1); 1.888 + while (values[insertpos] != nullptr) 1.889 + insertpos = (insertpos + 1) & (newCapacity - 1); 1.890 + return &values[insertpos]; 1.891 +} 1.892 + 1.893 +/* 1.894 + * Insert an element into the specified set if it is not already there, returning 1.895 + * an entry which is nullptr if the element was not there. 1.896 + */ 1.897 +template <class T, class U, class KEY> 1.898 +static inline U ** 1.899 +HashSetInsert(LifoAlloc &alloc, U **&values, unsigned &count, T key) 1.900 +{ 1.901 + if (count == 0) { 1.902 + JS_ASSERT(values == nullptr); 1.903 + count++; 1.904 + return (U **) &values; 1.905 + } 1.906 + 1.907 + if (count == 1) { 1.908 + U *oldData = (U*) values; 1.909 + if (KEY::getKey(oldData) == key) 1.910 + return (U **) &values; 1.911 + 1.912 + values = alloc.newArray<U*>(SET_ARRAY_SIZE); 1.913 + if (!values) { 1.914 + values = (U **) oldData; 1.915 + return nullptr; 1.916 + } 1.917 + mozilla::PodZero(values, SET_ARRAY_SIZE); 1.918 + count++; 1.919 + 1.920 + values[0] = oldData; 1.921 + return &values[1]; 1.922 + } 1.923 + 1.924 + if (count <= SET_ARRAY_SIZE) { 1.925 + for (unsigned i = 0; i < count; i++) { 1.926 + if (KEY::getKey(values[i]) == key) 1.927 + return &values[i]; 1.928 + } 1.929 + 1.930 + if (count < SET_ARRAY_SIZE) { 1.931 + count++; 1.932 + return &values[count - 1]; 1.933 + } 1.934 + } 1.935 + 1.936 + return HashSetInsertTry<T,U,KEY>(alloc, values, count, key); 1.937 +} 1.938 + 1.939 +/* Lookup an entry in a hash set, return nullptr if it does not exist. */ 1.940 +template <class T, class U, class KEY> 1.941 +static inline U * 1.942 +HashSetLookup(U **values, unsigned count, T key) 1.943 +{ 1.944 + if (count == 0) 1.945 + return nullptr; 1.946 + 1.947 + if (count == 1) 1.948 + return (KEY::getKey((U *) values) == key) ? (U *) values : nullptr; 1.949 + 1.950 + if (count <= SET_ARRAY_SIZE) { 1.951 + for (unsigned i = 0; i < count; i++) { 1.952 + if (KEY::getKey(values[i]) == key) 1.953 + return values[i]; 1.954 + } 1.955 + return nullptr; 1.956 + } 1.957 + 1.958 + unsigned capacity = HashSetCapacity(count); 1.959 + unsigned pos = HashKey<T,KEY>(key) & (capacity - 1); 1.960 + 1.961 + while (values[pos] != nullptr) { 1.962 + if (KEY::getKey(values[pos]) == key) 1.963 + return values[pos]; 1.964 + pos = (pos + 1) & (capacity - 1); 1.965 + } 1.966 + 1.967 + return nullptr; 1.968 +} 1.969 + 1.970 +inline TypeObjectKey * 1.971 +Type::objectKey() const 1.972 +{ 1.973 + JS_ASSERT(isObject()); 1.974 + if (isTypeObject()) 1.975 + TypeObject::readBarrier((TypeObject *) data); 1.976 + else 1.977 + JSObject::readBarrier((JSObject *) (data ^ 1)); 1.978 + return (TypeObjectKey *) data; 1.979 +} 1.980 + 1.981 +inline JSObject * 1.982 +Type::singleObject() const 1.983 +{ 1.984 + JS_ASSERT(isSingleObject()); 1.985 + JSObject::readBarrier((JSObject *) (data ^ 1)); 1.986 + return (JSObject *) (data ^ 1); 1.987 +} 1.988 + 1.989 +inline TypeObject * 1.990 +Type::typeObject() const 1.991 +{ 1.992 + JS_ASSERT(isTypeObject()); 1.993 + TypeObject::readBarrier((TypeObject *) data); 1.994 + return (TypeObject *) data; 1.995 +} 1.996 + 1.997 +inline bool 1.998 +TypeSet::hasType(Type type) const 1.999 +{ 1.1000 + if (unknown()) 1.1001 + return true; 1.1002 + 1.1003 + if (type.isUnknown()) { 1.1004 + return false; 1.1005 + } else if (type.isPrimitive()) { 1.1006 + return !!(flags & PrimitiveTypeFlag(type.primitive())); 1.1007 + } else if (type.isAnyObject()) { 1.1008 + return !!(flags & TYPE_FLAG_ANYOBJECT); 1.1009 + } else { 1.1010 + return !!(flags & TYPE_FLAG_ANYOBJECT) || 1.1011 + HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey> 1.1012 + (objectSet, baseObjectCount(), type.objectKey()) != nullptr; 1.1013 + } 1.1014 +} 1.1015 + 1.1016 +inline void 1.1017 +TypeSet::setBaseObjectCount(uint32_t count) 1.1018 +{ 1.1019 + JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT); 1.1020 + flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK) 1.1021 + | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT); 1.1022 +} 1.1023 + 1.1024 +inline void 1.1025 +HeapTypeSet::newPropertyState(ExclusiveContext *cxArg) 1.1026 +{ 1.1027 + /* Propagate the change to all constraints. */ 1.1028 + if (JSContext *cx = cxArg->maybeJSContext()) { 1.1029 + TypeConstraint *constraint = constraintList; 1.1030 + while (constraint) { 1.1031 + constraint->newPropertyState(cx, this); 1.1032 + constraint = constraint->next; 1.1033 + } 1.1034 + } else { 1.1035 + JS_ASSERT(!constraintList); 1.1036 + } 1.1037 +} 1.1038 + 1.1039 +inline void 1.1040 +HeapTypeSet::setNonDataPropertyIgnoringConstraints() 1.1041 +{ 1.1042 + flags |= TYPE_FLAG_NON_DATA_PROPERTY; 1.1043 +} 1.1044 + 1.1045 +inline void 1.1046 +HeapTypeSet::setNonDataProperty(ExclusiveContext *cx) 1.1047 +{ 1.1048 + if (flags & TYPE_FLAG_NON_DATA_PROPERTY) 1.1049 + return; 1.1050 + 1.1051 + setNonDataPropertyIgnoringConstraints(); 1.1052 + newPropertyState(cx); 1.1053 +} 1.1054 + 1.1055 +inline void 1.1056 +HeapTypeSet::setNonWritableProperty(ExclusiveContext *cx) 1.1057 +{ 1.1058 + if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY) 1.1059 + return; 1.1060 + 1.1061 + flags |= TYPE_FLAG_NON_WRITABLE_PROPERTY; 1.1062 + newPropertyState(cx); 1.1063 +} 1.1064 + 1.1065 +inline unsigned 1.1066 +TypeSet::getObjectCount() const 1.1067 +{ 1.1068 + JS_ASSERT(!unknownObject()); 1.1069 + uint32_t count = baseObjectCount(); 1.1070 + if (count > SET_ARRAY_SIZE) 1.1071 + return HashSetCapacity(count); 1.1072 + return count; 1.1073 +} 1.1074 + 1.1075 +inline TypeObjectKey * 1.1076 +TypeSet::getObject(unsigned i) const 1.1077 +{ 1.1078 + JS_ASSERT(i < getObjectCount()); 1.1079 + if (baseObjectCount() == 1) { 1.1080 + JS_ASSERT(i == 0); 1.1081 + return (TypeObjectKey *) objectSet; 1.1082 + } 1.1083 + return objectSet[i]; 1.1084 +} 1.1085 + 1.1086 +inline JSObject * 1.1087 +TypeSet::getSingleObject(unsigned i) const 1.1088 +{ 1.1089 + TypeObjectKey *key = getObject(i); 1.1090 + return (key && key->isSingleObject()) ? key->asSingleObject() : nullptr; 1.1091 +} 1.1092 + 1.1093 +inline TypeObject * 1.1094 +TypeSet::getTypeObject(unsigned i) const 1.1095 +{ 1.1096 + TypeObjectKey *key = getObject(i); 1.1097 + return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr; 1.1098 +} 1.1099 + 1.1100 +inline const Class * 1.1101 +TypeSet::getObjectClass(unsigned i) const 1.1102 +{ 1.1103 + if (JSObject *object = getSingleObject(i)) 1.1104 + return object->getClass(); 1.1105 + if (TypeObject *object = getTypeObject(i)) 1.1106 + return object->clasp(); 1.1107 + return nullptr; 1.1108 +} 1.1109 + 1.1110 +///////////////////////////////////////////////////////////////////// 1.1111 +// TypeObject 1.1112 +///////////////////////////////////////////////////////////////////// 1.1113 + 1.1114 +inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags) 1.1115 +{ 1.1116 + mozilla::PodZero(this); 1.1117 + 1.1118 + /* Inner objects may not appear on prototype chains. */ 1.1119 + JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); 1.1120 + 1.1121 + this->clasp_ = clasp; 1.1122 + this->proto_ = proto.raw(); 1.1123 + this->flags_ = initialFlags; 1.1124 + 1.1125 + InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this)); 1.1126 +} 1.1127 + 1.1128 +inline uint32_t 1.1129 +TypeObject::basePropertyCount() const 1.1130 +{ 1.1131 + return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; 1.1132 +} 1.1133 + 1.1134 +inline void 1.1135 +TypeObject::setBasePropertyCount(uint32_t count) 1.1136 +{ 1.1137 + // Note: Callers must ensure they are performing threadsafe operations. 1.1138 + JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); 1.1139 + flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) 1.1140 + | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); 1.1141 +} 1.1142 + 1.1143 +inline HeapTypeSet * 1.1144 +TypeObject::getProperty(ExclusiveContext *cx, jsid id) 1.1145 +{ 1.1146 + JS_ASSERT(cx->compartment()->activeAnalysis); 1.1147 + 1.1148 + JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); 1.1149 + JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); 1.1150 + JS_ASSERT(!unknownProperties()); 1.1151 + 1.1152 + if (HeapTypeSet *types = maybeGetProperty(id)) 1.1153 + return types; 1.1154 + 1.1155 + Property *base = cx->typeLifoAlloc().new_<Property>(id); 1.1156 + if (!base) { 1.1157 + markUnknown(cx); 1.1158 + return nullptr; 1.1159 + } 1.1160 + 1.1161 + uint32_t propertyCount = basePropertyCount(); 1.1162 + Property **pprop = HashSetInsert<jsid,Property,Property> 1.1163 + (cx->typeLifoAlloc(), propertySet, propertyCount, id); 1.1164 + if (!pprop) { 1.1165 + markUnknown(cx); 1.1166 + return nullptr; 1.1167 + } 1.1168 + 1.1169 + JS_ASSERT(!*pprop); 1.1170 + 1.1171 + setBasePropertyCount(propertyCount); 1.1172 + *pprop = base; 1.1173 + 1.1174 + updateNewPropertyTypes(cx, id, &base->types); 1.1175 + 1.1176 + if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) { 1.1177 + // We hit the maximum number of properties the object can have, mark 1.1178 + // the object unknown so that new properties will not be added in the 1.1179 + // future. 1.1180 + markUnknown(cx); 1.1181 + } 1.1182 + 1.1183 + return &base->types; 1.1184 +} 1.1185 + 1.1186 +inline HeapTypeSet * 1.1187 +TypeObject::maybeGetProperty(jsid id) 1.1188 +{ 1.1189 + JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); 1.1190 + JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); 1.1191 + JS_ASSERT(!unknownProperties()); 1.1192 + 1.1193 + Property *prop = HashSetLookup<jsid,Property,Property> 1.1194 + (propertySet, basePropertyCount(), id); 1.1195 + 1.1196 + return prop ? &prop->types : nullptr; 1.1197 +} 1.1198 + 1.1199 +inline unsigned 1.1200 +TypeObject::getPropertyCount() 1.1201 +{ 1.1202 + uint32_t count = basePropertyCount(); 1.1203 + if (count > SET_ARRAY_SIZE) 1.1204 + return HashSetCapacity(count); 1.1205 + return count; 1.1206 +} 1.1207 + 1.1208 +inline Property * 1.1209 +TypeObject::getProperty(unsigned i) 1.1210 +{ 1.1211 + JS_ASSERT(i < getPropertyCount()); 1.1212 + if (basePropertyCount() == 1) { 1.1213 + JS_ASSERT(i == 0); 1.1214 + return (Property *) propertySet; 1.1215 + } 1.1216 + return propertySet[i]; 1.1217 +} 1.1218 + 1.1219 +inline void 1.1220 +TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type) 1.1221 +{ 1.1222 +#ifdef JSGC_INCREMENTAL 1.1223 + if (!type) 1.1224 + return; 1.1225 + 1.1226 + switch (type->kind) { 1.1227 + case NewScript: 1.1228 + return TypeNewScript::writeBarrierPre(type->asNewScript()); 1.1229 + 1.1230 + case TypedObject: 1.1231 + return TypeTypedObject::writeBarrierPre(type->asTypedObject()); 1.1232 + } 1.1233 +#endif 1.1234 +} 1.1235 + 1.1236 +inline void 1.1237 +TypeNewScript::writeBarrierPre(TypeNewScript *newScript) 1.1238 +{ 1.1239 +#ifdef JSGC_INCREMENTAL 1.1240 + if (!newScript || !newScript->fun->runtimeFromAnyThread()->needsBarrier()) 1.1241 + return; 1.1242 + 1.1243 + JS::Zone *zone = newScript->fun->zoneFromAnyThread(); 1.1244 + if (zone->needsBarrier()) { 1.1245 + MarkObject(zone->barrierTracer(), &newScript->fun, "write barrier"); 1.1246 + MarkObject(zone->barrierTracer(), &newScript->templateObject, "write barrier"); 1.1247 + } 1.1248 +#endif 1.1249 +} 1.1250 + 1.1251 +} } /* namespace js::types */ 1.1252 + 1.1253 +inline bool 1.1254 +JSScript::ensureHasTypes(JSContext *cx) 1.1255 +{ 1.1256 + return types || makeTypes(cx); 1.1257 +} 1.1258 + 1.1259 +namespace js { 1.1260 + 1.1261 +template <> 1.1262 +struct GCMethods<const types::Type> 1.1263 +{ 1.1264 + static types::Type initial() { return types::Type::UnknownType(); } 1.1265 + static ThingRootKind kind() { return THING_ROOT_TYPE; } 1.1266 + static bool poisoned(const types::Type &v) { 1.1267 + return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) 1.1268 + || (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); 1.1269 + } 1.1270 +}; 1.1271 + 1.1272 +template <> 1.1273 +struct GCMethods<types::Type> 1.1274 +{ 1.1275 + static types::Type initial() { return types::Type::UnknownType(); } 1.1276 + static ThingRootKind kind() { return THING_ROOT_TYPE; } 1.1277 + static bool poisoned(const types::Type &v) { 1.1278 + return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) 1.1279 + || (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); 1.1280 + } 1.1281 +}; 1.1282 + 1.1283 +} // namespace js 1.1284 + 1.1285 +namespace JS { 1.1286 +template<> class AnchorPermitted<js::types::TypeObject *> { }; 1.1287 +} // namespace JS 1.1288 + 1.1289 +#endif /* jsinferinlines_h */