Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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;
1010 }
1011 }
1013 inline void
1014 TypeSet::setBaseObjectCount(uint32_t count)
1015 {
1016 JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT);
1017 flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK)
1018 | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
1019 }
1021 inline void
1022 HeapTypeSet::newPropertyState(ExclusiveContext *cxArg)
1023 {
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;
1030 }
1031 } else {
1032 JS_ASSERT(!constraintList);
1033 }
1034 }
1036 inline void
1037 HeapTypeSet::setNonDataPropertyIgnoringConstraints()
1038 {
1039 flags |= TYPE_FLAG_NON_DATA_PROPERTY;
1040 }
1042 inline void
1043 HeapTypeSet::setNonDataProperty(ExclusiveContext *cx)
1044 {
1045 if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
1046 return;
1048 setNonDataPropertyIgnoringConstraints();
1049 newPropertyState(cx);
1050 }
1052 inline void
1053 HeapTypeSet::setNonWritableProperty(ExclusiveContext *cx)
1054 {
1055 if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY)
1056 return;
1058 flags |= TYPE_FLAG_NON_WRITABLE_PROPERTY;
1059 newPropertyState(cx);
1060 }
1062 inline unsigned
1063 TypeSet::getObjectCount() const
1064 {
1065 JS_ASSERT(!unknownObject());
1066 uint32_t count = baseObjectCount();
1067 if (count > SET_ARRAY_SIZE)
1068 return HashSetCapacity(count);
1069 return count;
1070 }
1072 inline TypeObjectKey *
1073 TypeSet::getObject(unsigned i) const
1074 {
1075 JS_ASSERT(i < getObjectCount());
1076 if (baseObjectCount() == 1) {
1077 JS_ASSERT(i == 0);
1078 return (TypeObjectKey *) objectSet;
1079 }
1080 return objectSet[i];
1081 }
1083 inline JSObject *
1084 TypeSet::getSingleObject(unsigned i) const
1085 {
1086 TypeObjectKey *key = getObject(i);
1087 return (key && key->isSingleObject()) ? key->asSingleObject() : nullptr;
1088 }
1090 inline TypeObject *
1091 TypeSet::getTypeObject(unsigned i) const
1092 {
1093 TypeObjectKey *key = getObject(i);
1094 return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr;
1095 }
1097 inline const Class *
1098 TypeSet::getObjectClass(unsigned i) const
1099 {
1100 if (JSObject *object = getSingleObject(i))
1101 return object->getClass();
1102 if (TypeObject *object = getTypeObject(i))
1103 return object->clasp();
1104 return nullptr;
1105 }
1107 /////////////////////////////////////////////////////////////////////
1108 // TypeObject
1109 /////////////////////////////////////////////////////////////////////
1111 inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags)
1112 {
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));
1123 }
1125 inline uint32_t
1126 TypeObject::basePropertyCount() const
1127 {
1128 return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
1129 }
1131 inline void
1132 TypeObject::setBasePropertyCount(uint32_t count)
1133 {
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);
1138 }
1140 inline HeapTypeSet *
1141 TypeObject::getProperty(ExclusiveContext *cx, jsid id)
1142 {
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;
1156 }
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;
1164 }
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);
1178 }
1180 return &base->types;
1181 }
1183 inline HeapTypeSet *
1184 TypeObject::maybeGetProperty(jsid id)
1185 {
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;
1194 }
1196 inline unsigned
1197 TypeObject::getPropertyCount()
1198 {
1199 uint32_t count = basePropertyCount();
1200 if (count > SET_ARRAY_SIZE)
1201 return HashSetCapacity(count);
1202 return count;
1203 }
1205 inline Property *
1206 TypeObject::getProperty(unsigned i)
1207 {
1208 JS_ASSERT(i < getPropertyCount());
1209 if (basePropertyCount() == 1) {
1210 JS_ASSERT(i == 0);
1211 return (Property *) propertySet;
1212 }
1213 return propertySet[i];
1214 }
1216 inline void
1217 TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type)
1218 {
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());
1229 }
1230 #endif
1231 }
1233 inline void
1234 TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
1235 {
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");
1244 }
1245 #endif
1246 }
1248 } } /* namespace js::types */
1250 inline bool
1251 JSScript::ensureHasTypes(JSContext *cx)
1252 {
1253 return types || makeTypes(cx);
1254 }
1256 namespace js {
1258 template <>
1259 struct GCMethods<const types::Type>
1260 {
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()));
1266 }
1267 };
1269 template <>
1270 struct GCMethods<types::Type>
1271 {
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()));
1277 }
1278 };
1280 } // namespace js
1282 namespace JS {
1283 template<> class AnchorPermitted<js::types::TypeObject *> { };
1284 } // namespace JS
1286 #endif /* jsinferinlines_h */