Line data Source code
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/. */
6 :
7 : /* Definitions related to javascript type inference. */
8 :
9 : #ifndef vm_TypeInference_h
10 : #define vm_TypeInference_h
11 :
12 : #include "mozilla/MemoryReporting.h"
13 :
14 : #include "jsalloc.h"
15 : #include "jsfriendapi.h"
16 : #include "jstypes.h"
17 :
18 : #include "ds/IdValuePair.h"
19 : #include "ds/LifoAlloc.h"
20 : #include "gc/Barrier.h"
21 : #include "gc/Marking.h"
22 : #include "jit/IonTypes.h"
23 : #include "js/UbiNode.h"
24 : #include "js/Utility.h"
25 : #include "js/Vector.h"
26 : #include "threading/ProtectedData.h"
27 : #include "vm/TaggedProto.h"
28 :
29 : namespace js {
30 :
31 : namespace jit {
32 : struct IonScript;
33 : class JitAllocPolicy;
34 : class TempAllocator;
35 : } // namespace jit
36 :
37 : class TypeConstraint;
38 : class TypeNewScript;
39 : class TypeZone;
40 : class CompilerConstraintList;
41 : class HeapTypeSetKey;
42 :
43 : /*
44 : * Type inference memory management overview.
45 : *
46 : * Type information about the values observed within scripts and about the
47 : * contents of the heap is accumulated as the program executes. Compilation
48 : * accumulates constraints relating type information on the heap with the
49 : * compilations that should be invalidated when those types change. Type
50 : * information and constraints are allocated in the zone's typeLifoAlloc,
51 : * and on GC all data referring to live things is copied into a new allocator.
52 : * Thus, type set and constraints only hold weak references.
53 : */
54 :
55 : /* Flags and other state stored in TypeSet::flags */
56 : enum : uint32_t {
57 : TYPE_FLAG_UNDEFINED = 0x1,
58 : TYPE_FLAG_NULL = 0x2,
59 : TYPE_FLAG_BOOLEAN = 0x4,
60 : TYPE_FLAG_INT32 = 0x8,
61 : TYPE_FLAG_DOUBLE = 0x10,
62 : TYPE_FLAG_STRING = 0x20,
63 : TYPE_FLAG_SYMBOL = 0x40,
64 : TYPE_FLAG_LAZYARGS = 0x80,
65 : TYPE_FLAG_ANYOBJECT = 0x100,
66 :
67 : /* Mask containing all primitives */
68 : TYPE_FLAG_PRIMITIVE = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_BOOLEAN |
69 : TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_STRING |
70 : TYPE_FLAG_SYMBOL,
71 :
72 : /* Mask/shift for the number of objects in objectSet */
73 : TYPE_FLAG_OBJECT_COUNT_MASK = 0x3e00,
74 : TYPE_FLAG_OBJECT_COUNT_SHIFT = 9,
75 : TYPE_FLAG_OBJECT_COUNT_LIMIT = 7,
76 : TYPE_FLAG_DOMOBJECT_COUNT_LIMIT =
77 : TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT,
78 :
79 : /* Whether the contents of this type set are totally unknown. */
80 : TYPE_FLAG_UNKNOWN = 0x00004000,
81 :
82 : /* Mask of normal type flags on a type set. */
83 : TYPE_FLAG_BASE_MASK = 0x000041ff,
84 :
85 : /* Additional flags for HeapTypeSet sets. */
86 :
87 : /*
88 : * Whether the property has ever been deleted or reconfigured to behave
89 : * differently from a plain data property, other than making the property
90 : * non-writable.
91 : */
92 : TYPE_FLAG_NON_DATA_PROPERTY = 0x00008000,
93 :
94 : /* Whether the property has ever been made non-writable. */
95 : TYPE_FLAG_NON_WRITABLE_PROPERTY = 0x00010000,
96 :
97 : /* Whether the property might not be constant. */
98 : TYPE_FLAG_NON_CONSTANT_PROPERTY = 0x00020000,
99 :
100 : /*
101 : * Whether the property is definitely in a particular slot on all objects
102 : * from which it has not been deleted or reconfigured. For singletons
103 : * this may be a fixed or dynamic slot, and for other objects this will be
104 : * a fixed slot.
105 : *
106 : * If the property is definite, mask and shift storing the slot + 1.
107 : * Otherwise these bits are clear.
108 : */
109 : TYPE_FLAG_DEFINITE_MASK = 0xfffc0000,
110 : TYPE_FLAG_DEFINITE_SHIFT = 18
111 : };
112 : typedef uint32_t TypeFlags;
113 :
114 : /* Flags and other state stored in ObjectGroup::Flags */
115 : enum : uint32_t {
116 : /* Whether this group is associated with some allocation site. */
117 : OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1,
118 :
119 : /* Whether this group is associated with a single object. */
120 : OBJECT_FLAG_SINGLETON = 0x2,
121 :
122 : /*
123 : * Whether this group is used by objects whose singleton groups have not
124 : * been created yet.
125 : */
126 : OBJECT_FLAG_LAZY_SINGLETON = 0x4,
127 :
128 : /* Mask/shift for the number of properties in propertySet */
129 : OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff8,
130 : OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 3,
131 : OBJECT_FLAG_PROPERTY_COUNT_LIMIT =
132 : OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT,
133 :
134 : /* Whether any objects this represents may have sparse indexes. */
135 : OBJECT_FLAG_SPARSE_INDEXES = 0x00010000,
136 :
137 : /* Whether any objects this represents may not have packed dense elements. */
138 : OBJECT_FLAG_NON_PACKED = 0x00020000,
139 :
140 : /*
141 : * Whether any objects this represents may be arrays whose length does not
142 : * fit in an int32.
143 : */
144 : OBJECT_FLAG_LENGTH_OVERFLOW = 0x00040000,
145 :
146 : /* Whether any objects have been iterated over. */
147 : OBJECT_FLAG_ITERATED = 0x00080000,
148 :
149 : /* Whether any object this represents may have frozen elements. */
150 : OBJECT_FLAG_FROZEN_ELEMENTS = 0x00100000,
151 :
152 : /*
153 : * For the function on a run-once script, whether the function has actually
154 : * run multiple times.
155 : */
156 : OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000,
157 :
158 : /*
159 : * For a global object, whether any array buffers in this compartment with
160 : * typed object views have ever been detached.
161 : */
162 : OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER = 0x00400000,
163 :
164 : /*
165 : * Whether objects with this type should be allocated directly in the
166 : * tenured heap.
167 : */
168 : OBJECT_FLAG_PRE_TENURE = 0x00800000,
169 :
170 : /* Whether objects with this type might have copy on write elements. */
171 : OBJECT_FLAG_COPY_ON_WRITE = 0x01000000,
172 :
173 : /* Whether this type has had its 'new' script cleared in the past. */
174 : OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000,
175 :
176 : /*
177 : * Whether all properties of this object are considered unknown.
178 : * If set, all other flags in DYNAMIC_MASK will also be set.
179 : */
180 : OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000,
181 :
182 : /* Flags which indicate dynamic properties of represented objects. */
183 : OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000,
184 :
185 : // Mask/shift for the kind of addendum attached to this group.
186 : OBJECT_FLAG_ADDENDUM_MASK = 0x38000000,
187 : OBJECT_FLAG_ADDENDUM_SHIFT = 27,
188 :
189 : // Mask/shift for this group's generation. If out of sync with the
190 : // TypeZone's generation, this group hasn't been swept yet.
191 : OBJECT_FLAG_GENERATION_MASK = 0x40000000,
192 : OBJECT_FLAG_GENERATION_SHIFT = 30,
193 : };
194 : typedef uint32_t ObjectGroupFlags;
195 :
196 : class StackTypeSet;
197 : class HeapTypeSet;
198 : class TemporaryTypeSet;
199 :
200 : /*
201 : * Information about the set of types associated with an lvalue. There are
202 : * three kinds of type sets:
203 : *
204 : * - StackTypeSet are associated with TypeScripts, for arguments and values
205 : * observed at property reads. These are implicitly frozen on compilation
206 : * and only have constraints added to them which can trigger invalidation of
207 : * TypeNewScript information.
208 : *
209 : * - HeapTypeSet are associated with the properties of ObjectGroups. These
210 : * may have constraints added to them to trigger invalidation of either
211 : * compiled code or TypeNewScript information.
212 : *
213 : * - TemporaryTypeSet are created during compilation and do not outlive
214 : * that compilation.
215 : *
216 : * The contents of a type set completely describe the values that a particular
217 : * lvalue might have, except for the following cases:
218 : *
219 : * - If an object's prototype or class is dynamically mutated, its group will
220 : * change. Type sets containing the old group will not necessarily contain
221 : * the new group. When this occurs, the properties of the old and new group
222 : * will both be marked as unknown, which will prevent Ion from optimizing
223 : * based on the object's type information.
224 : *
225 : * - If an unboxed object is converted to a native object, its group will also
226 : * change and type sets containing the old group will not necessarily contain
227 : * the new group. Unlike the above case, this will not degrade property type
228 : * information, but Ion will no longer optimize unboxed objects with the old
229 : * group.
230 : */
231 : class TypeSet
232 : {
233 : public:
234 : // Type set entry for either a JSObject with singleton type or a
235 : // non-singleton ObjectGroup.
236 : class ObjectKey {
237 : public:
238 0 : static intptr_t keyBits(ObjectKey* obj) { return (intptr_t) obj; }
239 33019 : static ObjectKey* getKey(ObjectKey* obj) { return obj; }
240 :
241 : static inline ObjectKey* get(JSObject* obj);
242 : static inline ObjectKey* get(ObjectGroup* group);
243 :
244 47760 : bool isGroup() {
245 47760 : return (uintptr_t(this) & 1) == 0;
246 : }
247 26657 : bool isSingleton() {
248 26657 : return (uintptr_t(this) & 1) != 0;
249 : }
250 :
251 : inline ObjectGroup* group();
252 : inline JSObject* singleton();
253 :
254 : inline ObjectGroup* groupNoBarrier();
255 : inline JSObject* singletonNoBarrier();
256 :
257 : const Class* clasp();
258 : TaggedProto proto();
259 : TypeNewScript* newScript();
260 :
261 : bool unknownProperties();
262 : bool hasFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
263 : bool hasStableClassAndProto(CompilerConstraintList* constraints);
264 : void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints);
265 : void watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints);
266 : HeapTypeSetKey property(jsid id);
267 : void ensureTrackedProperty(JSContext* cx, jsid id);
268 :
269 : ObjectGroup* maybeGroup();
270 :
271 : JSCompartment* maybeCompartment();
272 : } JS_HAZ_GC_POINTER;
273 :
274 : // Information about a single concrete type. We pack this into one word,
275 : // where small values are particular primitive or other singleton types and
276 : // larger values are either specific JS objects or object groups.
277 : class Type
278 : {
279 : friend class TypeSet;
280 :
281 : uintptr_t data;
282 203666 : explicit Type(uintptr_t data) : data(data) {}
283 :
284 : public:
285 :
286 927 : uintptr_t raw() const { return data; }
287 :
288 271746 : bool isPrimitive() const {
289 271746 : return data < JSVAL_TYPE_OBJECT;
290 : }
291 :
292 22 : bool isPrimitive(JSValueType type) const {
293 22 : MOZ_ASSERT(type < JSVAL_TYPE_OBJECT);
294 22 : return (uintptr_t) type == data;
295 : }
296 :
297 105280 : JSValueType primitive() const {
298 105280 : MOZ_ASSERT(isPrimitive());
299 105280 : return (JSValueType) data;
300 : }
301 :
302 152 : bool isMagicArguments() const {
303 152 : return primitive() == JSVAL_TYPE_MAGIC;
304 : }
305 :
306 0 : bool isSomeObject() const {
307 0 : return data == JSVAL_TYPE_OBJECT || data > JSVAL_TYPE_UNKNOWN;
308 : }
309 :
310 182950 : bool isAnyObject() const {
311 182950 : return data == JSVAL_TYPE_OBJECT;
312 : }
313 :
314 288465 : bool isUnknown() const {
315 288465 : return data == JSVAL_TYPE_UNKNOWN;
316 : }
317 :
318 : /* Accessors for types that are either JSObject or ObjectGroup. */
319 :
320 121609 : bool isObject() const {
321 121609 : MOZ_ASSERT(!isAnyObject() && !isUnknown());
322 121610 : return data > JSVAL_TYPE_UNKNOWN;
323 : }
324 :
325 191112 : bool isObjectUnchecked() const {
326 191112 : return data > JSVAL_TYPE_UNKNOWN;
327 : }
328 :
329 : inline ObjectKey* objectKey() const;
330 :
331 : /* Accessors for JSObject types */
332 :
333 16126 : bool isSingleton() const {
334 16126 : return isObject() && !!(data & 1);
335 : }
336 88063 : bool isSingletonUnchecked() const {
337 88063 : return isObjectUnchecked() && !!(data & 1);
338 : }
339 :
340 : inline JSObject* singleton() const;
341 : inline JSObject* singletonNoBarrier() const;
342 :
343 : /* Accessors for ObjectGroup types */
344 :
345 14157 : bool isGroup() const {
346 14157 : return isObject() && !(data & 1);
347 : }
348 45532 : bool isGroupUnchecked() const {
349 45532 : return isObjectUnchecked() && !(data & 1);
350 : }
351 :
352 : inline ObjectGroup* group() const;
353 : inline ObjectGroup* groupNoBarrier() const;
354 :
355 : inline void trace(JSTracer* trc);
356 :
357 : JSCompartment* maybeCompartment();
358 :
359 5306 : bool operator == (Type o) const { return data == o.data; }
360 6840 : bool operator != (Type o) const { return data != o.data; }
361 : } JS_HAZ_GC_POINTER;
362 :
363 1616 : static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
364 1543 : static inline Type NullType() { return Type(JSVAL_TYPE_NULL); }
365 847 : static inline Type BooleanType() { return Type(JSVAL_TYPE_BOOLEAN); }
366 984 : static inline Type Int32Type() { return Type(JSVAL_TYPE_INT32); }
367 1566 : static inline Type DoubleType() { return Type(JSVAL_TYPE_DOUBLE); }
368 856 : static inline Type StringType() { return Type(JSVAL_TYPE_STRING); }
369 178 : static inline Type SymbolType() { return Type(JSVAL_TYPE_SYMBOL); }
370 197 : static inline Type MagicArgType() { return Type(JSVAL_TYPE_MAGIC); }
371 2703 : static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); }
372 1314 : static inline Type UnknownType() { return Type(JSVAL_TYPE_UNKNOWN); }
373 :
374 85609 : static inline Type PrimitiveType(JSValueType type) {
375 85609 : MOZ_ASSERT(type < JSVAL_TYPE_UNKNOWN);
376 85609 : return Type(type);
377 : }
378 :
379 : static inline Type ObjectType(JSObject* obj);
380 : static inline Type ObjectType(ObjectGroup* group);
381 : static inline Type ObjectType(ObjectKey* key);
382 :
383 : static const char* NonObjectTypeString(Type type);
384 :
385 : static const char* TypeString(Type type);
386 : static const char* ObjectGroupString(ObjectGroup* group);
387 :
388 : protected:
389 : /* Flags for this type set. */
390 : TypeFlags flags;
391 :
392 : /* Possible objects this type set can represent. */
393 : ObjectKey** objectSet;
394 :
395 : public:
396 :
397 36885 : TypeSet()
398 36885 : : flags(0), objectSet(nullptr)
399 36885 : {}
400 :
401 : void print(FILE* fp = stderr);
402 :
403 : /* Whether this set contains a specific type. */
404 : MOZ_ALWAYS_INLINE bool hasType(Type type) const;
405 :
406 76942 : TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; }
407 197216 : bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); }
408 182618 : bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
409 48006 : bool empty() const { return !baseFlags() && !baseObjectCount(); }
410 :
411 434 : bool hasAnyFlag(TypeFlags flags) const {
412 434 : MOZ_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags);
413 434 : return !!(baseFlags() & flags);
414 : }
415 :
416 197 : bool nonDataProperty() const {
417 197 : return flags & TYPE_FLAG_NON_DATA_PROPERTY;
418 : }
419 101 : bool nonWritableProperty() const {
420 101 : return flags & TYPE_FLAG_NON_WRITABLE_PROPERTY;
421 : }
422 15306 : bool nonConstantProperty() const {
423 15306 : return flags & TYPE_FLAG_NON_CONSTANT_PROPERTY;
424 : }
425 1221 : bool definiteProperty() const { return flags & TYPE_FLAG_DEFINITE_MASK; }
426 1132 : unsigned definiteSlot() const {
427 1132 : MOZ_ASSERT(definiteProperty());
428 1132 : return (flags >> TYPE_FLAG_DEFINITE_SHIFT) - 1;
429 : }
430 :
431 : /* Join two type sets into a new set. The result should not be modified further. */
432 : static TemporaryTypeSet* unionSets(TypeSet* a, TypeSet* b, LifoAlloc* alloc);
433 : /* Return the intersection of the 2 TypeSets. The result should not be modified further */
434 : static TemporaryTypeSet* intersectSets(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc);
435 : /*
436 : * Returns a copy of TypeSet a excluding/removing the types in TypeSet b.
437 : * TypeSet b can only contain primitives or be any object. No support for
438 : * specific objects. The result should not be modified further.
439 : */
440 : static TemporaryTypeSet* removeSet(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc);
441 :
442 : /* Add a type to this set using the specified allocator. */
443 : void addType(Type type, LifoAlloc* alloc);
444 :
445 : /* Get a list of all types in this set. */
446 : typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
447 : template <class TypeListT> bool enumerateTypes(TypeListT* list) const;
448 :
449 : /*
450 : * Iterate through the objects in this set. getObjectCount overapproximates
451 : * in the hash case (see SET_ARRAY_SIZE in TypeInference-inl.h), and
452 : * getObject may return nullptr.
453 : */
454 : inline unsigned getObjectCount() const;
455 : inline ObjectKey* getObject(unsigned i) const;
456 : inline JSObject* getSingleton(unsigned i) const;
457 : inline ObjectGroup* getGroup(unsigned i) const;
458 : inline JSObject* getSingletonNoBarrier(unsigned i) const;
459 : inline ObjectGroup* getGroupNoBarrier(unsigned i) const;
460 :
461 : /* The Class of an object in this set. */
462 : inline const Class* getObjectClass(unsigned i) const;
463 :
464 2130 : bool canSetDefinite(unsigned slot) {
465 : // Note: the cast is required to work around an MSVC issue.
466 2130 : return (slot + 1) <= (unsigned(TYPE_FLAG_DEFINITE_MASK) >> TYPE_FLAG_DEFINITE_SHIFT);
467 : }
468 1065 : void setDefinite(unsigned slot) {
469 1065 : MOZ_ASSERT(canSetDefinite(slot));
470 1065 : flags |= ((slot + 1) << TYPE_FLAG_DEFINITE_SHIFT);
471 1065 : MOZ_ASSERT(definiteSlot() == slot);
472 1065 : }
473 :
474 : /* Whether any values in this set might have the specified type. */
475 : bool mightBeMIRType(jit::MIRType type) const;
476 :
477 : /*
478 : * Get whether this type set is known to be a subset of other.
479 : * This variant doesn't freeze constraints. That variant is called knownSubset
480 : */
481 : bool isSubset(const TypeSet* other) const;
482 :
483 : /*
484 : * Get whether the objects in this TypeSet are a subset of the objects
485 : * in other.
486 : */
487 : bool objectsAreSubset(TypeSet* other);
488 :
489 : /* Whether this TypeSet contains exactly the same types as other. */
490 1606 : bool equals(const TypeSet* other) const {
491 1606 : return this->isSubset(other) && other->isSubset(this);
492 : }
493 :
494 : bool objectsIntersect(const TypeSet* other) const;
495 :
496 : /* Forward all types in this set to the specified constraint. */
497 : bool addTypesToConstraint(JSContext* cx, TypeConstraint* constraint);
498 :
499 : // Clone a type set into an arbitrary allocator.
500 : TemporaryTypeSet* clone(LifoAlloc* alloc) const;
501 : bool clone(LifoAlloc* alloc, TemporaryTypeSet* result) const;
502 :
503 : // Create a new TemporaryTypeSet where undefined and/or null has been filtered out.
504 : TemporaryTypeSet* filter(LifoAlloc* alloc, bool filterUndefined, bool filterNull) const;
505 : // Create a new TemporaryTypeSet where the type has been set to object.
506 : TemporaryTypeSet* cloneObjectsOnly(LifoAlloc* alloc);
507 : TemporaryTypeSet* cloneWithoutObjects(LifoAlloc* alloc);
508 :
509 : JSCompartment* maybeCompartment();
510 :
511 : // Trigger a read barrier on all the contents of a type set.
512 : static void readBarrier(const TypeSet* types);
513 :
514 : protected:
515 206179 : uint32_t baseObjectCount() const {
516 206179 : return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
517 : }
518 : inline void setBaseObjectCount(uint32_t count);
519 :
520 : void clearObjects();
521 :
522 : public:
523 : static inline Type GetValueType(const Value& val);
524 :
525 : static inline bool IsUntrackedValue(const Value& val);
526 :
527 : // Get the type of a possibly optimized out or uninitialized let value.
528 : // This generally only happens on unconditional type monitors on bailing
529 : // out of Ion, such as for argument and local types.
530 : static inline Type GetMaybeUntrackedValueType(const Value& val);
531 :
532 : static bool IsTypeMarked(JSRuntime* rt, Type* v);
533 : static bool IsTypeAboutToBeFinalized(Type* v);
534 : } JS_HAZ_GC_POINTER;
535 :
536 : #if JS_BITS_PER_WORD == 32
537 : static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4;
538 : #else
539 : static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4c5c6d7d8;
540 : #endif
541 : static const uintptr_t TypeConstraintMagic = BaseTypeInferenceMagic + 1;
542 : static const uintptr_t ConstraintTypeSetMagic = BaseTypeInferenceMagic + 2;
543 :
544 : #ifdef JS_CRASH_DIAGNOSTICS
545 : extern void
546 : ReportMagicWordFailure(uintptr_t actual, uintptr_t expected);
547 : extern void
548 : ReportMagicWordFailure(uintptr_t actual, uintptr_t expected, uintptr_t flags, uintptr_t objectSet);
549 : #endif
550 :
551 : /*
552 : * A constraint which listens to additions to a type set and propagates those
553 : * changes to other type sets.
554 : */
555 : class TypeConstraint
556 : {
557 : #ifdef JS_CRASH_DIAGNOSTICS
558 : uintptr_t magic_;
559 : #endif
560 :
561 : /* Next constraint listening to the same type set. */
562 : TypeConstraint* next_;
563 :
564 : public:
565 866 : TypeConstraint()
566 866 : : next_(nullptr)
567 : {
568 : #ifdef JS_CRASH_DIAGNOSTICS
569 866 : magic_ = TypeConstraintMagic;
570 : #endif
571 866 : }
572 :
573 2295 : void checkMagic() const {
574 : #ifdef JS_CRASH_DIAGNOSTICS
575 2295 : if (MOZ_UNLIKELY(magic_ != TypeConstraintMagic))
576 0 : ReportMagicWordFailure(magic_, TypeConstraintMagic);
577 : #endif
578 2295 : }
579 :
580 868 : TypeConstraint* next() const {
581 868 : checkMagic();
582 868 : if (next_)
583 0 : next_->checkMagic();
584 868 : return next_;
585 : }
586 866 : void setNext(TypeConstraint* next) {
587 866 : MOZ_ASSERT(!next_);
588 866 : checkMagic();
589 866 : if (next)
590 559 : next->checkMagic();
591 866 : next_ = next;
592 866 : }
593 :
594 : /* Debugging name for this kind of constraint. */
595 : virtual const char* kind() = 0;
596 :
597 : /* Register a new type for the set this constraint is listening to. */
598 : virtual void newType(JSContext* cx, TypeSet* source, TypeSet::Type type) = 0;
599 :
600 : /*
601 : * For constraints attached to an object property's type set, mark the
602 : * property as having changed somehow.
603 : */
604 0 : virtual void newPropertyState(JSContext* cx, TypeSet* source) {}
605 :
606 : /*
607 : * For constraints attached to the JSID_EMPTY type set on an object,
608 : * indicate a change in one of the object's dynamic property flags or other
609 : * state.
610 : */
611 0 : virtual void newObjectState(JSContext* cx, ObjectGroup* group) {}
612 :
613 : /*
614 : * If the data this constraint refers to is still live, copy it into the
615 : * zone's new allocator. Type constraints only hold weak references.
616 : */
617 : virtual bool sweep(TypeZone& zone, TypeConstraint** res) = 0;
618 :
619 : /* The associated compartment, if any. */
620 : virtual JSCompartment* maybeCompartment() = 0;
621 : };
622 :
623 : // If there is an OOM while sweeping types, the type information is deoptimized
624 : // so that it stays correct (i.e. overapproximates the possible types in the
625 : // zone), but constraints might not have been triggered on the deoptimization
626 : // or even copied over completely. In this case, destroy all JIT code and new
627 : // script information in the zone, the only things whose correctness depends on
628 : // the type constraints.
629 : class AutoClearTypeInferenceStateOnOOM
630 : {
631 : Zone* zone;
632 : bool oom;
633 :
634 : AutoClearTypeInferenceStateOnOOM(const AutoClearTypeInferenceStateOnOOM&) = delete;
635 : void operator=(const AutoClearTypeInferenceStateOnOOM&) = delete;
636 :
637 : public:
638 : explicit AutoClearTypeInferenceStateOnOOM(Zone* zone);
639 : ~AutoClearTypeInferenceStateOnOOM();
640 :
641 0 : void setOOM() {
642 0 : oom = true;
643 0 : }
644 0 : bool hadOOM() const {
645 0 : return oom;
646 : }
647 : };
648 :
649 : /* Superclass common to stack and heap type sets. */
650 : class ConstraintTypeSet : public TypeSet
651 : {
652 : #ifdef JS_CRASH_DIAGNOSTICS
653 : uintptr_t magic_;
654 : #endif
655 :
656 : protected:
657 : /* Chain of constraints which propagate changes out from this type set. */
658 : TypeConstraint* constraintList_;
659 :
660 : public:
661 24801 : ConstraintTypeSet()
662 24801 : : constraintList_(nullptr)
663 : {
664 : #ifdef JS_CRASH_DIAGNOSTICS
665 24801 : magic_ = ConstraintTypeSetMagic;
666 : #endif
667 24801 : }
668 :
669 : #ifdef JS_CRASH_DIAGNOSTICS
670 16285 : void initMagic() {
671 16285 : MOZ_ASSERT(!magic_);
672 16285 : magic_ = ConstraintTypeSetMagic;
673 16285 : }
674 : #endif
675 :
676 235107 : void checkMagic() const {
677 : #ifdef JS_CRASH_DIAGNOSTICS
678 235107 : if (MOZ_UNLIKELY(magic_ != ConstraintTypeSetMagic))
679 0 : ReportMagicWordFailure(magic_, ConstraintTypeSetMagic, uintptr_t(flags), uintptr_t(objectSet));
680 : #endif
681 235107 : }
682 :
683 58645 : TypeConstraint* constraintList() const {
684 58645 : checkMagic();
685 58645 : if (constraintList_)
686 2 : constraintList_->checkMagic();
687 58645 : return constraintList_;
688 : }
689 : void setConstraintList(TypeConstraint* constraint) {
690 : MOZ_ASSERT(!constraintList_);
691 : checkMagic();
692 : if (constraint)
693 : constraint->checkMagic();
694 : constraintList_ = constraint;
695 : }
696 :
697 : /*
698 : * Add a type to this set, calling any constraint handlers if this is a new
699 : * possible type.
700 : */
701 : void addType(JSContext* cx, Type type);
702 :
703 : // Trigger a post barrier when writing to this set, if necessary.
704 : // addType(cx, type) takes care of this automatically.
705 : void postWriteBarrier(JSContext* cx, Type type);
706 :
707 : /* Add a new constraint to this set. */
708 : bool addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting = true);
709 :
710 : inline void sweep(JS::Zone* zone, AutoClearTypeInferenceStateOnOOM& oom);
711 : inline void trace(JS::Zone* zone, JSTracer* trc);
712 : };
713 :
714 : class StackTypeSet : public ConstraintTypeSet
715 : {
716 : public:
717 : };
718 :
719 24801 : class HeapTypeSet : public ConstraintTypeSet
720 : {
721 : inline void newPropertyState(JSContext* cx);
722 :
723 : public:
724 : /* Mark this type set as representing a non-data property. */
725 : inline void setNonDataProperty(JSContext* cx);
726 :
727 : /* Mark this type set as representing a non-writable property. */
728 : inline void setNonWritableProperty(JSContext* cx);
729 :
730 : // Mark this type set as being non-constant.
731 : inline void setNonConstantProperty(JSContext* cx);
732 : };
733 :
734 : CompilerConstraintList*
735 : NewCompilerConstraintList(jit::TempAllocator& alloc);
736 :
737 : class TemporaryTypeSet : public TypeSet
738 : {
739 : public:
740 3247 : TemporaryTypeSet() {}
741 : TemporaryTypeSet(LifoAlloc* alloc, Type type);
742 :
743 6764 : TemporaryTypeSet(uint32_t flags, ObjectKey** objectSet) {
744 6764 : this->flags = flags;
745 6764 : this->objectSet = objectSet;
746 6764 : }
747 :
748 772 : TemporaryTypeSet(LifoAlloc* alloc, jit::MIRType type)
749 772 : : TemporaryTypeSet(alloc, PrimitiveType(ValueTypeFromMIRType(type)))
750 : {
751 772 : MOZ_ASSERT(type != jit::MIRType::Value);
752 772 : }
753 :
754 : /*
755 : * Constraints for JIT compilation.
756 : *
757 : * Methods for JIT compilation. These must be used when a script is
758 : * currently being compiled (see AutoEnterCompilation) and will add
759 : * constraints ensuring that if the return value change in the future due
760 : * to new type information, the script's jitcode will be discarded.
761 : */
762 :
763 : /* Get any type tag which all values in this set must have. */
764 : jit::MIRType getKnownMIRType();
765 :
766 : bool isMagicArguments() { return getKnownMIRType() == jit::MIRType::MagicOptimizedArguments; }
767 :
768 : /* Whether this value may be an object. */
769 678 : bool maybeObject() { return unknownObject() || baseObjectCount() > 0; }
770 :
771 : /*
772 : * Whether this typeset represents a potentially sentineled object value:
773 : * the value may be an object or null or undefined.
774 : * Returns false if the value cannot ever be an object.
775 : */
776 405 : bool objectOrSentinel() {
777 405 : TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT;
778 405 : if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK))
779 0 : return false;
780 :
781 405 : return hasAnyFlag(TYPE_FLAG_ANYOBJECT) || baseObjectCount() > 0;
782 : }
783 :
784 : /* Whether the type set contains objects with any of a set of flags. */
785 : bool hasObjectFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
786 :
787 : /* Get the class shared by all objects in this set, or nullptr. */
788 : const Class* getKnownClass(CompilerConstraintList* constraints);
789 :
790 : /* Result returned from forAllClasses */
791 : enum ForAllResult {
792 : EMPTY=1, // Set empty
793 : ALL_TRUE, // Set not empty and predicate returned true for all classes
794 : ALL_FALSE, // Set not empty and predicate returned false for all classes
795 : MIXED, // Set not empty and predicate returned false for some classes
796 : // and true for others, or set contains an unknown or non-object
797 : // type
798 : };
799 :
800 : /* Apply func to the members of the set and return an appropriate result.
801 : * The iteration may end early if the result becomes known early.
802 : */
803 : ForAllResult forAllClasses(CompilerConstraintList* constraints,
804 : bool (*func)(const Class* clasp));
805 :
806 : /*
807 : * Returns true if all objects in this set have the same prototype, and
808 : * assigns this object to *proto. The proto can be nullptr.
809 : */
810 : bool getCommonPrototype(CompilerConstraintList* constraints, JSObject** proto);
811 :
812 : /* Whether the buffer mapped by a TypedArray is shared memory or not */
813 : enum TypedArraySharedness {
814 : UnknownSharedness=1, // We can't determine sharedness
815 : KnownShared, // We know for sure the buffer is shared
816 : KnownUnshared // We know for sure the buffer is unshared
817 : };
818 :
819 : /* Get the typed array type of all objects in this set, or Scalar::MaxTypedArrayViewType.
820 : * If there is such a common type and sharedness is not nullptr then
821 : * *sharedness is set to what we know about the sharedness of the memory.
822 : */
823 : Scalar::Type getTypedArrayType(CompilerConstraintList* constraints,
824 : TypedArraySharedness* sharedness = nullptr);
825 :
826 : /* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */
827 : bool isDOMClass(CompilerConstraintList* constraints);
828 :
829 : /* Whether clasp->isCallable() is true for one or more objects in this set. */
830 : bool maybeCallable(CompilerConstraintList* constraints);
831 :
832 : /* Whether clasp->isProxy() might be true for one or more objects in this set. */
833 : bool maybeProxy(CompilerConstraintList* constraints);
834 :
835 : /* Whether clasp->emulatesUndefined() is true for one or more objects in this set. */
836 : bool maybeEmulatesUndefined(CompilerConstraintList* constraints);
837 :
838 : /* Get the single value which can appear in this type set, otherwise nullptr. */
839 : JSObject* maybeSingleton();
840 : ObjectKey* maybeSingleObject();
841 :
842 : /* Whether any objects in the type set needs a barrier on id. */
843 : bool propertyNeedsBarrier(CompilerConstraintList* constraints, jsid id);
844 :
845 : /*
846 : * Whether this set contains all types in other, except (possibly) the
847 : * specified type.
848 : */
849 : bool filtersType(const TemporaryTypeSet* other, Type type) const;
850 :
851 : enum DoubleConversion {
852 : /* All types in the set should use eager double conversion. */
853 : AlwaysConvertToDoubles,
854 :
855 : /* Some types in the set should use eager double conversion. */
856 : MaybeConvertToDoubles,
857 :
858 : /* No types should use eager double conversion. */
859 : DontConvertToDoubles,
860 :
861 : /* Some types should use eager double conversion, others cannot. */
862 : AmbiguousDoubleConversion
863 : };
864 :
865 : /*
866 : * Whether known double optimizations are possible for element accesses on
867 : * objects in this type set.
868 : */
869 : DoubleConversion convertDoubleElements(CompilerConstraintList* constraints);
870 :
871 : private:
872 : void getTypedArraySharedness(CompilerConstraintList* constraints,
873 : TypedArraySharedness* sharedness);
874 : };
875 :
876 : bool
877 : AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id);
878 :
879 : bool
880 : AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group,
881 : JSScript* script, JSScript* calleeScript);
882 :
883 : // For groups where only a small number of objects have been allocated, this
884 : // structure keeps track of all objects in the group. Once COUNT objects have
885 : // been allocated, this structure is cleared and the objects are analyzed, to
886 : // perform the new script properties analyses or determine if an unboxed
887 : // representation can be used.
888 : class PreliminaryObjectArray
889 : {
890 : public:
891 : static const uint32_t COUNT = 20;
892 :
893 : private:
894 : // All objects with the type which have been allocated. The pointers in
895 : // this array are weak.
896 : JSObject* objects[COUNT];
897 :
898 : public:
899 3473 : PreliminaryObjectArray() {
900 3473 : mozilla::PodZero(this);
901 3473 : }
902 :
903 : void registerNewObject(JSObject* res);
904 : void unregisterObject(JSObject* obj);
905 :
906 3091 : JSObject* get(size_t i) const {
907 3091 : MOZ_ASSERT(i < COUNT);
908 3091 : return objects[i];
909 : }
910 :
911 : bool full() const;
912 : bool empty() const;
913 : void sweep();
914 : };
915 :
916 58 : class PreliminaryObjectArrayWithTemplate : public PreliminaryObjectArray
917 : {
918 : HeapPtr<Shape*> shape_;
919 :
920 : public:
921 3278 : explicit PreliminaryObjectArrayWithTemplate(Shape* shape)
922 3278 : : shape_(shape)
923 3278 : {}
924 :
925 0 : void clear() {
926 0 : shape_.init(nullptr);
927 0 : }
928 :
929 2607 : Shape* shape() {
930 2607 : return shape_;
931 : }
932 :
933 : void maybeAnalyze(JSContext* cx, ObjectGroup* group, bool force = false);
934 :
935 : void trace(JSTracer* trc);
936 :
937 : static void writeBarrierPre(PreliminaryObjectArrayWithTemplate* preliminaryObjects);
938 : };
939 :
940 : // New script properties analyses overview.
941 : //
942 : // When constructing objects using 'new' on a script, we attempt to determine
943 : // the properties which that object will eventually have. This is done via two
944 : // analyses. One of these, the definite properties analysis, is static, and the
945 : // other, the acquired properties analysis, is dynamic. As objects are
946 : // constructed using 'new' on some script to create objects of group G, our
947 : // analysis strategy is as follows:
948 : //
949 : // - When the first objects are created, no analysis is immediately performed.
950 : // Instead, all objects of group G are accumulated in an array.
951 : //
952 : // - After a certain number of such objects have been created, the definite
953 : // properties analysis is performed. This analyzes the body of the
954 : // constructor script and any other functions it calls to look for properties
955 : // which will definitely be added by the constructor in a particular order,
956 : // creating an object with shape S.
957 : //
958 : // - The properties in S are compared with the greatest common prefix P of the
959 : // shapes of the objects that have been created. If P has more properties
960 : // than S, the acquired properties analysis is performed.
961 : //
962 : // - The acquired properties analysis marks all properties in P as definite
963 : // in G, and creates a new group IG for objects which are partially
964 : // initialized. Objects of group IG are initially created with shape S, and if
965 : // they are later given shape P, their group can be changed to G.
966 : //
967 : // For objects which are rarely created, the definite properties analysis can
968 : // be triggered after only one or a few objects have been allocated, when code
969 : // being Ion compiled might access them. In this case type information in the
970 : // constructor might not be good enough for the definite properties analysis to
971 : // compute useful information, but the acquired properties analysis will still
972 : // be able to identify definite properties in this case.
973 : //
974 : // This layered approach is designed to maximize performance on easily
975 : // analyzable code, while still allowing us to determine definite properties
976 : // robustly when code consistently adds the same properties to objects, but in
977 : // complex ways which can't be understood statically.
978 : class TypeNewScript
979 : {
980 : public:
981 : struct Initializer {
982 : enum Kind {
983 : SETPROP,
984 : SETPROP_FRAME,
985 : DONE
986 : } kind;
987 : uint32_t offset;
988 0 : Initializer(Kind kind, uint32_t offset)
989 0 : : kind(kind), offset(offset)
990 0 : {}
991 : };
992 :
993 : private:
994 : // Scripted function which this information was computed for.
995 : HeapPtr<JSFunction*> function_;
996 :
997 : // Any preliminary objects with the type. The analyses are not performed
998 : // until this array is cleared.
999 : PreliminaryObjectArray* preliminaryObjects;
1000 :
1001 : // After the new script properties analyses have been performed, a template
1002 : // object to use for newly constructed objects. The shape of this object
1003 : // reflects all definite properties the object will have, and the
1004 : // allocation kind to use. This is null if the new objects have an unboxed
1005 : // layout, in which case the UnboxedLayout provides the initial structure
1006 : // of the object.
1007 : HeapPtr<PlainObject*> templateObject_;
1008 :
1009 : // Order in which definite properties become initialized. We need this in
1010 : // case the definite properties are invalidated (such as by adding a setter
1011 : // to an object on the prototype chain) while an object is in the middle of
1012 : // being initialized, so we can walk the stack and fixup any objects which
1013 : // look for in-progress objects which were prematurely set with an incorrect
1014 : // shape. Property assignments in inner frames are preceded by a series of
1015 : // SETPROP_FRAME entries specifying the stack down to the frame containing
1016 : // the write.
1017 : Initializer* initializerList;
1018 :
1019 : // If there are additional properties found by the acquired properties
1020 : // analysis which were not found by the definite properties analysis, this
1021 : // shape contains all such additional properties (plus the definite
1022 : // properties). When an object of this group acquires this shape, it is
1023 : // fully initialized and its group can be changed to initializedGroup.
1024 : HeapPtr<Shape*> initializedShape_;
1025 :
1026 : // Group with definite properties set for all properties found by
1027 : // both the definite and acquired properties analyses.
1028 : HeapPtr<ObjectGroup*> initializedGroup_;
1029 :
1030 : public:
1031 195 : TypeNewScript() { mozilla::PodZero(this); }
1032 16 : ~TypeNewScript() {
1033 8 : js_delete(preliminaryObjects);
1034 8 : js_free(initializerList);
1035 8 : }
1036 :
1037 0 : void clear() {
1038 0 : function_.init(nullptr);
1039 0 : templateObject_.init(nullptr);
1040 0 : initializedShape_.init(nullptr);
1041 0 : initializedGroup_.init(nullptr);
1042 0 : }
1043 :
1044 : static void writeBarrierPre(TypeNewScript* newScript);
1045 :
1046 2677 : bool analyzed() const {
1047 2677 : return preliminaryObjects == nullptr;
1048 : }
1049 :
1050 26 : PlainObject* templateObject() const {
1051 26 : return templateObject_;
1052 : }
1053 :
1054 1488 : Shape* initializedShape() const {
1055 1488 : return initializedShape_;
1056 : }
1057 :
1058 1067 : ObjectGroup* initializedGroup() const {
1059 1067 : return initializedGroup_;
1060 : }
1061 :
1062 19 : JSFunction* function() const {
1063 19 : return function_;
1064 : }
1065 :
1066 : void trace(JSTracer* trc);
1067 : void sweep();
1068 :
1069 : void registerNewObject(PlainObject* res);
1070 : bool maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force = false);
1071 :
1072 : bool rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* group);
1073 :
1074 : static bool make(JSContext* cx, ObjectGroup* group, JSFunction* fun);
1075 : static TypeNewScript* makeNativeVersion(JSContext* cx, TypeNewScript* newScript,
1076 : PlainObject* templateObject);
1077 :
1078 : size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
1079 :
1080 13 : static size_t offsetOfPreliminaryObjects() {
1081 13 : return offsetof(TypeNewScript, preliminaryObjects);
1082 : }
1083 : };
1084 :
1085 : /* Is this a reasonable PC to be doing inlining on? */
1086 : inline bool isInlinableCall(jsbytecode* pc);
1087 :
1088 : bool
1089 : ClassCanHaveExtraProperties(const Class* clasp);
1090 :
1091 : /*
1092 : * Information about the result of the compilation of a script. This structure
1093 : * stored in the TypeCompartment is indexed by the RecompileInfo. This
1094 : * indirection enables the invalidation of all constraints related to the same
1095 : * compilation.
1096 : */
1097 : class CompilerOutput
1098 : {
1099 : // If this compilation has not been invalidated, the associated script and
1100 : // kind of compilation being performed.
1101 : JSScript* script_;
1102 :
1103 : // Whether this compilation is about to be invalidated.
1104 : bool pendingInvalidation_ : 1;
1105 :
1106 : // During sweeping, the list of compiler outputs is compacted and invalidated
1107 : // outputs are removed. This gives the new index for a valid compiler output.
1108 : uint32_t sweepIndex_ : 31;
1109 :
1110 : public:
1111 : static const uint32_t INVALID_SWEEP_INDEX = static_cast<uint32_t>(1 << 31) - 1;
1112 :
1113 : CompilerOutput()
1114 : : script_(nullptr),
1115 : pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
1116 : {}
1117 :
1118 7 : explicit CompilerOutput(JSScript* script)
1119 7 : : script_(script),
1120 7 : pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
1121 7 : {}
1122 :
1123 20 : JSScript* script() const { return script_; }
1124 :
1125 : inline jit::IonScript* ion() const;
1126 :
1127 26 : bool isValid() const {
1128 26 : return script_ != nullptr;
1129 : }
1130 7 : void invalidate() {
1131 7 : script_ = nullptr;
1132 7 : }
1133 :
1134 0 : void setPendingInvalidation() {
1135 0 : pendingInvalidation_ = true;
1136 0 : }
1137 5 : bool pendingInvalidation() {
1138 5 : return pendingInvalidation_;
1139 : }
1140 :
1141 0 : void setSweepIndex(uint32_t index) {
1142 0 : if (index >= INVALID_SWEEP_INDEX)
1143 0 : MOZ_CRASH();
1144 0 : sweepIndex_ = index;
1145 0 : }
1146 0 : uint32_t sweepIndex() {
1147 0 : MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
1148 0 : return sweepIndex_;
1149 : }
1150 : };
1151 :
1152 : class RecompileInfo
1153 : {
1154 : // Index in the TypeZone's compilerOutputs or sweepCompilerOutputs arrays,
1155 : // depending on the generation value.
1156 : uint32_t outputIndex : 31;
1157 :
1158 : // If out of sync with the TypeZone's generation, this index is for the
1159 : // zone's sweepCompilerOutputs rather than compilerOutputs.
1160 : uint32_t generation : 1;
1161 :
1162 : public:
1163 7 : RecompileInfo(uint32_t outputIndex, uint32_t generation)
1164 7 : : outputIndex(outputIndex), generation(generation)
1165 7 : {}
1166 :
1167 12 : RecompileInfo()
1168 12 : : outputIndex(JS_BITMASK(31)), generation(0)
1169 12 : {}
1170 :
1171 16 : bool operator==(const RecompileInfo& other) const {
1172 16 : return outputIndex == other.outputIndex && generation == other.generation;
1173 : }
1174 :
1175 : CompilerOutput* compilerOutput(TypeZone& types) const;
1176 : CompilerOutput* compilerOutput(JSContext* cx) const;
1177 : bool shouldSweep(TypeZone& types);
1178 : };
1179 :
1180 : // The RecompileInfoVector has a MinInlineCapacity of one so that invalidating a
1181 : // single IonScript doesn't require an allocation.
1182 : typedef Vector<RecompileInfo, 1, SystemAllocPolicy> RecompileInfoVector;
1183 :
1184 : /* Persistent type information for a script, retained across GCs. */
1185 0 : class TypeScript
1186 : {
1187 : friend class ::JSScript;
1188 :
1189 : // The freeze constraints added to stack type sets will only directly
1190 : // invalidate the script containing those stack type sets. This Vector
1191 : // contains compilations that inlined this script, so we can invalidate
1192 : // them as well.
1193 : RecompileInfoVector inlinedCompilations_;
1194 :
1195 : // Variable-size array
1196 : StackTypeSet typeArray_[1];
1197 :
1198 : public:
1199 4 : RecompileInfoVector& inlinedCompilations() {
1200 4 : return inlinedCompilations_;
1201 : }
1202 24 : MOZ_MUST_USE bool addInlinedCompilation(RecompileInfo info) {
1203 24 : if (!inlinedCompilations_.empty() && inlinedCompilations_.back() == info)
1204 15 : return true;
1205 9 : return inlinedCompilations_.append(info);
1206 : }
1207 :
1208 : /* Array of type sets for variables and JOF_TYPESET ops. */
1209 31814 : StackTypeSet* typeArray() const {
1210 : // Ensure typeArray_ is the last data member of TypeScript.
1211 : JS_STATIC_ASSERT(sizeof(TypeScript) ==
1212 : sizeof(typeArray_) + offsetof(TypeScript, typeArray_));
1213 31814 : return const_cast<StackTypeSet*>(typeArray_);
1214 : }
1215 :
1216 705 : static inline size_t SizeIncludingTypeArray(size_t arraySize) {
1217 : // Ensure typeArray_ is the last data member of TypeScript.
1218 : JS_STATIC_ASSERT(sizeof(TypeScript) ==
1219 : sizeof(StackTypeSet) + offsetof(TypeScript, typeArray_));
1220 705 : return offsetof(TypeScript, typeArray_) + arraySize * sizeof(StackTypeSet);
1221 : }
1222 :
1223 : static inline unsigned NumTypeSets(JSScript* script);
1224 :
1225 : static inline StackTypeSet* ThisTypes(JSScript* script);
1226 : static inline StackTypeSet* ArgTypes(JSScript* script, unsigned i);
1227 :
1228 : /* Get the type set for values observed at an opcode. */
1229 : static inline StackTypeSet* BytecodeTypes(JSScript* script, jsbytecode* pc);
1230 :
1231 : template <typename TYPESET>
1232 : static inline TYPESET* BytecodeTypes(JSScript* script, jsbytecode* pc, uint32_t* bytecodeMap,
1233 : uint32_t* hint, TYPESET* typeArray);
1234 :
1235 : /*
1236 : * Monitor a bytecode pushing any value. This must be called for any opcode
1237 : * which is JOF_TYPESET, and where either the script has not been analyzed
1238 : * by type inference or where the pc has type barriers. For simplicity, we
1239 : * always monitor JOF_TYPESET opcodes in the interpreter and stub calls,
1240 : * and only look at barriers when generating JIT code for the script.
1241 : */
1242 : static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
1243 : const js::Value& val);
1244 : static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
1245 : TypeSet::Type type);
1246 : static inline void Monitor(JSContext* cx, const js::Value& rval);
1247 :
1248 : static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
1249 : StackTypeSet* types, const js::Value& val);
1250 :
1251 : /* Monitor an assignment at a SETELEM on a non-integer identifier. */
1252 : static inline void MonitorAssign(JSContext* cx, HandleObject obj, jsid id);
1253 :
1254 : /* Add a type for a variable in a script. */
1255 : static inline void SetThis(JSContext* cx, JSScript* script, TypeSet::Type type);
1256 : static inline void SetThis(JSContext* cx, JSScript* script, const js::Value& value);
1257 : static inline void SetArgument(JSContext* cx, JSScript* script, unsigned arg,
1258 : TypeSet::Type type);
1259 : static inline void SetArgument(JSContext* cx, JSScript* script, unsigned arg,
1260 : const js::Value& value);
1261 :
1262 : /*
1263 : * Freeze all the stack type sets in a script, for a compilation. Returns
1264 : * copies of the type sets which will be checked against the actual ones
1265 : * under FinishCompilation, to detect any type changes.
1266 : */
1267 : static bool FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script,
1268 : TemporaryTypeSet** pThisTypes,
1269 : TemporaryTypeSet** pArgTypes,
1270 : TemporaryTypeSet** pBytecodeTypes);
1271 :
1272 : static void Purge(JSContext* cx, HandleScript script);
1273 :
1274 : void destroy();
1275 :
1276 0 : size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
1277 0 : return mallocSizeOf(this);
1278 : }
1279 :
1280 : #ifdef DEBUG
1281 : void printTypes(JSContext* cx, HandleScript script) const;
1282 : #endif
1283 : };
1284 :
1285 : void
1286 : FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap);
1287 :
1288 : class RecompileInfo;
1289 :
1290 : // Allocate a CompilerOutput for a finished compilation and generate the type
1291 : // constraints for the compilation. Sets |isValidOut| based on whether the type
1292 : // constraints still hold.
1293 : bool
1294 : FinishCompilation(JSContext* cx, HandleScript script, CompilerConstraintList* constraints,
1295 : RecompileInfo* precompileInfo, bool* isValidOut);
1296 :
1297 : // Reset any CompilerOutput present for a script.
1298 : void
1299 : InvalidateCompilerOutputsForScript(JSContext* cx, HandleScript script);
1300 :
1301 : // Update the actual types in any scripts queried by constraints with any
1302 : // speculative types added during the definite properties analysis.
1303 : void
1304 : FinishDefinitePropertiesAnalysis(JSContext* cx, CompilerConstraintList* constraints);
1305 :
1306 : // Representation of a heap type property which may or may not be instantiated.
1307 : // Heap properties for singleton types are instantiated lazily as they are used
1308 : // by the compiler, but this is only done on the active thread. If we are
1309 : // compiling off thread and use a property which has not yet been instantiated,
1310 : // it will be treated as empty and non-configured and will be instantiated when
1311 : // rejoining to the active thread. If it is in fact not empty, the compilation
1312 : // will fail; to avoid this, we try to instantiate singleton property types
1313 : // during generation of baseline caches.
1314 : class HeapTypeSetKey
1315 : {
1316 : friend class TypeSet::ObjectKey;
1317 :
1318 : // Object and property being accessed.
1319 : TypeSet::ObjectKey* object_;
1320 : jsid id_;
1321 :
1322 : // If instantiated, the underlying heap type set.
1323 : HeapTypeSet* maybeTypes_;
1324 :
1325 : public:
1326 1312 : HeapTypeSetKey()
1327 1312 : : object_(nullptr), id_(JSID_EMPTY), maybeTypes_(nullptr)
1328 1312 : {}
1329 :
1330 2352 : TypeSet::ObjectKey* object() const { return object_; }
1331 274 : jsid id() const { return id_; }
1332 :
1333 4404 : HeapTypeSet* maybeTypes() const {
1334 4404 : if (maybeTypes_)
1335 3329 : maybeTypes_->checkMagic();
1336 4404 : return maybeTypes_;
1337 : }
1338 :
1339 : bool instantiate(JSContext* cx);
1340 :
1341 : void freeze(CompilerConstraintList* constraints);
1342 : jit::MIRType knownMIRType(CompilerConstraintList* constraints);
1343 : bool nonData(CompilerConstraintList* constraints);
1344 : bool nonWritable(CompilerConstraintList* constraints);
1345 : bool isOwnProperty(CompilerConstraintList* constraints, bool allowEmptyTypesForGlobal = false);
1346 : bool knownSubset(CompilerConstraintList* constraints, const HeapTypeSetKey& other);
1347 : JSObject* singleton(CompilerConstraintList* constraints);
1348 : bool needsBarrier(CompilerConstraintList* constraints);
1349 : bool constant(CompilerConstraintList* constraints, Value* valOut);
1350 : bool couldBeConstant(CompilerConstraintList* constraints);
1351 : };
1352 :
1353 : struct AutoEnterAnalysis;
1354 :
1355 : class TypeZone
1356 : {
1357 : JS::Zone* const zone_;
1358 :
1359 : /* Pool for type information in this zone. */
1360 : static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
1361 : ZoneGroupData<LifoAlloc> typeLifoAlloc_;
1362 :
1363 : public:
1364 : // Current generation for sweeping.
1365 : ZoneGroupOrGCTaskOrIonCompileData<uint32_t> generation;
1366 :
1367 : /*
1368 : * All Ion compilations that have occured in this zone, for indexing via
1369 : * RecompileInfo. This includes both valid and invalid compilations, though
1370 : * invalidated compilations are swept on GC.
1371 : */
1372 : typedef Vector<CompilerOutput, 4, SystemAllocPolicy> CompilerOutputVector;
1373 : ZoneGroupData<CompilerOutputVector*> compilerOutputs;
1374 :
1375 : // During incremental sweeping, allocator holding the old type information
1376 : // for the zone.
1377 : ZoneGroupData<LifoAlloc> sweepTypeLifoAlloc;
1378 :
1379 : // During incremental sweeping, the old compiler outputs for use by
1380 : // recompile indexes with a stale generation.
1381 : ZoneGroupData<CompilerOutputVector*> sweepCompilerOutputs;
1382 :
1383 : // During incremental sweeping, whether to try to destroy all type
1384 : // information attached to scripts.
1385 : ZoneGroupData<bool> sweepReleaseTypes;
1386 :
1387 : ZoneGroupData<bool> sweepingTypes;
1388 :
1389 : // The topmost AutoEnterAnalysis on the stack, if there is one.
1390 : ZoneGroupData<AutoEnterAnalysis*> activeAnalysis;
1391 :
1392 : explicit TypeZone(JS::Zone* zone);
1393 : ~TypeZone();
1394 :
1395 0 : JS::Zone* zone() const { return zone_; }
1396 :
1397 85137 : LifoAlloc& typeLifoAlloc() {
1398 : #ifdef JS_CRASH_DIAGNOSTICS
1399 85137 : MOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone_));
1400 : #endif
1401 85137 : return typeLifoAlloc_.ref();
1402 : }
1403 :
1404 : void beginSweep(FreeOp* fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM& oom);
1405 : void endSweep(JSRuntime* rt);
1406 : void clearAllNewScriptsOnOOM();
1407 :
1408 : /* Mark a script as needing recompilation once inference has finished. */
1409 : void addPendingRecompile(JSContext* cx, const RecompileInfo& info);
1410 : void addPendingRecompile(JSContext* cx, JSScript* script);
1411 :
1412 : void processPendingRecompiles(FreeOp* fop, RecompileInfoVector& recompiles);
1413 :
1414 0 : void setSweepingTypes(bool sweeping) {
1415 0 : MOZ_RELEASE_ASSERT(sweepingTypes != sweeping);
1416 0 : sweepingTypes = sweeping;
1417 0 : }
1418 : };
1419 :
1420 : enum SpewChannel {
1421 : ISpewOps, /* ops: New constraints and types. */
1422 : ISpewResult, /* result: Final type sets. */
1423 : SPEW_COUNT
1424 : };
1425 :
1426 : #ifdef DEBUG
1427 :
1428 : bool InferSpewActive(SpewChannel channel);
1429 : const char * InferSpewColorReset();
1430 : const char * InferSpewColor(TypeConstraint* constraint);
1431 : const char * InferSpewColor(TypeSet* types);
1432 :
1433 : #define InferSpew(channel, ...) if (InferSpewActive(channel)) { InferSpewImpl(__VA_ARGS__); } else {}
1434 : void InferSpewImpl(const char* fmt, ...) MOZ_FORMAT_PRINTF(1, 2);
1435 :
1436 : /* Check that the type property for id in group contains value. */
1437 : bool ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Value& value);
1438 :
1439 : #else
1440 :
1441 : inline const char * InferSpewColorReset() { return nullptr; }
1442 : inline const char * InferSpewColor(TypeConstraint* constraint) { return nullptr; }
1443 : inline const char * InferSpewColor(TypeSet* types) { return nullptr; }
1444 :
1445 : #define InferSpew(channel, ...) do {} while (0)
1446 :
1447 : #endif
1448 :
1449 : // Prints type information for a context if spew is enabled or force is set.
1450 : void
1451 : PrintTypes(JSContext* cx, JSCompartment* comp, bool force);
1452 :
1453 : } /* namespace js */
1454 :
1455 : // JS::ubi::Nodes can point to object groups; they're js::gc::Cell instances
1456 : // with no associated compartment.
1457 : namespace JS {
1458 : namespace ubi {
1459 :
1460 : template<>
1461 : class Concrete<js::ObjectGroup> : TracerConcrete<js::ObjectGroup> {
1462 : protected:
1463 0 : explicit Concrete(js::ObjectGroup *ptr) : TracerConcrete<js::ObjectGroup>(ptr) { }
1464 :
1465 : public:
1466 0 : static void construct(void *storage, js::ObjectGroup *ptr) { new (storage) Concrete(ptr); }
1467 :
1468 : Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
1469 :
1470 0 : const char16_t* typeName() const override { return concreteTypeName; }
1471 : static const char16_t concreteTypeName[];
1472 : };
1473 :
1474 : } // namespace ubi
1475 : } // namespace JS
1476 :
1477 : #endif /* vm_TypeInference_h */
|