summaryrefslogtreecommitdiffstats
path: root/js/src/vm/TypeInference.h
blob: fd021fc96ca14af8c7d886a0cfc6d02ec4879d2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Definitions related to javascript type inference. */

#ifndef vm_TypeInference_h
#define vm_TypeInference_h

#include "mozilla/MemoryReporting.h"

#include "jsalloc.h"
#include "jsfriendapi.h"
#include "jstypes.h"

#include "ds/IdValuePair.h"
#include "ds/LifoAlloc.h"
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "jit/IonTypes.h"
#include "js/UbiNode.h"
#include "js/Utility.h"
#include "js/Vector.h"
#include "vm/TaggedProto.h"

namespace js {

namespace jit {
    struct IonScript;
    class JitAllocPolicy;
    class TempAllocator;
} // namespace jit

struct TypeZone;
class TypeConstraint;
class TypeNewScript;
class CompilerConstraintList;
class HeapTypeSetKey;

/*
 * Type inference memory management overview.
 *
 * Type information about the values observed within scripts and about the
 * contents of the heap is accumulated as the program executes. Compilation
 * accumulates constraints relating type information on the heap with the
 * compilations that should be invalidated when those types change. Type
 * information and constraints are allocated in the zone's typeLifoAlloc,
 * and on GC all data referring to live things is copied into a new allocator.
 * Thus, type set and constraints only hold weak references.
 */

/* Flags and other state stored in TypeSet::flags */
enum : uint32_t {
    TYPE_FLAG_UNDEFINED =   0x1,
    TYPE_FLAG_NULL      =   0x2,
    TYPE_FLAG_BOOLEAN   =   0x4,
    TYPE_FLAG_INT32     =   0x8,
    TYPE_FLAG_DOUBLE    =  0x10,
    TYPE_FLAG_STRING    =  0x20,
    TYPE_FLAG_SYMBOL    =  0x40,
    TYPE_FLAG_LAZYARGS  =  0x80,
    TYPE_FLAG_ANYOBJECT = 0x100,

    /* Mask containing all primitives */
    TYPE_FLAG_PRIMITIVE = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_BOOLEAN |
                          TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_STRING |
                          TYPE_FLAG_SYMBOL,

    /* Mask/shift for the number of objects in objectSet */
    TYPE_FLAG_OBJECT_COUNT_MASK     = 0x3e00,
    TYPE_FLAG_OBJECT_COUNT_SHIFT    = 9,
    TYPE_FLAG_OBJECT_COUNT_LIMIT    = 7,
    TYPE_FLAG_DOMOBJECT_COUNT_LIMIT =
        TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT,

    /* Whether the contents of this type set are totally unknown. */
    TYPE_FLAG_UNKNOWN             = 0x00004000,

    /* Mask of normal type flags on a type set. */
    TYPE_FLAG_BASE_MASK           = 0x000041ff,

    /* Additional flags for HeapTypeSet sets. */

    /*
     * Whether the property has ever been deleted or reconfigured to behave
     * differently from a plain data property, other than making the property
     * non-writable.
     */
    TYPE_FLAG_NON_DATA_PROPERTY = 0x00008000,

    /* Whether the property has ever been made non-writable. */
    TYPE_FLAG_NON_WRITABLE_PROPERTY = 0x00010000,

    /* Whether the property might not be constant. */
    TYPE_FLAG_NON_CONSTANT_PROPERTY = 0x00020000,

    /*
     * Whether the property is definitely in a particular slot on all objects
     * from which it has not been deleted or reconfigured. For singletons
     * this may be a fixed or dynamic slot, and for other objects this will be
     * a fixed slot.
     *
     * If the property is definite, mask and shift storing the slot + 1.
     * Otherwise these bits are clear.
     */
    TYPE_FLAG_DEFINITE_MASK       = 0xfffc0000,
    TYPE_FLAG_DEFINITE_SHIFT      = 18
};
typedef uint32_t TypeFlags;

/* Flags and other state stored in ObjectGroup::Flags */
enum : uint32_t {
    /* Whether this group is associated with some allocation site. */
    OBJECT_FLAG_FROM_ALLOCATION_SITE  = 0x1,

    /* Whether this group is associated with a single object. */
    OBJECT_FLAG_SINGLETON             = 0x2,

    /*
     * Whether this group is used by objects whose singleton groups have not
     * been created yet.
     */
    OBJECT_FLAG_LAZY_SINGLETON        = 0x4,

    /* Mask/shift for the number of properties in propertySet */
    OBJECT_FLAG_PROPERTY_COUNT_MASK   = 0xfff8,
    OBJECT_FLAG_PROPERTY_COUNT_SHIFT  = 3,
    OBJECT_FLAG_PROPERTY_COUNT_LIMIT  =
        OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT,

    /* Whether any objects this represents may have sparse indexes. */
    OBJECT_FLAG_SPARSE_INDEXES        = 0x00010000,

    /* Whether any objects this represents may not have packed dense elements. */
    OBJECT_FLAG_NON_PACKED            = 0x00020000,

    /*
     * Whether any objects this represents may be arrays whose length does not
     * fit in an int32.
     */
    OBJECT_FLAG_LENGTH_OVERFLOW       = 0x00040000,

    /* Whether any objects have been iterated over. */
    OBJECT_FLAG_ITERATED              = 0x00080000,

    /* Whether any object this represents may be frozen. */
    OBJECT_FLAG_FROZEN                = 0x00100000,

    /*
     * For the function on a run-once script, whether the function has actually
     * run multiple times.
     */
    OBJECT_FLAG_RUNONCE_INVALIDATED   = 0x00200000,

    /*
     * For a global object, whether any array buffers in this compartment with
     * typed object views have ever been detached.
     */
    OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER = 0x00400000,

    /*
     * Whether objects with this type should be allocated directly in the
     * tenured heap.
     */
    OBJECT_FLAG_PRE_TENURE            = 0x00800000,

    /* Whether objects with this type might have copy on write elements. */
    OBJECT_FLAG_COPY_ON_WRITE         = 0x01000000,

    /* Whether this type has had its 'new' script cleared in the past. */
    OBJECT_FLAG_NEW_SCRIPT_CLEARED    = 0x02000000,

    /*
     * Whether all properties of this object are considered unknown.
     * If set, all other flags in DYNAMIC_MASK will also be set.
     */
    OBJECT_FLAG_UNKNOWN_PROPERTIES    = 0x04000000,

    /* Flags which indicate dynamic properties of represented objects. */
    OBJECT_FLAG_DYNAMIC_MASK          = 0x07ff0000,

    // Mask/shift for the kind of addendum attached to this group.
    OBJECT_FLAG_ADDENDUM_MASK         = 0x38000000,
    OBJECT_FLAG_ADDENDUM_SHIFT        = 27,

    // Mask/shift for this group's generation. If out of sync with the
    // TypeZone's generation, this group hasn't been swept yet.
    OBJECT_FLAG_GENERATION_MASK       = 0x40000000,
    OBJECT_FLAG_GENERATION_SHIFT      = 30,
};
typedef uint32_t ObjectGroupFlags;

class StackTypeSet;
class HeapTypeSet;
class TemporaryTypeSet;

/*
 * Information about the set of types associated with an lvalue. There are
 * three kinds of type sets:
 *
 * - StackTypeSet are associated with TypeScripts, for arguments and values
 *   observed at property reads. These are implicitly frozen on compilation
 *   and only have constraints added to them which can trigger invalidation of
 *   TypeNewScript information.
 *
 * - HeapTypeSet are associated with the properties of ObjectGroups. These
 *   may have constraints added to them to trigger invalidation of either
 *   compiled code or TypeNewScript information.
 *
 * - TemporaryTypeSet are created during compilation and do not outlive
 *   that compilation.
 *
 * The contents of a type set completely describe the values that a particular
 * lvalue might have, except for the following cases:
 *
 * - If an object's prototype or class is dynamically mutated, its group will
 *   change. Type sets containing the old group will not necessarily contain
 *   the new group. When this occurs, the properties of the old and new group
 *   will both be marked as unknown, which will prevent Ion from optimizing
 *   based on the object's type information.
 *
 * - If an unboxed object is converted to a native object, its group will also
 *   change and type sets containing the old group will not necessarily contain
 *   the new group. Unlike the above case, this will not degrade property type
 *   information, but Ion will no longer optimize unboxed objects with the old
 *   group.
 */
class TypeSet
{
  public:
    // Type set entry for either a JSObject with singleton type or a
    // non-singleton ObjectGroup.
    class ObjectKey {
      public:
        static intptr_t keyBits(ObjectKey* obj) { return (intptr_t) obj; }
        static ObjectKey* getKey(ObjectKey* obj) { return obj; }

        static inline ObjectKey* get(JSObject* obj);
        static inline ObjectKey* get(ObjectGroup* group);

        bool isGroup() {
            return (uintptr_t(this) & 1) == 0;
        }
        bool isSingleton() {
            return (uintptr_t(this) & 1) != 0;
        }

        inline ObjectGroup* group();
        inline JSObject* singleton();

        inline ObjectGroup* groupNoBarrier();
        inline JSObject* singletonNoBarrier();

        const Class* clasp();
        TaggedProto proto();
        TypeNewScript* newScript();

        bool unknownProperties();
        bool hasFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
        bool hasStableClassAndProto(CompilerConstraintList* constraints);
        void watchStateChangeForInlinedCall(CompilerConstraintList* constraints);
        void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints);
        HeapTypeSetKey property(jsid id);
        void ensureTrackedProperty(JSContext* cx, jsid id);

        ObjectGroup* maybeGroup();

        JSCompartment* maybeCompartment();
    } JS_HAZ_GC_POINTER;

    // Information about a single concrete type. We pack this into one word,
    // where small values are particular primitive or other singleton types and
    // larger values are either specific JS objects or object groups.
    class Type
    {
        friend class TypeSet;

        uintptr_t data;
        explicit Type(uintptr_t data) : data(data) {}

      public:

        uintptr_t raw() const { return data; }

        bool isPrimitive() const {
            return data < JSVAL_TYPE_OBJECT;
        }

        bool isPrimitive(JSValueType type) const {
            MOZ_ASSERT(type < JSVAL_TYPE_OBJECT);
            return (uintptr_t) type == data;
        }

        JSValueType primitive() const {
            MOZ_ASSERT(isPrimitive());
            return (JSValueType) data;
        }

        bool isMagicArguments() const {
            return primitive() == JSVAL_TYPE_MAGIC;
        }

        bool isSomeObject() const {
            return data == JSVAL_TYPE_OBJECT || data > JSVAL_TYPE_UNKNOWN;
        }

        bool isAnyObject() const {
            return data == JSVAL_TYPE_OBJECT;
        }

        bool isUnknown() const {
            return data == JSVAL_TYPE_UNKNOWN;
        }

        /* Accessors for types that are either JSObject or ObjectGroup. */

        bool isObject() const {
            MOZ_ASSERT(!isAnyObject() && !isUnknown());
            return data > JSVAL_TYPE_UNKNOWN;
        }

        bool isObjectUnchecked() const {
            return data > JSVAL_TYPE_UNKNOWN;
        }

        inline ObjectKey* objectKey() const;

        /* Accessors for JSObject types */

        bool isSingleton() const {
            return isObject() && !!(data & 1);
        }
        bool isSingletonUnchecked() const {
            return isObjectUnchecked() && !!(data & 1);
        }

        inline JSObject* singleton() const;
        inline JSObject* singletonNoBarrier() const;

        /* Accessors for ObjectGroup types */

        bool isGroup() const {
            return isObject() && !(data & 1);
        }
        bool isGroupUnchecked() const {
            return isObjectUnchecked() && !(data & 1);
        }

        inline ObjectGroup* group() const;
        inline ObjectGroup* groupNoBarrier() const;

        inline void trace(JSTracer* trc);

        JSCompartment* maybeCompartment();

        bool operator == (Type o) const { return data == o.data; }
        bool operator != (Type o) const { return data != o.data; }
    } JS_HAZ_GC_POINTER;

    static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
    static inline Type NullType()      { return Type(JSVAL_TYPE_NULL); }
    static inline Type BooleanType()   { return Type(JSVAL_TYPE_BOOLEAN); }
    static inline Type Int32Type()     { return Type(JSVAL_TYPE_INT32); }
    static inline Type DoubleType()    { return Type(JSVAL_TYPE_DOUBLE); }
    static inline Type StringType()    { return Type(JSVAL_TYPE_STRING); }
    static inline Type SymbolType()    { return Type(JSVAL_TYPE_SYMBOL); }
    static inline Type MagicArgType()  { return Type(JSVAL_TYPE_MAGIC); }
    static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); }
    static inline Type UnknownType()   { return Type(JSVAL_TYPE_UNKNOWN); }

    static inline Type PrimitiveType(JSValueType type) {
        MOZ_ASSERT(type < JSVAL_TYPE_UNKNOWN);
        return Type(type);
    }

    static inline Type ObjectType(JSObject* obj);
    static inline Type ObjectType(ObjectGroup* group);
    static inline Type ObjectType(ObjectKey* key);

    static const char* NonObjectTypeString(Type type);

    static const char* TypeString(Type type);
    static const char* ObjectGroupString(ObjectGroup* group);

  protected:
    /* Flags for this type set. */
    TypeFlags flags;

    /* Possible objects this type set can represent. */
    ObjectKey** objectSet;

  public:

    TypeSet()
      : flags(0), objectSet(nullptr)
    {}

    void print(FILE* fp = stderr);

    /* Whether this set contains a specific type. */
    inline bool hasType(Type type) const;

    TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; }
    bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); }
    bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
    bool empty() const { return !baseFlags() && !baseObjectCount(); }

    bool hasAnyFlag(TypeFlags flags) const {
        MOZ_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags);
        return !!(baseFlags() & flags);
    }

    bool nonDataProperty() const {
        return flags & TYPE_FLAG_NON_DATA_PROPERTY;
    }
    bool nonWritableProperty() const {
        return flags & TYPE_FLAG_NON_WRITABLE_PROPERTY;
    }
    bool nonConstantProperty() const {
        return flags & TYPE_FLAG_NON_CONSTANT_PROPERTY;
    }
    bool definiteProperty() const { return flags & TYPE_FLAG_DEFINITE_MASK; }
    unsigned definiteSlot() const {
        MOZ_ASSERT(definiteProperty());
        return (flags >> TYPE_FLAG_DEFINITE_SHIFT) - 1;
    }

    /* Join two type sets into a new set. The result should not be modified further. */
    static TemporaryTypeSet* unionSets(TypeSet* a, TypeSet* b, LifoAlloc* alloc);
    /* Return the intersection of the 2 TypeSets. The result should not be modified further */
    static TemporaryTypeSet* intersectSets(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc);
    /*
     * Returns a copy of TypeSet a excluding/removing the types in TypeSet b.
     * TypeSet b can only contain primitives or be any object. No support for
     * specific objects. The result should not be modified further.
     */
    static TemporaryTypeSet* removeSet(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc);

    /* Add a type to this set using the specified allocator. */
    void addType(Type type, LifoAlloc* alloc);

    /* Get a list of all types in this set. */
    typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
    template <class TypeListT> bool enumerateTypes(TypeListT* list) const;

    /*
     * Iterate through the objects in this set. getObjectCount overapproximates
     * in the hash case (see SET_ARRAY_SIZE in TypeInference-inl.h), and
     * getObject may return nullptr.
     */
    inline unsigned getObjectCount() const;
    inline ObjectKey* getObject(unsigned i) const;
    inline JSObject* getSingleton(unsigned i) const;
    inline ObjectGroup* getGroup(unsigned i) const;
    inline JSObject* getSingletonNoBarrier(unsigned i) const;
    inline ObjectGroup* getGroupNoBarrier(unsigned i) const;

    /* The Class of an object in this set. */
    inline const Class* getObjectClass(unsigned i) const;

    bool canSetDefinite(unsigned slot) {
        // Note: the cast is required to work around an MSVC issue.
        return (slot + 1) <= (unsigned(TYPE_FLAG_DEFINITE_MASK) >> TYPE_FLAG_DEFINITE_SHIFT);
    }
    void setDefinite(unsigned slot) {
        MOZ_ASSERT(canSetDefinite(slot));
        flags |= ((slot + 1) << TYPE_FLAG_DEFINITE_SHIFT);
        MOZ_ASSERT(definiteSlot() == slot);
    }

    /* Whether any values in this set might have the specified type. */
    bool mightBeMIRType(jit::MIRType type) const;

    /*
     * Get whether this type set is known to be a subset of other.
     * This variant doesn't freeze constraints. That variant is called knownSubset
     */
    bool isSubset(const TypeSet* other) const;

    /*
     * Get whether the objects in this TypeSet are a subset of the objects
     * in other.
     */
    bool objectsAreSubset(TypeSet* other);

    /* Whether this TypeSet contains exactly the same types as other. */
    bool equals(const TypeSet* other) const {
        return this->isSubset(other) && other->isSubset(this);
    }

    bool objectsIntersect(const TypeSet* other) const;

    /* Forward all types in this set to the specified constraint. */
    bool addTypesToConstraint(JSContext* cx, TypeConstraint* constraint);

    // Clone a type set into an arbitrary allocator.
    TemporaryTypeSet* clone(LifoAlloc* alloc) const;

    // |*result| is not even partly initialized when this function is called:
    // this function placement-new's its contents into existence.
    bool cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const;

    // Create a new TemporaryTypeSet where undefined and/or null has been filtered out.
    TemporaryTypeSet* filter(LifoAlloc* alloc, bool filterUndefined, bool filterNull) const;
    // Create a new TemporaryTypeSet where the type has been set to object.
    TemporaryTypeSet* cloneObjectsOnly(LifoAlloc* alloc);
    TemporaryTypeSet* cloneWithoutObjects(LifoAlloc* alloc);

    JSCompartment* maybeCompartment();

    // Trigger a read barrier on all the contents of a type set.
    static void readBarrier(const TypeSet* types);

  protected:
    uint32_t baseObjectCount() const {
        return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
    }
    inline void setBaseObjectCount(uint32_t count);

    void clearObjects();

  public:
    static inline Type GetValueType(const Value& val);

    static inline bool IsUntrackedValue(const Value& val);

    // Get the type of a possibly optimized out or uninitialized let value.
    // This generally only happens on unconditional type monitors on bailing
    // out of Ion, such as for argument and local types.
    static inline Type GetMaybeUntrackedValueType(const Value& val);

    static bool IsTypeMarked(JSRuntime* rt, Type* v);
    static bool IsTypeAllocatedDuringIncremental(Type v);
    static bool IsTypeAboutToBeFinalized(Type* v);
} JS_HAZ_GC_POINTER;

/*
 * A constraint which listens to additions to a type set and propagates those
 * changes to other type sets.
 */
class TypeConstraint
{
public:
    /* Next constraint listening to the same type set. */
    TypeConstraint* next;

    TypeConstraint()
        : next(nullptr)
    {}

    /* Debugging name for this kind of constraint. */
    virtual const char* kind() = 0;

    /* Register a new type for the set this constraint is listening to. */
    virtual void newType(JSContext* cx, TypeSet* source, TypeSet::Type type) = 0;

    /*
     * For constraints attached to an object property's type set, mark the
     * property as having changed somehow.
     */
    virtual void newPropertyState(JSContext* cx, TypeSet* source) {}

    /*
     * For constraints attached to the JSID_EMPTY type set on an object,
     * indicate a change in one of the object's dynamic property flags or other
     * state.
     */
    virtual void newObjectState(JSContext* cx, ObjectGroup* group) {}

    /*
     * If the data this constraint refers to is still live, copy it into the
     * zone's new allocator. Type constraints only hold weak references.
     */
    virtual bool sweep(TypeZone& zone, TypeConstraint** res) = 0;

    /* The associated compartment, if any. */
    virtual JSCompartment* maybeCompartment() = 0;
};

// If there is an OOM while sweeping types, the type information is deoptimized
// so that it stays correct (i.e. overapproximates the possible types in the
// zone), but constraints might not have been triggered on the deoptimization
// or even copied over completely. In this case, destroy all JIT code and new
// script information in the zone, the only things whose correctness depends on
// the type constraints.
class AutoClearTypeInferenceStateOnOOM
{
    Zone* zone;
    bool oom;

  public:
    explicit AutoClearTypeInferenceStateOnOOM(Zone* zone)
      : zone(zone), oom(false)
    {}

    ~AutoClearTypeInferenceStateOnOOM();

    void setOOM() {
        oom = true;
    }
    bool hadOOM() const {
        return oom;
    }
};

/* Superclass common to stack and heap type sets. */
class ConstraintTypeSet : public TypeSet
{
  public:
    /* Chain of constraints which propagate changes out from this type set. */
    TypeConstraint* constraintList;

    ConstraintTypeSet() : constraintList(nullptr) {}

    /*
     * Add a type to this set, calling any constraint handlers if this is a new
     * possible type.
     */
    void addType(ExclusiveContext* cx, Type type);

    // Trigger a post barrier when writing to this set, if necessary.
    // addType(cx, type) takes care of this automatically.
    void postWriteBarrier(ExclusiveContext* cx, Type type);

    /* Add a new constraint to this set. */
    bool addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting = true);

    inline void sweep(JS::Zone* zone, AutoClearTypeInferenceStateOnOOM& oom);
    inline void trace(JS::Zone* zone, JSTracer* trc);
};

class StackTypeSet : public ConstraintTypeSet
{
  public:
};

class HeapTypeSet : public ConstraintTypeSet
{
    inline void newPropertyState(ExclusiveContext* cx);

  public:
    /* Mark this type set as representing a non-data property. */
    inline void setNonDataProperty(ExclusiveContext* cx);

    /* Mark this type set as representing a non-writable property. */
    inline void setNonWritableProperty(ExclusiveContext* cx);

    // Mark this type set as being non-constant.
    inline void setNonConstantProperty(ExclusiveContext* cx);
};

CompilerConstraintList*
NewCompilerConstraintList(jit::TempAllocator& alloc);

class TemporaryTypeSet : public TypeSet
{
  public:
    TemporaryTypeSet() {}
    TemporaryTypeSet(LifoAlloc* alloc, Type type);

    TemporaryTypeSet(uint32_t flags, ObjectKey** objectSet) {
        this->flags = flags;
        this->objectSet = objectSet;
    }

    TemporaryTypeSet(LifoAlloc* alloc, jit::MIRType type)
      : TemporaryTypeSet(alloc, PrimitiveType(ValueTypeFromMIRType(type)))
    {
        MOZ_ASSERT(type != jit::MIRType::Value);
    }

    /*
     * Constraints for JIT compilation.
     *
     * Methods for JIT compilation. These must be used when a script is
     * currently being compiled (see AutoEnterCompilation) and will add
     * constraints ensuring that if the return value change in the future due
     * to new type information, the script's jitcode will be discarded.
     */

    /* Get any type tag which all values in this set must have. */
    jit::MIRType getKnownMIRType();

    bool isMagicArguments() { return getKnownMIRType() == jit::MIRType::MagicOptimizedArguments; }

    /* Whether this value may be an object. */
    bool maybeObject() { return unknownObject() || baseObjectCount() > 0; }

    /*
     * Whether this typeset represents a potentially sentineled object value:
     * the value may be an object or null or undefined.
     * Returns false if the value cannot ever be an object.
     */
    bool objectOrSentinel() {
        TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT;
        if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK))
            return false;

        return hasAnyFlag(TYPE_FLAG_ANYOBJECT) || baseObjectCount() > 0;
    }

    /* Whether the type set contains objects with any of a set of flags. */
    bool hasObjectFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);

    /* Get the class shared by all objects in this set, or nullptr. */
    const Class* getKnownClass(CompilerConstraintList* constraints);

    /* Result returned from forAllClasses */
    enum ForAllResult {
        EMPTY=1,                // Set empty
        ALL_TRUE,               // Set not empty and predicate returned true for all classes
        ALL_FALSE,              // Set not empty and predicate returned false for all classes
        MIXED,                  // Set not empty and predicate returned false for some classes
                                // and true for others, or set contains an unknown or non-object
                                // type
    };

    /* Apply func to the members of the set and return an appropriate result.
     * The iteration may end early if the result becomes known early.
     */
    ForAllResult forAllClasses(CompilerConstraintList* constraints,
                               bool (*func)(const Class* clasp));

    /*
     * Returns true if all objects in this set have the same prototype, and
     * assigns this object to *proto. The proto can be nullptr.
     */
    bool getCommonPrototype(CompilerConstraintList* constraints, JSObject** proto);

    /* Whether the buffer mapped by a TypedArray is shared memory or not */
    enum TypedArraySharedness {
        UnknownSharedness=1,    // We can't determine sharedness
        KnownShared,            // We know for sure the buffer is shared
        KnownUnshared           // We know for sure the buffer is unshared
    };

    /* Get the typed array type of all objects in this set, or Scalar::MaxTypedArrayViewType.
     * If there is such a common type and sharedness is not nullptr then
     * *sharedness is set to what we know about the sharedness of the memory.
     */
    Scalar::Type getTypedArrayType(CompilerConstraintList* constraints,
                                   TypedArraySharedness* sharedness = nullptr);

    /* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */
    bool isDOMClass(CompilerConstraintList* constraints);

    /* Whether clasp->isCallable() is true for one or more objects in this set. */
    bool maybeCallable(CompilerConstraintList* constraints);

    /* Whether clasp->emulatesUndefined() is true for one or more objects in this set. */
    bool maybeEmulatesUndefined(CompilerConstraintList* constraints);

    /* Get the single value which can appear in this type set, otherwise nullptr. */
    JSObject* maybeSingleton();

    /* Whether any objects in the type set needs a barrier on id. */
    bool propertyNeedsBarrier(CompilerConstraintList* constraints, jsid id);

    /*
     * Whether this set contains all types in other, except (possibly) the
     * specified type.
     */
    bool filtersType(const TemporaryTypeSet* other, Type type) const;

    enum DoubleConversion {
        /* All types in the set should use eager double conversion. */
        AlwaysConvertToDoubles,

        /* Some types in the set should use eager double conversion. */
        MaybeConvertToDoubles,

        /* No types should use eager double conversion. */
        DontConvertToDoubles,

        /* Some types should use eager double conversion, others cannot. */
        AmbiguousDoubleConversion
    };

    /*
     * Whether known double optimizations are possible for element accesses on
     * objects in this type set.
     */
    DoubleConversion convertDoubleElements(CompilerConstraintList* constraints);

  private:
    void getTypedArraySharedness(CompilerConstraintList* constraints,
                                 TypedArraySharedness* sharedness);
};

// Stack class to record information about constraints that need to be added
// after finishing the Definite Properties Analysis. When the analysis succeeds,
// the |finishConstraints| method must be called to add the constraints to the
// TypeSets.
//
// There are two constraint types managed here:
//
//   1. Proto constraints for HeapTypeSets, to guard against things like getters
//      and setters on the proto chain.
//
//   2. Inlining constraints for StackTypeSets, to invalidate when additional
//      functions could be called at call sites where we inlined a function.
//
// This class uses bare GC-thing pointers because GC is suppressed when the
// analysis runs.
class MOZ_RAII DPAConstraintInfo {
  struct ProtoConstraint {
    JSObject* proto;
    jsid id;
    ProtoConstraint(JSObject* proto, jsid id) : proto(proto), id(id) {}
  };
  struct InliningConstraint {
    JSScript* caller;
    JSScript* callee;
    InliningConstraint(JSScript* caller, JSScript* callee)
        : caller(caller), callee(callee) {}
  };

  JS::AutoCheckCannotGC nogc_;
  Vector<ProtoConstraint, 8> protoConstraints_;
  Vector<InliningConstraint, 4> inliningConstraints_;

public:
  explicit DPAConstraintInfo(JSContext* cx)
      : nogc_(cx)
      , protoConstraints_(cx)
      , inliningConstraints_(cx)
  {
  }

  DPAConstraintInfo(const DPAConstraintInfo&) = delete;
  void operator=(const DPAConstraintInfo&) = delete;

  MOZ_MUST_USE bool addProtoConstraint(JSObject* proto, jsid id) {
    return protoConstraints_.emplaceBack(proto, id);
  }
  MOZ_MUST_USE bool addInliningConstraint(JSScript* caller, JSScript* callee) {
    return inliningConstraints_.emplaceBack(caller, callee);
  }

  MOZ_MUST_USE bool finishConstraints(JSContext* cx, ObjectGroup* group);
};

bool
AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx,
                                              DPAConstraintInfo& constraintInfo,
                                              ObjectGroup* group,
                                              HandleId id,
                                              bool* added);

bool
AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group,
                                     JSScript* script, JSScript* calleeScript);

// For groups where only a small number of objects have been allocated, this
// structure keeps track of all objects in the group. Once COUNT objects have
// been allocated, this structure is cleared and the objects are analyzed, to
// perform the new script properties analyses or determine if an unboxed
// representation can be used.
class PreliminaryObjectArray
{
  public:
    static const uint32_t COUNT = 20;

  private:
    // All objects with the type which have been allocated. The pointers in
    // this array are weak.
    JSObject* objects[COUNT] = {}; // zeroes

  public:
    PreliminaryObjectArray() = default;

    void registerNewObject(PlainObject* res);
    void unregisterObject(PlainObject* obj);

    JSObject* get(size_t i) const {
        MOZ_ASSERT(i < COUNT);
        return objects[i];
    }

    bool full() const;
    bool empty() const;
    void sweep();
};

class PreliminaryObjectArrayWithTemplate : public PreliminaryObjectArray
{
    HeapPtr<Shape*> shape_;

  public:
    explicit PreliminaryObjectArrayWithTemplate(Shape* shape)
      : shape_(shape)
    {}

    void clear() {
        shape_.init(nullptr);
    }

    Shape* shape() {
        return shape_;
    }

    void maybeAnalyze(ExclusiveContext* cx, ObjectGroup* group, bool force = false);

    void trace(JSTracer* trc);

    static void writeBarrierPre(PreliminaryObjectArrayWithTemplate* preliminaryObjects);
};

// New script properties analyses overview.
//
// When constructing objects using 'new' on a script, we attempt to determine
// the properties which that object will eventually have. This is done via two
// analyses. One of these, the definite properties analysis, is static, and the
// other, the acquired properties analysis, is dynamic. As objects are
// constructed using 'new' on some script to create objects of group G, our
// analysis strategy is as follows:
//
// - When the first objects are created, no analysis is immediately performed.
//   Instead, all objects of group G are accumulated in an array.
//
// - After a certain number of such objects have been created, the definite
//   properties analysis is performed. This analyzes the body of the
//   constructor script and any other functions it calls to look for properties
//   which will definitely be added by the constructor in a particular order,
//   creating an object with shape S.
//
// - The properties in S are compared with the greatest common prefix P of the
//   shapes of the objects that have been created. If P has more properties
//   than S, the acquired properties analysis is performed.
//
// - The acquired properties analysis marks all properties in P as definite
//   in G, and creates a new group IG for objects which are partially
//   initialized. Objects of group IG are initially created with shape S, and if
//   they are later given shape P, their group can be changed to G.
//
// For objects which are rarely created, the definite properties analysis can
// be triggered after only one or a few objects have been allocated, when code
// being Ion compiled might access them. In this case type information in the
// constructor might not be good enough for the definite properties analysis to
// compute useful information, but the acquired properties analysis will still
// be able to identify definite properties in this case.
//
// This layered approach is designed to maximize performance on easily
// analyzable code, while still allowing us to determine definite properties
// robustly when code consistently adds the same properties to objects, but in
// complex ways which can't be understood statically.
class TypeNewScript
{
  public:
    struct Initializer {
        enum Kind {
            SETPROP,
            SETPROP_FRAME,
            DONE
        } kind;
        uint32_t offset;
        Initializer(Kind kind, uint32_t offset)
          : kind(kind), offset(offset)
        {}
    };

  private:
    // Scripted function which this information was computed for.
    HeapPtr<JSFunction*> function_ = {};

    // Any preliminary objects with the type. The analyses are not performed
    // until this array is cleared.
    PreliminaryObjectArray* preliminaryObjects = nullptr;

    // After the new script properties analyses have been performed, a template
    // object to use for newly constructed objects. The shape of this object
    // reflects all definite properties the object will have, and the
    // allocation kind to use. This is null if the new objects have an unboxed
    // layout, in which case the UnboxedLayout provides the initial structure
    // of the object.
    HeapPtr<PlainObject*> templateObject_ = {};

    // Order in which definite properties become initialized. We need this in
    // case the definite properties are invalidated (such as by adding a setter
    // to an object on the prototype chain) while an object is in the middle of
    // being initialized, so we can walk the stack and fixup any objects which
    // look for in-progress objects which were prematurely set with an incorrect
    // shape. Property assignments in inner frames are preceded by a series of
    // SETPROP_FRAME entries specifying the stack down to the frame containing
    // the write.
    Initializer* initializerList = nullptr;

    // If there are additional properties found by the acquired properties
    // analysis which were not found by the definite properties analysis, this
    // shape contains all such additional properties (plus the definite
    // properties). When an object of this group acquires this shape, it is
    // fully initialized and its group can be changed to initializedGroup.
    HeapPtr<Shape*> initializedShape_ = {};

    // Group with definite properties set for all properties found by
    // both the definite and acquired properties analyses.
    HeapPtr<ObjectGroup*> initializedGroup_ = {};

  public:
    TypeNewScript() = default;
    ~TypeNewScript() {
        js_delete(preliminaryObjects);
        js_free(initializerList);
    }

    void clear() {
        function_.init(nullptr);
        templateObject_.init(nullptr);
        initializedShape_.init(nullptr);
        initializedGroup_.init(nullptr);
    }

    static void writeBarrierPre(TypeNewScript* newScript);

    bool analyzed() const {
        return preliminaryObjects == nullptr;
    }

    PlainObject* templateObject() const {
        return templateObject_;
    }

    Shape* initializedShape() const {
        return initializedShape_;
    }

    ObjectGroup* initializedGroup() const {
        return initializedGroup_;
    }

    JSFunction* function() const {
        return function_;
    }

    void trace(JSTracer* trc);
    void sweep();

    void registerNewObject(PlainObject* res);
    bool maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force = false);

    bool rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* group);

    static bool make(JSContext* cx, ObjectGroup* group, JSFunction* fun);
    static TypeNewScript* makeNativeVersion(JSContext* cx, TypeNewScript* newScript,
                                            PlainObject* templateObject);

    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
};

/* Is this a reasonable PC to be doing inlining on? */
inline bool isInlinableCall(jsbytecode* pc);

bool
ClassCanHaveExtraProperties(const Class* clasp);

/* Persistent type information for a script, retained across GCs. */
class TypeScript
{
    friend class ::JSScript;

    // Variable-size array
    StackTypeSet typeArray_[1];

  public:
    /* Array of type sets for variables and JOF_TYPESET ops. */
    StackTypeSet* typeArray() const {
        // Ensure typeArray_ is the last data member of TypeScript.
        JS_STATIC_ASSERT(sizeof(TypeScript) ==
                         sizeof(typeArray_) + offsetof(TypeScript, typeArray_));
        return const_cast<StackTypeSet*>(typeArray_);
    }

    static inline size_t SizeIncludingTypeArray(size_t arraySize) {
        // Ensure typeArray_ is the last data member of TypeScript.
        JS_STATIC_ASSERT(sizeof(TypeScript) ==
            sizeof(StackTypeSet) + offsetof(TypeScript, typeArray_));
        return offsetof(TypeScript, typeArray_) + arraySize * sizeof(StackTypeSet);
    }

    static inline unsigned NumTypeSets(JSScript* script);

    static inline StackTypeSet* ThisTypes(JSScript* script);
    static inline StackTypeSet* ArgTypes(JSScript* script, unsigned i);

    /* Get the type set for values observed at an opcode. */
    static inline StackTypeSet* BytecodeTypes(JSScript* script, jsbytecode* pc);

    template <typename TYPESET>
    static inline TYPESET* BytecodeTypes(JSScript* script, jsbytecode* pc, uint32_t* bytecodeMap,
                                         uint32_t* hint, TYPESET* typeArray);

    /*
     * Monitor a bytecode pushing any value. This must be called for any opcode
     * which is JOF_TYPESET, and where either the script has not been analyzed
     * by type inference or where the pc has type barriers. For simplicity, we
     * always monitor JOF_TYPESET opcodes in the interpreter and stub calls,
     * and only look at barriers when generating JIT code for the script.
     */
    static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
                               const js::Value& val);
    static inline void Monitor(JSContext* cx, JSScript* script, jsbytecode* pc,
                               TypeSet::Type type);
    static inline void Monitor(JSContext* cx, const js::Value& rval);

    /* Monitor an assignment at a SETELEM on a non-integer identifier. */
    static inline void MonitorAssign(JSContext* cx, HandleObject obj, jsid id);

    /* Add a type for a variable in a script. */
    static inline void SetThis(JSContext* cx, JSScript* script, TypeSet::Type type);
    static inline void SetThis(JSContext* cx, JSScript* script, const js::Value& value);
    static inline void SetArgument(JSContext* cx, JSScript* script, unsigned arg,
                                   TypeSet::Type type);
    static inline void SetArgument(JSContext* cx, JSScript* script, unsigned arg,
                                   const js::Value& value);

    /*
     * Freeze all the stack type sets in a script, for a compilation. Returns
     * copies of the type sets which will be checked against the actual ones
     * under FinishCompilation, to detect any type changes.
     */
    static bool FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script,
                               TemporaryTypeSet** pThisTypes,
                               TemporaryTypeSet** pArgTypes,
                               TemporaryTypeSet** pBytecodeTypes);

    static void Purge(JSContext* cx, HandleScript script);

    void destroy();

    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
        return mallocSizeOf(this);
    }

#ifdef DEBUG
    void printTypes(JSContext* cx, HandleScript script) const;
#endif
};

void
FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap);

class RecompileInfo;

// Allocate a CompilerOutput for a finished compilation and generate the type
// constraints for the compilation. Sets |isValidOut| based on whether the type
// constraints still hold.
bool
FinishCompilation(JSContext* cx, HandleScript script, CompilerConstraintList* constraints,
                  RecompileInfo* precompileInfo, bool* isValidOut);

// Update the actual types in any scripts queried by constraints with any
// speculative types added during the definite properties analysis.
void
FinishDefinitePropertiesAnalysis(JSContext* cx, CompilerConstraintList* constraints);

// Representation of a heap type property which may or may not be instantiated.
// Heap properties for singleton types are instantiated lazily as they are used
// by the compiler, but this is only done on the main thread. If we are
// compiling off thread and use a property which has not yet been instantiated,
// it will be treated as empty and non-configured and will be instantiated when
// rejoining to the main thread. If it is in fact not empty, the compilation
// will fail; to avoid this, we try to instantiate singleton property types
// during generation of baseline caches.
class HeapTypeSetKey
{
    friend class TypeSet::ObjectKey;

    // Object and property being accessed.
    TypeSet::ObjectKey* object_;
    jsid id_;

    // If instantiated, the underlying heap type set.
    HeapTypeSet* maybeTypes_;

  public:
    HeapTypeSetKey()
      : object_(nullptr), id_(JSID_EMPTY), maybeTypes_(nullptr)
    {}

    TypeSet::ObjectKey* object() const { return object_; }
    jsid id() const { return id_; }
    HeapTypeSet* maybeTypes() const { return maybeTypes_; }

    bool instantiate(JSContext* cx);

    void freeze(CompilerConstraintList* constraints);
    jit::MIRType knownMIRType(CompilerConstraintList* constraints);
    bool nonData(CompilerConstraintList* constraints);
    bool nonWritable(CompilerConstraintList* constraints);
    bool isOwnProperty(CompilerConstraintList* constraints, bool allowEmptyTypesForGlobal = false);
    bool knownSubset(CompilerConstraintList* constraints, const HeapTypeSetKey& other);
    JSObject* singleton(CompilerConstraintList* constraints);
    bool needsBarrier(CompilerConstraintList* constraints);
    bool constant(CompilerConstraintList* constraints, Value* valOut);
    bool couldBeConstant(CompilerConstraintList* constraints);
};

/*
 * Information about the result of the compilation of a script.  This structure
 * stored in the TypeCompartment is indexed by the RecompileInfo. This
 * indirection enables the invalidation of all constraints related to the same
 * compilation.
 */
class CompilerOutput
{
    // If this compilation has not been invalidated, the associated script and
    // kind of compilation being performed.
    JSScript* script_;

    // Whether this compilation is about to be invalidated.
    bool pendingInvalidation_ : 1;

    // During sweeping, the list of compiler outputs is compacted and invalidated
    // outputs are removed. This gives the new index for a valid compiler output.
    uint32_t sweepIndex_ : 31;

  public:
    static const uint32_t INVALID_SWEEP_INDEX = static_cast<uint32_t>(1 << 31) - 1;

    CompilerOutput()
      : script_(nullptr),
        pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
    {}

    explicit CompilerOutput(JSScript* script)
      : script_(script),
        pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
    {}

    JSScript* script() const { return script_; }

    inline jit::IonScript* ion() const;

    bool isValid() const {
        return script_ != nullptr;
    }
    void invalidate() {
        script_ = nullptr;
    }

    void setPendingInvalidation() {
        pendingInvalidation_ = true;
    }
    bool pendingInvalidation() {
        return pendingInvalidation_;
    }

    void setSweepIndex(uint32_t index) {
        if (index >= INVALID_SWEEP_INDEX)
            MOZ_CRASH();
        sweepIndex_ = index;
    }
    uint32_t sweepIndex() {
        MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
        return sweepIndex_;
    }
};

class RecompileInfo
{
    // Index in the TypeZone's compilerOutputs or sweepCompilerOutputs arrays,
    // depending on the generation value.
    uint32_t outputIndex : 31;

    // If out of sync with the TypeZone's generation, this index is for the
    // zone's sweepCompilerOutputs rather than compilerOutputs.
    uint32_t generation : 1;

  public:
    RecompileInfo(uint32_t outputIndex, uint32_t generation)
      : outputIndex(outputIndex), generation(generation)
    {}

    RecompileInfo()
      : outputIndex(JS_BITMASK(31)), generation(0)
    {}

    CompilerOutput* compilerOutput(TypeZone& types) const;
    CompilerOutput* compilerOutput(JSContext* cx) const;
    bool shouldSweep(TypeZone& types);
};

// The RecompileInfoVector has a MinInlineCapacity of one so that invalidating a
// single IonScript doesn't require an allocation.
typedef Vector<RecompileInfo, 1, SystemAllocPolicy> RecompileInfoVector;

struct AutoEnterAnalysis;

struct TypeZone
{
    JS::Zone* zone_;

    /* Pool for type information in this zone. */
    static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
    LifoAlloc typeLifoAlloc;

    // Current generation for sweeping.
    uint32_t generation : 1;

    /*
     * All Ion compilations that have occured in this zone, for indexing via
     * RecompileInfo. This includes both valid and invalid compilations, though
     * invalidated compilations are swept on GC.
     */
    typedef Vector<CompilerOutput, 4, SystemAllocPolicy> CompilerOutputVector;
    CompilerOutputVector* compilerOutputs;

    // During incremental sweeping, allocator holding the old type information
    // for the zone.
    LifoAlloc sweepTypeLifoAlloc;

    // During incremental sweeping, the old compiler outputs for use by
    // recompile indexes with a stale generation.
    CompilerOutputVector* sweepCompilerOutputs;

    // During incremental sweeping, whether to try to destroy all type
    // information attached to scripts.
    bool sweepReleaseTypes;

    // The topmost AutoEnterAnalysis on the stack, if there is one.
    AutoEnterAnalysis* activeAnalysis;

    explicit TypeZone(JS::Zone* zone);
    ~TypeZone();

    JS::Zone* zone() const { return zone_; }

    void beginSweep(FreeOp* fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM& oom);
    void endSweep(JSRuntime* rt);
    void clearAllNewScriptsOnOOM();

    /* Mark a script as needing recompilation once inference has finished. */
    void addPendingRecompile(JSContext* cx, const RecompileInfo& info);
    void addPendingRecompile(JSContext* cx, JSScript* script);

    void processPendingRecompiles(FreeOp* fop, RecompileInfoVector& recompiles);
};

enum SpewChannel {
    ISpewOps,      /* ops: New constraints and types. */
    ISpewResult,   /* result: Final type sets. */
    SPEW_COUNT
};

#ifdef DEBUG

bool InferSpewActive(SpewChannel channel);
const char * InferSpewColorReset();
const char * InferSpewColor(TypeConstraint* constraint);
const char * InferSpewColor(TypeSet* types);

#define InferSpew(channel, ...) if (InferSpewActive(channel)) { InferSpewImpl(__VA_ARGS__); } else {}
void InferSpewImpl(const char* fmt, ...) MOZ_FORMAT_PRINTF(1, 2);

/* Check that the type property for id in group contains value. */
bool ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Value& value);

#else

inline const char * InferSpewColorReset() { return nullptr; }
inline const char * InferSpewColor(TypeConstraint* constraint) { return nullptr; }
inline const char * InferSpewColor(TypeSet* types) { return nullptr; }

#define InferSpew(channel, ...) do {} while (0)

#endif

// Prints type information for a context if spew is enabled or force is set.
void
PrintTypes(JSContext* cx, JSCompartment* comp, bool force);

} /* namespace js */

// JS::ubi::Nodes can point to object groups; they're js::gc::Cell instances
// with no associated compartment.
namespace JS {
namespace ubi {

template<>
class Concrete<js::ObjectGroup> : TracerConcrete<js::ObjectGroup> {
  protected:
    explicit Concrete(js::ObjectGroup *ptr) : TracerConcrete<js::ObjectGroup>(ptr) { }

  public:
    static void construct(void *storage, js::ObjectGroup *ptr) { new (storage) Concrete(ptr); }

    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;

    const char16_t* typeName() const override { return concreteTypeName; }
    static const char16_t concreteTypeName[];
};

} // namespace ubi
} // namespace JS

#endif /* vm_TypeInference_h */