js/src/jsinfer.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:a343b8cd2144
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 #include "jsinferinlines.h"
8
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/PodOperations.h"
12
13 #include "jsapi.h"
14 #include "jscntxt.h"
15 #include "jsgc.h"
16 #include "jshashutil.h"
17 #include "jsobj.h"
18 #include "jsprf.h"
19 #include "jsscript.h"
20 #include "jsstr.h"
21 #include "jsworkers.h"
22 #include "prmjtime.h"
23
24 #include "gc/Marking.h"
25 #ifdef JS_ION
26 #include "jit/BaselineJIT.h"
27 #include "jit/Ion.h"
28 #include "jit/IonAnalysis.h"
29 #include "jit/JitCompartment.h"
30 #endif
31 #include "js/MemoryMetrics.h"
32 #include "vm/Opcodes.h"
33 #include "vm/Shape.h"
34
35 #include "jsatominlines.h"
36 #include "jsgcinlines.h"
37 #include "jsobjinlines.h"
38 #include "jsscriptinlines.h"
39
40 #include "jit/ExecutionMode-inl.h"
41
42 using namespace js;
43 using namespace js::gc;
44 using namespace js::types;
45 using namespace js::analyze;
46
47 using mozilla::DebugOnly;
48 using mozilla::Maybe;
49 using mozilla::PodArrayZero;
50 using mozilla::PodCopy;
51 using mozilla::PodZero;
52
53 static inline jsid
54 id_prototype(JSContext *cx) {
55 return NameToId(cx->names().prototype);
56 }
57
58 static inline jsid
59 id___proto__(JSContext *cx) {
60 return NameToId(cx->names().proto);
61 }
62
63 static inline jsid
64 id_constructor(JSContext *cx) {
65 return NameToId(cx->names().constructor);
66 }
67
68 static inline jsid
69 id_caller(JSContext *cx) {
70 return NameToId(cx->names().caller);
71 }
72
73 #ifdef DEBUG
74 const char *
75 types::TypeIdStringImpl(jsid id)
76 {
77 if (JSID_IS_VOID(id))
78 return "(index)";
79 if (JSID_IS_EMPTY(id))
80 return "(new)";
81 static char bufs[4][100];
82 static unsigned which = 0;
83 which = (which + 1) & 3;
84 PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0);
85 return bufs[which];
86 }
87 #endif
88
89 /////////////////////////////////////////////////////////////////////
90 // Logging
91 /////////////////////////////////////////////////////////////////////
92
93 #ifdef DEBUG
94
95 static bool InferSpewActive(SpewChannel channel)
96 {
97 static bool active[SPEW_COUNT];
98 static bool checked = false;
99 if (!checked) {
100 checked = true;
101 PodArrayZero(active);
102 const char *env = getenv("INFERFLAGS");
103 if (!env)
104 return false;
105 if (strstr(env, "ops"))
106 active[ISpewOps] = true;
107 if (strstr(env, "result"))
108 active[ISpewResult] = true;
109 if (strstr(env, "full")) {
110 for (unsigned i = 0; i < SPEW_COUNT; i++)
111 active[i] = true;
112 }
113 }
114 return active[channel];
115 }
116
117 static bool InferSpewColorable()
118 {
119 /* Only spew colors on xterm-color to not screw up emacs. */
120 static bool colorable = false;
121 static bool checked = false;
122 if (!checked) {
123 checked = true;
124 const char *env = getenv("TERM");
125 if (!env)
126 return false;
127 if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
128 colorable = true;
129 }
130 return colorable;
131 }
132
133 const char *
134 types::InferSpewColorReset()
135 {
136 if (!InferSpewColorable())
137 return "";
138 return "\x1b[0m";
139 }
140
141 const char *
142 types::InferSpewColor(TypeConstraint *constraint)
143 {
144 /* Type constraints are printed out using foreground colors. */
145 static const char * const colors[] = { "\x1b[31m", "\x1b[32m", "\x1b[33m",
146 "\x1b[34m", "\x1b[35m", "\x1b[36m",
147 "\x1b[37m" };
148 if (!InferSpewColorable())
149 return "";
150 return colors[DefaultHasher<TypeConstraint *>::hash(constraint) % 7];
151 }
152
153 const char *
154 types::InferSpewColor(TypeSet *types)
155 {
156 /* Type sets are printed out using bold colors. */
157 static const char * const colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m",
158 "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m",
159 "\x1b[1;37m" };
160 if (!InferSpewColorable())
161 return "";
162 return colors[DefaultHasher<TypeSet *>::hash(types) % 7];
163 }
164
165 const char *
166 types::TypeString(Type type)
167 {
168 if (type.isPrimitive()) {
169 switch (type.primitive()) {
170 case JSVAL_TYPE_UNDEFINED:
171 return "void";
172 case JSVAL_TYPE_NULL:
173 return "null";
174 case JSVAL_TYPE_BOOLEAN:
175 return "bool";
176 case JSVAL_TYPE_INT32:
177 return "int";
178 case JSVAL_TYPE_DOUBLE:
179 return "float";
180 case JSVAL_TYPE_STRING:
181 return "string";
182 case JSVAL_TYPE_MAGIC:
183 return "lazyargs";
184 default:
185 MOZ_ASSUME_UNREACHABLE("Bad type");
186 }
187 }
188 if (type.isUnknown())
189 return "unknown";
190 if (type.isAnyObject())
191 return " object";
192
193 static char bufs[4][40];
194 static unsigned which = 0;
195 which = (which + 1) & 3;
196
197 if (type.isSingleObject())
198 JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleObject());
199 else
200 JS_snprintf(bufs[which], 40, "[0x%p]", (void *) type.typeObject());
201
202 return bufs[which];
203 }
204
205 const char *
206 types::TypeObjectString(TypeObject *type)
207 {
208 return TypeString(Type::ObjectType(type));
209 }
210
211 unsigned JSScript::id() {
212 if (!id_) {
213 id_ = ++compartment()->types.scriptCount;
214 InferSpew(ISpewOps, "script #%u: %p %s:%d",
215 id_, this, filename() ? filename() : "<null>", lineno());
216 }
217 return id_;
218 }
219
220 void
221 types::InferSpew(SpewChannel channel, const char *fmt, ...)
222 {
223 if (!InferSpewActive(channel))
224 return;
225
226 va_list ap;
227 va_start(ap, fmt);
228 fprintf(stderr, "[infer] ");
229 vfprintf(stderr, fmt, ap);
230 fprintf(stderr, "\n");
231 va_end(ap);
232 }
233
234 bool
235 types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value)
236 {
237 /*
238 * Check the correctness of the type information in the object's property
239 * against an actual value.
240 */
241 if (!obj->unknownProperties() && !value.isUndefined()) {
242 id = IdToTypeId(id);
243
244 /* Watch for properties which inference does not monitor. */
245 if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
246 return true;
247
248 Type type = GetValueType(value);
249
250 AutoEnterAnalysis enter(cx);
251
252 /*
253 * We don't track types for properties inherited from prototypes which
254 * haven't yet been accessed during analysis of the inheriting object.
255 * Don't do the property instantiation now.
256 */
257 TypeSet *types = obj->maybeGetProperty(id);
258 if (!types)
259 return true;
260
261 if (!types->hasType(type)) {
262 TypeFailure(cx, "Missing type in object %s %s: %s",
263 TypeObjectString(obj), TypeIdString(id), TypeString(type));
264 }
265 }
266 return true;
267 }
268
269 #endif
270
271 void
272 types::TypeFailure(JSContext *cx, const char *fmt, ...)
273 {
274 char msgbuf[1024]; /* Larger error messages will be truncated */
275 char errbuf[1024];
276
277 va_list ap;
278 va_start(ap, fmt);
279 JS_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
280 va_end(ap);
281
282 JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf);
283
284 /* Dump type state, even if INFERFLAGS is unset. */
285 cx->compartment()->types.print(cx, true);
286
287 MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
288 MOZ_CRASH();
289 }
290
291 /////////////////////////////////////////////////////////////////////
292 // TypeSet
293 /////////////////////////////////////////////////////////////////////
294
295 TemporaryTypeSet::TemporaryTypeSet(Type type)
296 {
297 if (type.isUnknown()) {
298 flags |= TYPE_FLAG_BASE_MASK;
299 } else if (type.isPrimitive()) {
300 flags = PrimitiveTypeFlag(type.primitive());
301 if (flags == TYPE_FLAG_DOUBLE)
302 flags |= TYPE_FLAG_INT32;
303 } else if (type.isAnyObject()) {
304 flags |= TYPE_FLAG_ANYOBJECT;
305 } else if (type.isTypeObject() && type.typeObject()->unknownProperties()) {
306 flags |= TYPE_FLAG_ANYOBJECT;
307 } else {
308 setBaseObjectCount(1);
309 objectSet = reinterpret_cast<TypeObjectKey**>(type.objectKey());
310 }
311 }
312
313 bool
314 TypeSet::mightBeMIRType(jit::MIRType type)
315 {
316 if (unknown())
317 return true;
318
319 if (type == jit::MIRType_Object)
320 return unknownObject() || baseObjectCount() != 0;
321
322 switch (type) {
323 case jit::MIRType_Undefined:
324 return baseFlags() & TYPE_FLAG_UNDEFINED;
325 case jit::MIRType_Null:
326 return baseFlags() & TYPE_FLAG_NULL;
327 case jit::MIRType_Boolean:
328 return baseFlags() & TYPE_FLAG_BOOLEAN;
329 case jit::MIRType_Int32:
330 return baseFlags() & TYPE_FLAG_INT32;
331 case jit::MIRType_Float32: // Fall through, there's no JSVAL for Float32.
332 case jit::MIRType_Double:
333 return baseFlags() & TYPE_FLAG_DOUBLE;
334 case jit::MIRType_String:
335 return baseFlags() & TYPE_FLAG_STRING;
336 case jit::MIRType_MagicOptimizedArguments:
337 return baseFlags() & TYPE_FLAG_LAZYARGS;
338 case jit::MIRType_MagicHole:
339 case jit::MIRType_MagicIsConstructing:
340 // These magic constants do not escape to script and are not observed
341 // in the type sets.
342 //
343 // The reason we can return false here is subtle: if Ion is asking the
344 // type set if it has seen such a magic constant, then the MIR in
345 // question is the most generic type, MIRType_Value. A magic constant
346 // could only be emitted by a MIR of MIRType_Value if that MIR is a
347 // phi, and we check that different magic constants do not flow to the
348 // same join point in GuessPhiType.
349 return false;
350 default:
351 MOZ_ASSUME_UNREACHABLE("Bad MIR type");
352 }
353 }
354
355 bool
356 TypeSet::isSubset(TypeSet *other)
357 {
358 if ((baseFlags() & other->baseFlags()) != baseFlags())
359 return false;
360
361 if (unknownObject()) {
362 JS_ASSERT(other->unknownObject());
363 } else {
364 for (unsigned i = 0; i < getObjectCount(); i++) {
365 TypeObjectKey *obj = getObject(i);
366 if (!obj)
367 continue;
368 if (!other->hasType(Type::ObjectType(obj)))
369 return false;
370 }
371 }
372
373 return true;
374 }
375
376 bool
377 TypeSet::enumerateTypes(TypeList *list)
378 {
379 /* If any type is possible, there's no need to worry about specifics. */
380 if (flags & TYPE_FLAG_UNKNOWN)
381 return list->append(Type::UnknownType());
382
383 /* Enqueue type set members stored as bits. */
384 for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
385 if (flags & flag) {
386 Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
387 if (!list->append(type))
388 return false;
389 }
390 }
391
392 /* If any object is possible, skip specifics. */
393 if (flags & TYPE_FLAG_ANYOBJECT)
394 return list->append(Type::AnyObjectType());
395
396 /* Enqueue specific object types. */
397 unsigned count = getObjectCount();
398 for (unsigned i = 0; i < count; i++) {
399 TypeObjectKey *object = getObject(i);
400 if (object) {
401 if (!list->append(Type::ObjectType(object)))
402 return false;
403 }
404 }
405
406 return true;
407 }
408
409 inline bool
410 TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint)
411 {
412 /*
413 * Build all types in the set into a vector before triggering the
414 * constraint, as doing so may modify this type set.
415 */
416 TypeList types;
417 if (!enumerateTypes(&types))
418 return false;
419
420 for (unsigned i = 0; i < types.length(); i++)
421 constraint->newType(cx, this, types[i]);
422
423 return true;
424 }
425
426 bool
427 ConstraintTypeSet::addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting)
428 {
429 if (!constraint) {
430 /* OOM failure while constructing the constraint. */
431 return false;
432 }
433
434 JS_ASSERT(cx->compartment()->activeAnalysis);
435
436 InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s",
437 InferSpewColor(this), this, InferSpewColorReset(),
438 InferSpewColor(constraint), constraint, InferSpewColorReset(),
439 constraint->kind());
440
441 JS_ASSERT(constraint->next == nullptr);
442 constraint->next = constraintList;
443 constraintList = constraint;
444
445 if (callExisting)
446 return addTypesToConstraint(cx, constraint);
447 return true;
448 }
449
450 void
451 TypeSet::clearObjects()
452 {
453 setBaseObjectCount(0);
454 objectSet = nullptr;
455 }
456
457 void
458 TypeSet::addType(Type type, LifoAlloc *alloc)
459 {
460 if (unknown())
461 return;
462
463 if (type.isUnknown()) {
464 flags |= TYPE_FLAG_BASE_MASK;
465 clearObjects();
466 JS_ASSERT(unknown());
467 return;
468 }
469
470 if (type.isPrimitive()) {
471 TypeFlags flag = PrimitiveTypeFlag(type.primitive());
472 if (flags & flag)
473 return;
474
475 /* If we add float to a type set it is also considered to contain int. */
476 if (flag == TYPE_FLAG_DOUBLE)
477 flag |= TYPE_FLAG_INT32;
478
479 flags |= flag;
480 return;
481 }
482
483 if (flags & TYPE_FLAG_ANYOBJECT)
484 return;
485 if (type.isAnyObject())
486 goto unknownObject;
487
488 {
489 uint32_t objectCount = baseObjectCount();
490 TypeObjectKey *object = type.objectKey();
491 TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
492 (*alloc, objectSet, objectCount, object);
493 if (!pentry)
494 goto unknownObject;
495 if (*pentry)
496 return;
497 *pentry = object;
498
499 setBaseObjectCount(objectCount);
500
501 if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
502 goto unknownObject;
503 }
504
505 if (type.isTypeObject()) {
506 TypeObject *nobject = type.typeObject();
507 JS_ASSERT(!nobject->singleton());
508 if (nobject->unknownProperties())
509 goto unknownObject;
510 }
511
512 if (false) {
513 unknownObject:
514 flags |= TYPE_FLAG_ANYOBJECT;
515 clearObjects();
516 }
517 }
518
519 void
520 ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
521 {
522 JS_ASSERT(cxArg->compartment()->activeAnalysis);
523
524 if (hasType(type))
525 return;
526
527 TypeSet::addType(type, &cxArg->typeLifoAlloc());
528
529 if (type.isObjectUnchecked() && unknownObject())
530 type = Type::AnyObjectType();
531
532 InferSpew(ISpewOps, "addType: %sT%p%s %s",
533 InferSpewColor(this), this, InferSpewColorReset(),
534 TypeString(type));
535
536 /* Propagate the type to all constraints. */
537 if (JSContext *cx = cxArg->maybeJSContext()) {
538 TypeConstraint *constraint = constraintList;
539 while (constraint) {
540 constraint->newType(cx, this, type);
541 constraint = constraint->next;
542 }
543 } else {
544 JS_ASSERT(!constraintList);
545 }
546 }
547
548 void
549 TypeSet::print()
550 {
551 if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
552 fprintf(stderr, " [non-data]");
553
554 if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY)
555 fprintf(stderr, " [non-writable]");
556
557 if (definiteProperty())
558 fprintf(stderr, " [definite:%d]", definiteSlot());
559
560 if (baseFlags() == 0 && !baseObjectCount()) {
561 fprintf(stderr, " missing");
562 return;
563 }
564
565 if (flags & TYPE_FLAG_UNKNOWN)
566 fprintf(stderr, " unknown");
567 if (flags & TYPE_FLAG_ANYOBJECT)
568 fprintf(stderr, " object");
569
570 if (flags & TYPE_FLAG_UNDEFINED)
571 fprintf(stderr, " void");
572 if (flags & TYPE_FLAG_NULL)
573 fprintf(stderr, " null");
574 if (flags & TYPE_FLAG_BOOLEAN)
575 fprintf(stderr, " bool");
576 if (flags & TYPE_FLAG_INT32)
577 fprintf(stderr, " int");
578 if (flags & TYPE_FLAG_DOUBLE)
579 fprintf(stderr, " float");
580 if (flags & TYPE_FLAG_STRING)
581 fprintf(stderr, " string");
582 if (flags & TYPE_FLAG_LAZYARGS)
583 fprintf(stderr, " lazyargs");
584
585 uint32_t objectCount = baseObjectCount();
586 if (objectCount) {
587 fprintf(stderr, " object[%u]", objectCount);
588
589 unsigned count = getObjectCount();
590 for (unsigned i = 0; i < count; i++) {
591 TypeObjectKey *object = getObject(i);
592 if (object)
593 fprintf(stderr, " %s", TypeString(Type::ObjectType(object)));
594 }
595 }
596 }
597
598 bool
599 TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const
600 {
601 JS_ASSERT(result->empty());
602
603 unsigned objectCount = baseObjectCount();
604 unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
605
606 TypeObjectKey **newSet;
607 if (capacity) {
608 newSet = alloc->newArray<TypeObjectKey*>(capacity);
609 if (!newSet)
610 return false;
611 PodCopy(newSet, objectSet, capacity);
612 }
613
614 new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet);
615 return true;
616 }
617
618 TemporaryTypeSet *
619 TypeSet::clone(LifoAlloc *alloc) const
620 {
621 TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>();
622 if (!res || !clone(alloc, res))
623 return nullptr;
624 return res;
625 }
626
627 TemporaryTypeSet *
628 TypeSet::filter(LifoAlloc *alloc, bool filterUndefined, bool filterNull) const
629 {
630 TemporaryTypeSet *res = clone(alloc);
631 if (!res)
632 return nullptr;
633
634 if (filterUndefined)
635 res->flags = res->flags & ~TYPE_FLAG_UNDEFINED;
636
637 if (filterNull)
638 res->flags = res->flags & ~TYPE_FLAG_NULL;
639
640 return res;
641 }
642
643 /* static */ TemporaryTypeSet *
644 TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc)
645 {
646 TemporaryTypeSet *res = alloc->new_<TemporaryTypeSet>(a->baseFlags() | b->baseFlags(),
647 static_cast<TypeObjectKey**>(nullptr));
648 if (!res)
649 return nullptr;
650
651 if (!res->unknownObject()) {
652 for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) {
653 if (TypeObjectKey *key = a->getObject(i))
654 res->addType(Type::ObjectType(key), alloc);
655 }
656 for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
657 if (TypeObjectKey *key = b->getObject(i))
658 res->addType(Type::ObjectType(key), alloc);
659 }
660 }
661
662 return res;
663 }
664
665 /////////////////////////////////////////////////////////////////////
666 // Compiler constraints
667 /////////////////////////////////////////////////////////////////////
668
669 // Compiler constraints overview
670 //
671 // Constraints generated during Ion compilation capture assumptions made about
672 // heap properties that will trigger invalidation of the resulting Ion code if
673 // the constraint is violated. Constraints can only be attached to type sets on
674 // the main thread, so to allow compilation to occur almost entirely off thread
675 // the generation is split into two phases.
676 //
677 // During compilation, CompilerConstraint values are constructed in a list,
678 // recording the heap property type set which was read from and its expected
679 // contents, along with the assumption made about those contents.
680 //
681 // At the end of compilation, when linking the result on the main thread, the
682 // list of compiler constraints are read and converted to type constraints and
683 // attached to the type sets. If the property type sets have changed so that the
684 // assumptions no longer hold then the compilation is aborted and its result
685 // discarded.
686
687 // Superclass of all constraints generated during Ion compilation. These may
688 // be allocated off the main thread, using the current Ion context's allocator.
689 class CompilerConstraint
690 {
691 public:
692 // Property being queried by the compiler.
693 HeapTypeSetKey property;
694
695 // Contents of the property at the point when the query was performed. This
696 // may differ from the actual property types later in compilation as the
697 // main thread performs side effects.
698 TemporaryTypeSet *expected;
699
700 CompilerConstraint(LifoAlloc *alloc, const HeapTypeSetKey &property)
701 : property(property),
702 expected(property.maybeTypes() ? property.maybeTypes()->clone(alloc) : nullptr)
703 {}
704
705 // Generate the type constraint recording the assumption made by this
706 // compilation. Returns true if the assumption originally made still holds.
707 virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0;
708 };
709
710 class types::CompilerConstraintList
711 {
712 public:
713 struct FrozenScript
714 {
715 JSScript *script;
716 TemporaryTypeSet *thisTypes;
717 TemporaryTypeSet *argTypes;
718 TemporaryTypeSet *bytecodeTypes;
719 };
720
721 private:
722
723 // OOM during generation of some constraint.
724 bool failed_;
725
726 #ifdef JS_ION
727 // Allocator used for constraints.
728 LifoAlloc *alloc_;
729
730 // Constraints generated on heap properties.
731 Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints;
732
733 // Scripts whose stack type sets were frozen for the compilation.
734 Vector<FrozenScript, 1, jit::IonAllocPolicy> frozenScripts;
735 #endif
736
737 public:
738 CompilerConstraintList(jit::TempAllocator &alloc)
739 : failed_(false)
740 #ifdef JS_ION
741 , alloc_(alloc.lifoAlloc())
742 , constraints(alloc)
743 , frozenScripts(alloc)
744 #endif
745 {}
746
747 void add(CompilerConstraint *constraint) {
748 #ifdef JS_ION
749 if (!constraint || !constraints.append(constraint))
750 setFailed();
751 #else
752 MOZ_CRASH();
753 #endif
754 }
755
756 void freezeScript(JSScript *script,
757 TemporaryTypeSet *thisTypes,
758 TemporaryTypeSet *argTypes,
759 TemporaryTypeSet *bytecodeTypes)
760 {
761 #ifdef JS_ION
762 FrozenScript entry;
763 entry.script = script;
764 entry.thisTypes = thisTypes;
765 entry.argTypes = argTypes;
766 entry.bytecodeTypes = bytecodeTypes;
767 if (!frozenScripts.append(entry))
768 setFailed();
769 #else
770 MOZ_CRASH();
771 #endif
772 }
773
774 size_t length() {
775 #ifdef JS_ION
776 return constraints.length();
777 #else
778 MOZ_CRASH();
779 #endif
780 }
781
782 CompilerConstraint *get(size_t i) {
783 #ifdef JS_ION
784 return constraints[i];
785 #else
786 MOZ_CRASH();
787 #endif
788 }
789
790 size_t numFrozenScripts() {
791 #ifdef JS_ION
792 return frozenScripts.length();
793 #else
794 MOZ_CRASH();
795 #endif
796 }
797
798 const FrozenScript &frozenScript(size_t i) {
799 #ifdef JS_ION
800 return frozenScripts[i];
801 #else
802 MOZ_CRASH();
803 #endif
804 }
805
806 bool failed() {
807 return failed_;
808 }
809 void setFailed() {
810 failed_ = true;
811 }
812 LifoAlloc *alloc() const {
813 #ifdef JS_ION
814 return alloc_;
815 #else
816 MOZ_CRASH();
817 #endif
818 }
819 };
820
821 CompilerConstraintList *
822 types::NewCompilerConstraintList(jit::TempAllocator &alloc)
823 {
824 #ifdef JS_ION
825 return alloc.lifoAlloc()->new_<CompilerConstraintList>(alloc);
826 #else
827 MOZ_CRASH();
828 #endif
829 }
830
831 /* static */ bool
832 TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script,
833 TemporaryTypeSet **pThisTypes,
834 TemporaryTypeSet **pArgTypes,
835 TemporaryTypeSet **pBytecodeTypes)
836 {
837 LifoAlloc *alloc = constraints->alloc();
838 StackTypeSet *existing = script->types->typeArray();
839
840 size_t count = NumTypeSets(script);
841 TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
842 if (!types)
843 return false;
844 PodZero(types, count);
845
846 for (size_t i = 0; i < count; i++) {
847 if (!existing[i].clone(alloc, &types[i]))
848 return false;
849 }
850
851 *pThisTypes = types + (ThisTypes(script) - existing);
852 *pArgTypes = (script->functionNonDelazifying() && script->functionNonDelazifying()->nargs())
853 ? (types + (ArgTypes(script, 0) - existing))
854 : nullptr;
855 *pBytecodeTypes = types;
856
857 constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes);
858 return true;
859 }
860
861 namespace {
862
863 template <typename T>
864 class CompilerConstraintInstance : public CompilerConstraint
865 {
866 T data;
867
868 public:
869 CompilerConstraintInstance<T>(LifoAlloc *alloc, const HeapTypeSetKey &property, const T &data)
870 : CompilerConstraint(alloc, property), data(data)
871 {}
872
873 bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo);
874 };
875
876 // Constraint generated from a CompilerConstraint when linking the compilation.
877 template <typename T>
878 class TypeCompilerConstraint : public TypeConstraint
879 {
880 // Compilation which this constraint may invalidate.
881 RecompileInfo compilation;
882
883 T data;
884
885 public:
886 TypeCompilerConstraint<T>(RecompileInfo compilation, const T &data)
887 : compilation(compilation), data(data)
888 {}
889
890 const char *kind() { return data.kind(); }
891
892 void newType(JSContext *cx, TypeSet *source, Type type) {
893 if (data.invalidateOnNewType(type))
894 cx->zone()->types.addPendingRecompile(cx, compilation);
895 }
896
897 void newPropertyState(JSContext *cx, TypeSet *source) {
898 if (data.invalidateOnNewPropertyState(source))
899 cx->zone()->types.addPendingRecompile(cx, compilation);
900 }
901
902 void newObjectState(JSContext *cx, TypeObject *object) {
903 // Note: Once the object has unknown properties, no more notifications
904 // will be sent on changes to its state, so always invalidate any
905 // associated compilations.
906 if (object->unknownProperties() || data.invalidateOnNewObjectState(object))
907 cx->zone()->types.addPendingRecompile(cx, compilation);
908 }
909
910 bool sweep(TypeZone &zone, TypeConstraint **res) {
911 if (data.shouldSweep() || compilation.shouldSweep(zone))
912 return false;
913 *res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data);
914 return true;
915 }
916 };
917
918 template <typename T>
919 bool
920 CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo)
921 {
922 if (property.object()->unknownProperties())
923 return false;
924
925 if (!property.instantiate(cx))
926 return false;
927
928 if (!data.constraintHolds(cx, property, expected))
929 return false;
930
931 return property.maybeTypes()->addConstraint(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
932 /* callExisting = */ false);
933 }
934
935 } /* anonymous namespace */
936
937 const Class *
938 TypeObjectKey::clasp()
939 {
940 return isTypeObject() ? asTypeObject()->clasp() : asSingleObject()->getClass();
941 }
942
943 TaggedProto
944 TypeObjectKey::proto()
945 {
946 JS_ASSERT(hasTenuredProto());
947 return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
948 }
949
950 bool
951 ObjectImpl::hasTenuredProto() const
952 {
953 return type_->hasTenuredProto();
954 }
955
956 bool
957 TypeObjectKey::hasTenuredProto()
958 {
959 return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto();
960 }
961
962 JSObject *
963 TypeObjectKey::singleton()
964 {
965 return isTypeObject() ? asTypeObject()->singleton() : asSingleObject();
966 }
967
968 TypeNewScript *
969 TypeObjectKey::newScript()
970 {
971 if (isTypeObject() && asTypeObject()->hasNewScript())
972 return asTypeObject()->newScript();
973 return nullptr;
974 }
975
976 TypeObject *
977 TypeObjectKey::maybeType()
978 {
979 if (isTypeObject())
980 return asTypeObject();
981 if (asSingleObject()->hasLazyType())
982 return nullptr;
983 return asSingleObject()->type();
984 }
985
986 bool
987 TypeObjectKey::unknownProperties()
988 {
989 if (TypeObject *type = maybeType())
990 return type->unknownProperties();
991 return false;
992 }
993
994 HeapTypeSetKey
995 TypeObjectKey::property(jsid id)
996 {
997 JS_ASSERT(!unknownProperties());
998
999 HeapTypeSetKey property;
1000 property.object_ = this;
1001 property.id_ = id;
1002 if (TypeObject *type = maybeType())
1003 property.maybeTypes_ = type->maybeGetProperty(id);
1004
1005 return property;
1006 }
1007
1008 void
1009 TypeObjectKey::ensureTrackedProperty(JSContext *cx, jsid id)
1010 {
1011 #ifdef JS_ION
1012 // If we are accessing a lazily defined property which actually exists in
1013 // the VM and has not been instantiated yet, instantiate it now if we are
1014 // on the main thread and able to do so.
1015 if (!JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) {
1016 JS_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
1017 if (JSObject *obj = singleton()) {
1018 if (obj->isNative() && obj->nativeLookupPure(id))
1019 EnsureTrackPropertyTypes(cx, obj, id);
1020 }
1021 }
1022 #endif // JS_ION
1023 }
1024
1025 bool
1026 HeapTypeSetKey::instantiate(JSContext *cx)
1027 {
1028 if (maybeTypes())
1029 return true;
1030 if (object()->isSingleObject() && !object()->asSingleObject()->getType(cx)) {
1031 cx->clearPendingException();
1032 return false;
1033 }
1034 maybeTypes_ = object()->maybeType()->getProperty(cx, id());
1035 return maybeTypes_ != nullptr;
1036 }
1037
1038 static bool
1039 CheckFrozenTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual)
1040 {
1041 // Return whether the types frozen for a script during compilation are
1042 // still valid. Also check for any new types added to the frozen set during
1043 // compilation, and add them to the actual stack type sets. These new types
1044 // indicate places where the compiler relaxed its possible inputs to be
1045 // more tolerant of potential new types.
1046
1047 if (!actual->isSubset(frozen))
1048 return false;
1049
1050 if (!frozen->isSubset(actual)) {
1051 TypeSet::TypeList list;
1052 frozen->enumerateTypes(&list);
1053
1054 for (size_t i = 0; i < list.length(); i++)
1055 actual->addType(cx, list[i]);
1056 }
1057
1058 return true;
1059 }
1060
1061 namespace {
1062
1063 /*
1064 * As for TypeConstraintFreeze, but describes an implicit freeze constraint
1065 * added for stack types within a script. Applies to all compilations of the
1066 * script, not just a single one.
1067 */
1068 class TypeConstraintFreezeStack : public TypeConstraint
1069 {
1070 JSScript *script_;
1071
1072 public:
1073 TypeConstraintFreezeStack(JSScript *script)
1074 : script_(script)
1075 {}
1076
1077 const char *kind() { return "freezeStack"; }
1078
1079 void newType(JSContext *cx, TypeSet *source, Type type) {
1080 /*
1081 * Unlike TypeConstraintFreeze, triggering this constraint once does
1082 * not disable it on future changes to the type set.
1083 */
1084 cx->zone()->types.addPendingRecompile(cx, script_);
1085 }
1086
1087 bool sweep(TypeZone &zone, TypeConstraint **res) {
1088 if (IsScriptAboutToBeFinalized(&script_))
1089 return false;
1090 *res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_);
1091 return true;
1092 }
1093 };
1094
1095 } /* anonymous namespace */
1096
1097 bool
1098 types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode executionMode,
1099 CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
1100 {
1101 if (constraints->failed())
1102 return false;
1103
1104 CompilerOutput co(script, executionMode);
1105
1106 TypeZone &types = cx->zone()->types;
1107 if (!types.compilerOutputs) {
1108 types.compilerOutputs = cx->new_< Vector<CompilerOutput> >(cx);
1109 if (!types.compilerOutputs)
1110 return false;
1111 }
1112
1113 #ifdef DEBUG
1114 for (size_t i = 0; i < types.compilerOutputs->length(); i++) {
1115 const CompilerOutput &co = (*types.compilerOutputs)[i];
1116 JS_ASSERT_IF(co.isValid(), co.script() != script || co.mode() != executionMode);
1117 }
1118 #endif
1119
1120 uint32_t index = types.compilerOutputs->length();
1121 if (!types.compilerOutputs->append(co))
1122 return false;
1123
1124 *precompileInfo = RecompileInfo(index);
1125
1126 bool succeeded = true;
1127
1128 for (size_t i = 0; i < constraints->length(); i++) {
1129 CompilerConstraint *constraint = constraints->get(i);
1130 if (!constraint->generateTypeConstraint(cx, *precompileInfo))
1131 succeeded = false;
1132 }
1133
1134 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1135 const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
1136 JS_ASSERT(entry.script->types);
1137
1138 if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script)))
1139 succeeded = false;
1140 unsigned nargs = entry.script->functionNonDelazifying()
1141 ? entry.script->functionNonDelazifying()->nargs()
1142 : 0;
1143 for (size_t i = 0; i < nargs; i++) {
1144 if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], types::TypeScript::ArgTypes(entry.script, i)))
1145 succeeded = false;
1146 }
1147 for (size_t i = 0; i < entry.script->nTypeSets(); i++) {
1148 if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types->typeArray()[i]))
1149 succeeded = false;
1150 }
1151
1152 // If necessary, add constraints to trigger invalidation on the script
1153 // after any future changes to the stack type sets.
1154 if (entry.script->hasFreezeConstraints())
1155 continue;
1156 entry.script->setHasFreezeConstraints();
1157
1158 size_t count = TypeScript::NumTypeSets(entry.script);
1159
1160 StackTypeSet *array = entry.script->types->typeArray();
1161 for (size_t i = 0; i < count; i++) {
1162 if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false))
1163 succeeded = false;
1164 }
1165 }
1166
1167 if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) {
1168 types.compilerOutputs->back().invalidate();
1169 script->resetUseCount();
1170 return false;
1171 }
1172
1173 return true;
1174 }
1175
1176 static void
1177 CheckDefinitePropertiesTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual)
1178 {
1179 // The definite properties analysis happens on the main thread, so no new
1180 // types can have been added to actual. The analysis may have updated the
1181 // contents of |frozen| though with new speculative types, and these need
1182 // to be reflected in |actual| for AddClearDefiniteFunctionUsesInScript
1183 // to work.
1184 if (!frozen->isSubset(actual)) {
1185 TypeSet::TypeList list;
1186 frozen->enumerateTypes(&list);
1187
1188 for (size_t i = 0; i < list.length(); i++)
1189 actual->addType(cx, list[i]);
1190 }
1191 }
1192
1193 void
1194 types::FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *constraints)
1195 {
1196 #ifdef DEBUG
1197 // Assert no new types have been added to the StackTypeSets. Do this before
1198 // calling CheckDefinitePropertiesTypeSet, as it may add new types to the
1199 // StackTypeSets and break these invariants if a script is inlined more
1200 // than once. See also CheckDefinitePropertiesTypeSet.
1201 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1202 const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
1203 JSScript *script = entry.script;
1204 JS_ASSERT(script->types);
1205
1206 JS_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes));
1207
1208 unsigned nargs = entry.script->functionNonDelazifying()
1209 ? entry.script->functionNonDelazifying()->nargs()
1210 : 0;
1211 for (size_t j = 0; j < nargs; j++)
1212 JS_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j]));
1213
1214 for (size_t j = 0; j < script->nTypeSets(); j++)
1215 JS_ASSERT(script->types->typeArray()[j].isSubset(&entry.bytecodeTypes[j]));
1216 }
1217 #endif
1218
1219 for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
1220 const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
1221 JSScript *script = entry.script;
1222 JS_ASSERT(script->types);
1223
1224 if (!script->types)
1225 MOZ_CRASH();
1226
1227 CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script));
1228
1229 unsigned nargs = script->functionNonDelazifying()
1230 ? script->functionNonDelazifying()->nargs()
1231 : 0;
1232 for (size_t j = 0; j < nargs; j++)
1233 CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j));
1234
1235 for (size_t j = 0; j < script->nTypeSets(); j++)
1236 CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types->typeArray()[j]);
1237 }
1238 }
1239
1240 namespace {
1241
1242 // Constraint which triggers recompilation of a script if any type is added to a type set. */
1243 class ConstraintDataFreeze
1244 {
1245 public:
1246 ConstraintDataFreeze() {}
1247
1248 const char *kind() { return "freeze"; }
1249
1250 bool invalidateOnNewType(Type type) { return true; }
1251 bool invalidateOnNewPropertyState(TypeSet *property) { return true; }
1252 bool invalidateOnNewObjectState(TypeObject *object) { return false; }
1253
1254 bool constraintHolds(JSContext *cx,
1255 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
1256 {
1257 return expected
1258 ? property.maybeTypes()->isSubset(expected)
1259 : property.maybeTypes()->empty();
1260 }
1261
1262 bool shouldSweep() { return false; }
1263 };
1264
1265 } /* anonymous namespace */
1266
1267 void
1268 HeapTypeSetKey::freeze(CompilerConstraintList *constraints)
1269 {
1270 LifoAlloc *alloc = constraints->alloc();
1271
1272 typedef CompilerConstraintInstance<ConstraintDataFreeze> T;
1273 constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataFreeze()));
1274 }
1275
1276 static inline jit::MIRType
1277 GetMIRTypeFromTypeFlags(TypeFlags flags)
1278 {
1279 switch (flags) {
1280 case TYPE_FLAG_UNDEFINED:
1281 return jit::MIRType_Undefined;
1282 case TYPE_FLAG_NULL:
1283 return jit::MIRType_Null;
1284 case TYPE_FLAG_BOOLEAN:
1285 return jit::MIRType_Boolean;
1286 case TYPE_FLAG_INT32:
1287 return jit::MIRType_Int32;
1288 case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
1289 return jit::MIRType_Double;
1290 case TYPE_FLAG_STRING:
1291 return jit::MIRType_String;
1292 case TYPE_FLAG_LAZYARGS:
1293 return jit::MIRType_MagicOptimizedArguments;
1294 case TYPE_FLAG_ANYOBJECT:
1295 return jit::MIRType_Object;
1296 default:
1297 return jit::MIRType_Value;
1298 }
1299 }
1300
1301 jit::MIRType
1302 TemporaryTypeSet::getKnownMIRType()
1303 {
1304 TypeFlags flags = baseFlags();
1305 jit::MIRType type;
1306
1307 if (baseObjectCount())
1308 type = flags ? jit::MIRType_Value : jit::MIRType_Object;
1309 else
1310 type = GetMIRTypeFromTypeFlags(flags);
1311
1312 /*
1313 * If the type set is totally empty then it will be treated as unknown,
1314 * but we still need to record the dependency as adding a new type can give
1315 * it a definite type tag. This is not needed if there are enough types
1316 * that the exact tag is unknown, as it will stay unknown as more types are
1317 * added to the set.
1318 */
1319 DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
1320 JS_ASSERT_IF(empty, type == jit::MIRType_Value);
1321
1322 return type;
1323 }
1324
1325 jit::MIRType
1326 HeapTypeSetKey::knownMIRType(CompilerConstraintList *constraints)
1327 {
1328 TypeSet *types = maybeTypes();
1329
1330 if (!types || types->unknown())
1331 return jit::MIRType_Value;
1332
1333 TypeFlags flags = types->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
1334 jit::MIRType type;
1335
1336 if (types->unknownObject() || types->getObjectCount())
1337 type = flags ? jit::MIRType_Value : jit::MIRType_Object;
1338 else
1339 type = GetMIRTypeFromTypeFlags(flags);
1340
1341 if (type != jit::MIRType_Value)
1342 freeze(constraints);
1343
1344 /*
1345 * If the type set is totally empty then it will be treated as unknown,
1346 * but we still need to record the dependency as adding a new type can give
1347 * it a definite type tag. This is not needed if there are enough types
1348 * that the exact tag is unknown, as it will stay unknown as more types are
1349 * added to the set.
1350 */
1351 JS_ASSERT_IF(types->empty(), type == jit::MIRType_Value);
1352
1353 return type;
1354 }
1355
1356 bool
1357 HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints)
1358 {
1359 if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
1360 return true;
1361 if (JSObject *obj = object()->singleton()) {
1362 if (CanHaveEmptyPropertyTypesForOwnProperty(obj))
1363 return true;
1364 }
1365 freeze(constraints);
1366 return false;
1367 }
1368
1369 bool
1370 HeapTypeSetKey::knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other)
1371 {
1372 if (!maybeTypes() || maybeTypes()->empty()) {
1373 freeze(constraints);
1374 return true;
1375 }
1376 if (!other.maybeTypes() || !maybeTypes()->isSubset(other.maybeTypes()))
1377 return false;
1378 freeze(constraints);
1379 return true;
1380 }
1381
1382 JSObject *
1383 TemporaryTypeSet::getSingleton()
1384 {
1385 if (baseFlags() != 0 || baseObjectCount() != 1)
1386 return nullptr;
1387
1388 return getSingleObject(0);
1389 }
1390
1391 JSObject *
1392 HeapTypeSetKey::singleton(CompilerConstraintList *constraints)
1393 {
1394 HeapTypeSet *types = maybeTypes();
1395
1396 if (!types || types->nonDataProperty() || types->baseFlags() != 0 || types->getObjectCount() != 1)
1397 return nullptr;
1398
1399 JSObject *obj = types->getSingleObject(0);
1400
1401 if (obj)
1402 freeze(constraints);
1403
1404 return obj;
1405 }
1406
1407 bool
1408 HeapTypeSetKey::needsBarrier(CompilerConstraintList *constraints)
1409 {
1410 TypeSet *types = maybeTypes();
1411 if (!types)
1412 return false;
1413 bool result = types->unknownObject()
1414 || types->getObjectCount() > 0
1415 || types->hasAnyFlag(TYPE_FLAG_STRING);
1416 if (!result)
1417 freeze(constraints);
1418 return result;
1419 }
1420
1421 namespace {
1422
1423 // Constraint which triggers recompilation if an object acquires particular flags.
1424 class ConstraintDataFreezeObjectFlags
1425 {
1426 public:
1427 // Flags we are watching for on this object.
1428 TypeObjectFlags flags;
1429
1430 ConstraintDataFreezeObjectFlags(TypeObjectFlags flags)
1431 : flags(flags)
1432 {
1433 JS_ASSERT(flags);
1434 }
1435
1436 const char *kind() { return "freezeObjectFlags"; }
1437
1438 bool invalidateOnNewType(Type type) { return false; }
1439 bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
1440 bool invalidateOnNewObjectState(TypeObject *object) {
1441 return object->hasAnyFlags(flags);
1442 }
1443
1444 bool constraintHolds(JSContext *cx,
1445 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
1446 {
1447 return !invalidateOnNewObjectState(property.object()->maybeType());
1448 }
1449
1450 bool shouldSweep() { return false; }
1451 };
1452
1453 } /* anonymous namespace */
1454
1455 bool
1456 TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
1457 {
1458 JS_ASSERT(flags);
1459
1460 if (TypeObject *type = maybeType()) {
1461 if (type->hasAnyFlags(flags))
1462 return true;
1463 }
1464
1465 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1466 LifoAlloc *alloc = constraints->alloc();
1467
1468 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T;
1469 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(flags)));
1470 return false;
1471 }
1472
1473 bool
1474 TemporaryTypeSet::hasObjectFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
1475 {
1476 if (unknownObject())
1477 return true;
1478
1479 /*
1480 * Treat type sets containing no objects as having all object flags,
1481 * to spare callers from having to check this.
1482 */
1483 if (baseObjectCount() == 0)
1484 return true;
1485
1486 unsigned count = getObjectCount();
1487 for (unsigned i = 0; i < count; i++) {
1488 TypeObjectKey *object = getObject(i);
1489 if (object && object->hasFlags(constraints, flags))
1490 return true;
1491 }
1492
1493 return false;
1494 }
1495
1496 gc::InitialHeap
1497 TypeObject::initialHeap(CompilerConstraintList *constraints)
1498 {
1499 // If this object is not required to be pretenured but could be in the
1500 // future, add a constraint to trigger recompilation if the requirement
1501 // changes.
1502
1503 if (shouldPreTenure())
1504 return gc::TenuredHeap;
1505
1506 if (!canPreTenure())
1507 return gc::DefaultHeap;
1508
1509 HeapTypeSetKey objectProperty = TypeObjectKey::get(this)->property(JSID_EMPTY);
1510 LifoAlloc *alloc = constraints->alloc();
1511
1512 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> T;
1513 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectFlags(OBJECT_FLAG_PRE_TENURE)));
1514
1515 return gc::DefaultHeap;
1516 }
1517
1518 namespace {
1519
1520 // Constraint which triggers recompilation on any type change in an inlined
1521 // script. The freeze constraints added to stack type sets will only directly
1522 // invalidate the script containing those stack type sets. To invalidate code
1523 // for scripts into which the base script was inlined, ObjectStateChange is used.
1524 class ConstraintDataFreezeObjectForInlinedCall
1525 {
1526 public:
1527 ConstraintDataFreezeObjectForInlinedCall()
1528 {}
1529
1530 const char *kind() { return "freezeObjectForInlinedCall"; }
1531
1532 bool invalidateOnNewType(Type type) { return false; }
1533 bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
1534 bool invalidateOnNewObjectState(TypeObject *object) {
1535 // We don't keep track of the exact dependencies the caller has on its
1536 // inlined scripts' type sets, so always invalidate the caller.
1537 return true;
1538 }
1539
1540 bool constraintHolds(JSContext *cx,
1541 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
1542 {
1543 return true;
1544 }
1545
1546 bool shouldSweep() { return false; }
1547 };
1548
1549 // Constraint which triggers recompilation when the template object for a
1550 // type's new script changes.
1551 class ConstraintDataFreezeObjectForNewScriptTemplate
1552 {
1553 JSObject *templateObject;
1554
1555 public:
1556 ConstraintDataFreezeObjectForNewScriptTemplate(JSObject *templateObject)
1557 : templateObject(templateObject)
1558 {}
1559
1560 const char *kind() { return "freezeObjectForNewScriptTemplate"; }
1561
1562 bool invalidateOnNewType(Type type) { return false; }
1563 bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
1564 bool invalidateOnNewObjectState(TypeObject *object) {
1565 return !object->hasNewScript() || object->newScript()->templateObject != templateObject;
1566 }
1567
1568 bool constraintHolds(JSContext *cx,
1569 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
1570 {
1571 return !invalidateOnNewObjectState(property.object()->maybeType());
1572 }
1573
1574 bool shouldSweep() {
1575 // Note: |templateObject| is only used for equality testing.
1576 return false;
1577 }
1578 };
1579
1580 // Constraint which triggers recompilation when a typed array's data becomes
1581 // invalid.
1582 class ConstraintDataFreezeObjectForTypedArrayData
1583 {
1584 void *viewData;
1585 uint32_t length;
1586
1587 public:
1588 ConstraintDataFreezeObjectForTypedArrayData(TypedArrayObject &tarray)
1589 : viewData(tarray.viewData()),
1590 length(tarray.length())
1591 {}
1592
1593 const char *kind() { return "freezeObjectForTypedArrayData"; }
1594
1595 bool invalidateOnNewType(Type type) { return false; }
1596 bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
1597 bool invalidateOnNewObjectState(TypeObject *object) {
1598 TypedArrayObject &tarray = object->singleton()->as<TypedArrayObject>();
1599 return tarray.viewData() != viewData || tarray.length() != length;
1600 }
1601
1602 bool constraintHolds(JSContext *cx,
1603 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
1604 {
1605 return !invalidateOnNewObjectState(property.object()->maybeType());
1606 }
1607
1608 bool shouldSweep() {
1609 // Note: |viewData| is only used for equality testing.
1610 return false;
1611 }
1612 };
1613
1614 } /* anonymous namespace */
1615
1616 void
1617 TypeObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList *constraints)
1618 {
1619 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1620 LifoAlloc *alloc = constraints->alloc();
1621
1622 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForInlinedCall> T;
1623 constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectForInlinedCall()));
1624 }
1625
1626 void
1627 TypeObjectKey::watchStateChangeForNewScriptTemplate(CompilerConstraintList *constraints)
1628 {
1629 JSObject *templateObject = asTypeObject()->newScript()->templateObject;
1630 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1631 LifoAlloc *alloc = constraints->alloc();
1632
1633 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForNewScriptTemplate> T;
1634 constraints->add(alloc->new_<T>(alloc, objectProperty,
1635 ConstraintDataFreezeObjectForNewScriptTemplate(templateObject)));
1636 }
1637
1638 void
1639 TypeObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList *constraints)
1640 {
1641 TypedArrayObject &tarray = asSingleObject()->as<TypedArrayObject>();
1642 HeapTypeSetKey objectProperty = property(JSID_EMPTY);
1643 LifoAlloc *alloc = constraints->alloc();
1644
1645 typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForTypedArrayData> T;
1646 constraints->add(alloc->new_<T>(alloc, objectProperty,
1647 ConstraintDataFreezeObjectForTypedArrayData(tarray)));
1648 }
1649
1650 static void
1651 ObjectStateChange(ExclusiveContext *cxArg, TypeObject *object, bool markingUnknown)
1652 {
1653 if (object->unknownProperties())
1654 return;
1655
1656 /* All constraints listening to state changes are on the empty id. */
1657 HeapTypeSet *types = object->maybeGetProperty(JSID_EMPTY);
1658
1659 /* Mark as unknown after getting the types, to avoid assertion. */
1660 if (markingUnknown)
1661 object->addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
1662
1663 if (types) {
1664 if (JSContext *cx = cxArg->maybeJSContext()) {
1665 TypeConstraint *constraint = types->constraintList;
1666 while (constraint) {
1667 constraint->newObjectState(cx, object);
1668 constraint = constraint->next;
1669 }
1670 } else {
1671 JS_ASSERT(!types->constraintList);
1672 }
1673 }
1674 }
1675
1676 static void
1677 CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun);
1678
1679 namespace {
1680
1681 class ConstraintDataFreezePropertyState
1682 {
1683 public:
1684 enum Which {
1685 NON_DATA,
1686 NON_WRITABLE
1687 } which;
1688
1689 ConstraintDataFreezePropertyState(Which which)
1690 : which(which)
1691 {}
1692
1693 const char *kind() { return (which == NON_DATA) ? "freezeNonDataProperty" : "freezeNonWritableProperty"; }
1694
1695 bool invalidateOnNewType(Type type) { return false; }
1696 bool invalidateOnNewPropertyState(TypeSet *property) {
1697 return (which == NON_DATA)
1698 ? property->nonDataProperty()
1699 : property->nonWritableProperty();
1700 }
1701 bool invalidateOnNewObjectState(TypeObject *object) { return false; }
1702
1703 bool constraintHolds(JSContext *cx,
1704 const HeapTypeSetKey &property, TemporaryTypeSet *expected)
1705 {
1706 return !invalidateOnNewPropertyState(property.maybeTypes());
1707 }
1708
1709 bool shouldSweep() { return false; }
1710 };
1711
1712 } /* anonymous namespace */
1713
1714 bool
1715 HeapTypeSetKey::nonData(CompilerConstraintList *constraints)
1716 {
1717 if (maybeTypes() && maybeTypes()->nonDataProperty())
1718 return true;
1719
1720 LifoAlloc *alloc = constraints->alloc();
1721
1722 typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T;
1723 constraints->add(alloc->new_<T>(alloc, *this,
1724 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_DATA)));
1725 return false;
1726 }
1727
1728 bool
1729 HeapTypeSetKey::nonWritable(CompilerConstraintList *constraints)
1730 {
1731 if (maybeTypes() && maybeTypes()->nonWritableProperty())
1732 return true;
1733
1734 LifoAlloc *alloc = constraints->alloc();
1735
1736 typedef CompilerConstraintInstance<ConstraintDataFreezePropertyState> T;
1737 constraints->add(alloc->new_<T>(alloc, *this,
1738 ConstraintDataFreezePropertyState(ConstraintDataFreezePropertyState::NON_WRITABLE)));
1739 return false;
1740 }
1741
1742 bool
1743 TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const
1744 {
1745 if (other->unknown())
1746 return unknown();
1747
1748 for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
1749 Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
1750 if (type != filteredType && other->hasType(type) && !hasType(type))
1751 return false;
1752 }
1753
1754 if (other->unknownObject())
1755 return unknownObject();
1756
1757 for (size_t i = 0; i < other->getObjectCount(); i++) {
1758 TypeObjectKey *key = other->getObject(i);
1759 if (key) {
1760 Type type = Type::ObjectType(key);
1761 if (type != filteredType && !hasType(type))
1762 return false;
1763 }
1764 }
1765
1766 return true;
1767 }
1768
1769 TemporaryTypeSet::DoubleConversion
1770 TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints)
1771 {
1772 if (unknownObject() || !getObjectCount())
1773 return AmbiguousDoubleConversion;
1774
1775 bool alwaysConvert = true;
1776 bool maybeConvert = false;
1777 bool dontConvert = false;
1778
1779 for (unsigned i = 0; i < getObjectCount(); i++) {
1780 TypeObjectKey *type = getObject(i);
1781 if (!type)
1782 continue;
1783
1784 if (type->unknownProperties()) {
1785 alwaysConvert = false;
1786 continue;
1787 }
1788
1789 HeapTypeSetKey property = type->property(JSID_VOID);
1790 property.freeze(constraints);
1791
1792 // We can't convert to double elements for objects which do not have
1793 // double in their element types (as the conversion may render the type
1794 // information incorrect), nor for non-array objects (as their elements
1795 // may point to emptyObjectElements, which cannot be converted).
1796 if (!property.maybeTypes() ||
1797 !property.maybeTypes()->hasType(Type::DoubleType()) ||
1798 type->clasp() != &ArrayObject::class_)
1799 {
1800 dontConvert = true;
1801 alwaysConvert = false;
1802 continue;
1803 }
1804
1805 // Only bother with converting known packed arrays whose possible
1806 // element types are int or double. Other arrays require type tests
1807 // when elements are accessed regardless of the conversion.
1808 if (property.knownMIRType(constraints) == jit::MIRType_Double &&
1809 !type->hasFlags(constraints, OBJECT_FLAG_NON_PACKED))
1810 {
1811 maybeConvert = true;
1812 } else {
1813 alwaysConvert = false;
1814 }
1815 }
1816
1817 JS_ASSERT_IF(alwaysConvert, maybeConvert);
1818
1819 if (maybeConvert && dontConvert)
1820 return AmbiguousDoubleConversion;
1821 if (alwaysConvert)
1822 return AlwaysConvertToDoubles;
1823 if (maybeConvert)
1824 return MaybeConvertToDoubles;
1825 return DontConvertToDoubles;
1826 }
1827
1828 const Class *
1829 TemporaryTypeSet::getKnownClass()
1830 {
1831 if (unknownObject())
1832 return nullptr;
1833
1834 const Class *clasp = nullptr;
1835 unsigned count = getObjectCount();
1836
1837 for (unsigned i = 0; i < count; i++) {
1838 const Class *nclasp = getObjectClass(i);
1839 if (!nclasp)
1840 continue;
1841
1842 if (clasp && clasp != nclasp)
1843 return nullptr;
1844 clasp = nclasp;
1845 }
1846
1847 return clasp;
1848 }
1849
1850 TemporaryTypeSet::ForAllResult
1851 TemporaryTypeSet::forAllClasses(bool (*func)(const Class* clasp))
1852 {
1853 if (unknownObject())
1854 return ForAllResult::MIXED;
1855
1856 unsigned count = getObjectCount();
1857 if (count == 0)
1858 return ForAllResult::EMPTY;
1859
1860 bool true_results = false;
1861 bool false_results = false;
1862 for (unsigned i = 0; i < count; i++) {
1863 const Class *clasp = getObjectClass(i);
1864 if (!clasp)
1865 return ForAllResult::MIXED;
1866 if (func(clasp)) {
1867 true_results = true;
1868 if (false_results) return ForAllResult::MIXED;
1869 }
1870 else {
1871 false_results = true;
1872 if (true_results) return ForAllResult::MIXED;
1873 }
1874 }
1875
1876 JS_ASSERT(true_results != false_results);
1877
1878 return true_results ? ForAllResult::ALL_TRUE : ForAllResult::ALL_FALSE;
1879 }
1880
1881 int
1882 TemporaryTypeSet::getTypedArrayType()
1883 {
1884 const Class *clasp = getKnownClass();
1885
1886 if (clasp && IsTypedArrayClass(clasp))
1887 return clasp - &TypedArrayObject::classes[0];
1888 return ScalarTypeDescr::TYPE_MAX;
1889 }
1890
1891 bool
1892 TemporaryTypeSet::isDOMClass()
1893 {
1894 if (unknownObject())
1895 return false;
1896
1897 unsigned count = getObjectCount();
1898 for (unsigned i = 0; i < count; i++) {
1899 const Class *clasp = getObjectClass(i);
1900 if (clasp && !clasp->isDOMClass())
1901 return false;
1902 }
1903
1904 return count > 0;
1905 }
1906
1907 bool
1908 TemporaryTypeSet::maybeCallable()
1909 {
1910 if (!maybeObject())
1911 return false;
1912
1913 if (unknownObject())
1914 return true;
1915
1916 unsigned count = getObjectCount();
1917 for (unsigned i = 0; i < count; i++) {
1918 const Class *clasp = getObjectClass(i);
1919 if (clasp && clasp->isCallable())
1920 return true;
1921 }
1922
1923 return false;
1924 }
1925
1926 bool
1927 TemporaryTypeSet::maybeEmulatesUndefined()
1928 {
1929 if (!maybeObject())
1930 return false;
1931
1932 if (unknownObject())
1933 return true;
1934
1935 unsigned count = getObjectCount();
1936 for (unsigned i = 0; i < count; i++) {
1937 // The object emulates undefined if clasp->emulatesUndefined() or if
1938 // it's a WrapperObject, see EmulatesUndefined. Since all wrappers are
1939 // proxies, we can just check for that.
1940 const Class *clasp = getObjectClass(i);
1941 if (clasp && (clasp->emulatesUndefined() || clasp->isProxy()))
1942 return true;
1943 }
1944
1945 return false;
1946 }
1947
1948 JSObject *
1949 TemporaryTypeSet::getCommonPrototype()
1950 {
1951 if (unknownObject())
1952 return nullptr;
1953
1954 JSObject *proto = nullptr;
1955 unsigned count = getObjectCount();
1956
1957 for (unsigned i = 0; i < count; i++) {
1958 TypeObjectKey *object = getObject(i);
1959 if (!object)
1960 continue;
1961
1962 if (!object->hasTenuredProto())
1963 return nullptr;
1964
1965 TaggedProto nproto = object->proto();
1966 if (proto) {
1967 if (nproto != proto)
1968 return nullptr;
1969 } else {
1970 if (!nproto.isObject())
1971 return nullptr;
1972 proto = nproto.toObject();
1973 }
1974 }
1975
1976 return proto;
1977 }
1978
1979 bool
1980 TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id)
1981 {
1982 if (unknownObject())
1983 return true;
1984
1985 for (unsigned i = 0; i < getObjectCount(); i++) {
1986 TypeObjectKey *type = getObject(i);
1987 if (!type)
1988 continue;
1989
1990 if (type->unknownProperties())
1991 return true;
1992
1993 HeapTypeSetKey property = type->property(id);
1994 if (property.needsBarrier(constraints))
1995 return true;
1996 }
1997
1998 return false;
1999 }
2000
2001 /////////////////////////////////////////////////////////////////////
2002 // TypeCompartment
2003 /////////////////////////////////////////////////////////////////////
2004
2005 TypeCompartment::TypeCompartment()
2006 {
2007 PodZero(this);
2008 }
2009
2010 TypeObject *
2011 TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto,
2012 TypeObjectFlags initialFlags)
2013 {
2014 JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
2015
2016 if (cx->isJSContext()) {
2017 if (proto.isObject() && IsInsideNursery(cx->asJSContext()->runtime(), proto.toObject()))
2018 initialFlags |= OBJECT_FLAG_NURSERY_PROTO;
2019 }
2020
2021 TypeObject *object = js::NewTypeObject(cx);
2022 if (!object)
2023 return nullptr;
2024 new(object) TypeObject(clasp, proto, initialFlags);
2025
2026 return object;
2027 }
2028
2029 TypeObject *
2030 TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key)
2031 {
2032 AutoEnterAnalysis enter(cx);
2033
2034 if (!allocationSiteTable) {
2035 allocationSiteTable = cx->new_<AllocationSiteTable>();
2036 if (!allocationSiteTable || !allocationSiteTable->init()) {
2037 js_delete(allocationSiteTable);
2038 return nullptr;
2039 }
2040 }
2041
2042 AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key);
2043 JS_ASSERT(!p);
2044
2045 TypeObject *res = nullptr;
2046
2047 jsbytecode *pc = key.script->offsetToPC(key.offset);
2048 RootedScript keyScript(cx, key.script);
2049
2050 if (!res) {
2051 RootedObject proto(cx);
2052 if (!GetBuiltinPrototype(cx, key.kind, &proto))
2053 return nullptr;
2054
2055 Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
2056 res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE);
2057 if (!res)
2058 return nullptr;
2059 key.script = keyScript;
2060 }
2061
2062 if (JSOp(*pc) == JSOP_NEWOBJECT) {
2063 /*
2064 * This object is always constructed the same way and will not be
2065 * observed by other code before all properties have been added. Mark
2066 * all the properties as definite properties of the object.
2067 */
2068 RootedObject baseobj(cx, key.script->getObject(GET_UINT32_INDEX(pc)));
2069
2070 if (!res->addDefiniteProperties(cx, baseobj))
2071 return nullptr;
2072 }
2073
2074 if (!allocationSiteTable->add(p, key, res))
2075 return nullptr;
2076
2077 return res;
2078 }
2079
2080 static inline jsid
2081 GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
2082 {
2083 PropertyName *name = script->getName(GET_UINT32_INDEX(pc + offset));
2084 return IdToTypeId(NameToId(name));
2085 }
2086
2087 bool
2088 types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc)
2089 {
2090 /*
2091 * Make a heuristic guess at a use of JSOP_NEW that the constructed object
2092 * should have a fresh type object. We do this when the NEW is immediately
2093 * followed by a simple assignment to an object's .prototype field.
2094 * This is designed to catch common patterns for subclassing in JS:
2095 *
2096 * function Super() { ... }
2097 * function Sub1() { ... }
2098 * function Sub2() { ... }
2099 *
2100 * Sub1.prototype = new Super();
2101 * Sub2.prototype = new Super();
2102 *
2103 * Using distinct type objects for the particular prototypes of Sub1 and
2104 * Sub2 lets us continue to distinguish the two subclasses and any extra
2105 * properties added to those prototype objects.
2106 */
2107 if (JSOp(*pc) == JSOP_NEW)
2108 pc += JSOP_NEW_LENGTH;
2109 else if (JSOp(*pc) == JSOP_SPREADNEW)
2110 pc += JSOP_SPREADNEW_LENGTH;
2111 else
2112 return false;
2113 if (JSOp(*pc) == JSOP_SETPROP) {
2114 jsid id = GetAtomId(cx, script, pc, 0);
2115 if (id == id_prototype(cx))
2116 return true;
2117 }
2118
2119 return false;
2120 }
2121
2122 NewObjectKind
2123 types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key)
2124 {
2125 /*
2126 * Objects created outside loops in global and eval scripts should have
2127 * singleton types. For now this is only done for plain objects and typed
2128 * arrays, but not normal arrays.
2129 */
2130
2131 if (script->functionNonDelazifying() && !script->treatAsRunOnce())
2132 return GenericObject;
2133
2134 if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
2135 return GenericObject;
2136
2137 /*
2138 * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
2139 * indicating their boundary.
2140 */
2141
2142 if (!script->hasTrynotes())
2143 return SingletonObject;
2144
2145 unsigned offset = script->pcToOffset(pc);
2146
2147 JSTryNote *tn = script->trynotes()->vector;
2148 JSTryNote *tnlimit = tn + script->trynotes()->length;
2149 for (; tn < tnlimit; tn++) {
2150 if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP)
2151 continue;
2152
2153 unsigned startOffset = script->mainOffset() + tn->start;
2154 unsigned endOffset = startOffset + tn->length;
2155
2156 if (offset >= startOffset && offset < endOffset)
2157 return GenericObject;
2158 }
2159
2160 return SingletonObject;
2161 }
2162
2163 NewObjectKind
2164 types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp)
2165 {
2166 return UseNewTypeForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp));
2167 }
2168
2169 static inline bool
2170 ClassCanHaveExtraProperties(const Class *clasp)
2171 {
2172 JS_ASSERT(clasp->resolve);
2173 return clasp->resolve != JS_ResolveStub
2174 || clasp->ops.lookupGeneric
2175 || clasp->ops.getGeneric
2176 || IsTypedArrayClass(clasp);
2177 }
2178
2179 static inline bool
2180 PrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSObject *obj)
2181 {
2182 do {
2183 TypeObjectKey *type = TypeObjectKey::get(obj);
2184 if (ClassCanHaveExtraProperties(type->clasp()))
2185 return true;
2186 if (type->unknownProperties())
2187 return true;
2188 HeapTypeSetKey index = type->property(JSID_VOID);
2189 if (index.nonData(constraints) || index.isOwnProperty(constraints))
2190 return true;
2191 if (!obj->hasTenuredProto())
2192 return true;
2193 obj = obj->getProto();
2194 } while (obj);
2195
2196 return false;
2197 }
2198
2199 bool
2200 types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSScript *script)
2201 {
2202 if (JSObject *proto = script->global().maybeGetArrayPrototype())
2203 return PrototypeHasIndexedProperty(constraints, proto);
2204 return true;
2205 }
2206
2207 bool
2208 types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
2209 TemporaryTypeSet *types)
2210 {
2211 const Class *clasp = types->getKnownClass();
2212
2213 // Note: typed arrays have indexed properties not accounted for by type
2214 // information, though these are all in bounds and will be accounted for
2215 // by JIT paths.
2216 if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp)))
2217 return true;
2218
2219 if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES))
2220 return true;
2221
2222 JSObject *proto = types->getCommonPrototype();
2223 if (!proto)
2224 return true;
2225
2226 return PrototypeHasIndexedProperty(constraints, proto);
2227 }
2228
2229 void
2230 TypeZone::processPendingRecompiles(FreeOp *fop)
2231 {
2232 if (!pendingRecompiles)
2233 return;
2234
2235 /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
2236 Vector<RecompileInfo> *pending = pendingRecompiles;
2237 pendingRecompiles = nullptr;
2238
2239 JS_ASSERT(!pending->empty());
2240
2241 #ifdef JS_ION
2242 jit::Invalidate(*this, fop, *pending);
2243 #endif
2244
2245 fop->delete_(pending);
2246 }
2247
2248 void
2249 TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
2250 {
2251 CompilerOutput *co = info.compilerOutput(cx);
2252 if (!co || !co->isValid() || co->pendingInvalidation())
2253 return;
2254
2255 InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d",
2256 co->script(), co->script()->filename(), co->script()->lineno());
2257
2258 co->setPendingInvalidation();
2259
2260 if (!pendingRecompiles) {
2261 pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
2262 if (!pendingRecompiles)
2263 CrashAtUnhandlableOOM("Could not update pendingRecompiles");
2264 }
2265
2266 if (!pendingRecompiles->append(info))
2267 CrashAtUnhandlableOOM("Could not update pendingRecompiles");
2268 }
2269
2270 void
2271 TypeZone::addPendingRecompile(JSContext *cx, JSScript *script)
2272 {
2273 JS_ASSERT(script);
2274
2275 #ifdef JS_ION
2276 CancelOffThreadIonCompile(cx->compartment(), script);
2277
2278 // Let the script warm up again before attempting another compile.
2279 if (jit::IsBaselineEnabled(cx))
2280 script->resetUseCount();
2281
2282 if (script->hasIonScript())
2283 addPendingRecompile(cx, script->ionScript()->recompileInfo());
2284
2285 if (script->hasParallelIonScript())
2286 addPendingRecompile(cx, script->parallelIonScript()->recompileInfo());
2287 #endif
2288
2289 // When one script is inlined into another the caller listens to state
2290 // changes on the callee's script, so trigger these to force recompilation
2291 // of any such callers.
2292 if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyType())
2293 ObjectStateChange(cx, script->functionNonDelazifying()->type(), false);
2294 }
2295
2296 void
2297 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
2298 {
2299 JS_ASSERT(this == &cx->compartment()->types);
2300 JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
2301 JS_ASSERT(!target->singleton());
2302 JS_ASSERT(target->unknownProperties());
2303
2304 AutoEnterAnalysis enter(cx);
2305
2306 /* Mark type sets which contain obj as having a generic object types. */
2307
2308 for (gc::CellIter i(cx->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
2309 TypeObject *object = i.get<TypeObject>();
2310 unsigned count = object->getPropertyCount();
2311 for (unsigned i = 0; i < count; i++) {
2312 Property *prop = object->getProperty(i);
2313 if (prop && prop->types.hasType(Type::ObjectType(target)))
2314 prop->types.addType(cx, Type::AnyObjectType());
2315 }
2316 }
2317
2318 for (gc::CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
2319 RootedScript script(cx, i.get<JSScript>());
2320 if (script->types) {
2321 unsigned count = TypeScript::NumTypeSets(script);
2322 StackTypeSet *typeArray = script->types->typeArray();
2323 for (unsigned i = 0; i < count; i++) {
2324 if (typeArray[i].hasType(Type::ObjectType(target)))
2325 typeArray[i].addType(cx, Type::AnyObjectType());
2326 }
2327 }
2328 }
2329
2330 target->addFlags(OBJECT_FLAG_SETS_MARKED_UNKNOWN);
2331 }
2332
2333 void
2334 TypeCompartment::print(JSContext *cx, bool force)
2335 {
2336 #ifdef DEBUG
2337 gc::AutoSuppressGC suppressGC(cx);
2338
2339 JSCompartment *compartment = this->compartment();
2340 AutoEnterAnalysis enter(nullptr, compartment);
2341
2342 if (!force && !InferSpewActive(ISpewResult))
2343 return;
2344
2345 for (gc::CellIter i(compartment->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
2346 // Note: use cx->runtime() instead of cx to work around IsInRequest(cx)
2347 // assertion failures when we're called from DestroyContext.
2348 RootedScript script(cx->runtime(), i.get<JSScript>());
2349 if (script->types)
2350 script->types->printTypes(cx, script);
2351 }
2352
2353 for (gc::CellIter i(compartment->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
2354 TypeObject *object = i.get<TypeObject>();
2355 object->print();
2356 }
2357 #endif
2358 }
2359
2360 /////////////////////////////////////////////////////////////////////
2361 // TypeCompartment tables
2362 /////////////////////////////////////////////////////////////////////
2363
2364 /*
2365 * The arrayTypeTable and objectTypeTable are per-compartment tables for making
2366 * common type objects to model the contents of large script singletons and
2367 * JSON objects. These are vanilla Arrays and native Objects, so we distinguish
2368 * the types of different ones by looking at the types of their properties.
2369 *
2370 * All singleton/JSON arrays which have the same prototype, are homogenous and
2371 * of the same element type will share a type object. All singleton/JSON
2372 * objects which have the same shape and property types will also share a type
2373 * object. We don't try to collate arrays or objects that have type mismatches.
2374 */
2375
2376 static inline bool
2377 NumberTypes(Type a, Type b)
2378 {
2379 return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
2380 && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
2381 }
2382
2383 /*
2384 * As for GetValueType, but requires object types to be non-singletons with
2385 * their default prototype. These are the only values that should appear in
2386 * arrays and objects whose type can be fixed.
2387 */
2388 static inline Type
2389 GetValueTypeForTable(const Value &v)
2390 {
2391 Type type = GetValueType(v);
2392 JS_ASSERT(!type.isSingleObject());
2393 return type;
2394 }
2395
2396 struct types::ArrayTableKey : public DefaultHasher<types::ArrayTableKey>
2397 {
2398 Type type;
2399 JSObject *proto;
2400
2401 ArrayTableKey()
2402 : type(Type::UndefinedType()), proto(nullptr)
2403 {}
2404
2405 ArrayTableKey(Type type, JSObject *proto)
2406 : type(type), proto(proto)
2407 {}
2408
2409 static inline uint32_t hash(const ArrayTableKey &v) {
2410 return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2));
2411 }
2412
2413 static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) {
2414 return v1.type == v2.type && v1.proto == v2.proto;
2415 }
2416 };
2417
2418 void
2419 TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx,
2420 JSObject *obj, Type elementType)
2421 {
2422 JS_ASSERT(cx->compartment()->activeAnalysis);
2423
2424 if (!arrayTypeTable) {
2425 arrayTypeTable = cx->new_<ArrayTypeTable>();
2426 if (!arrayTypeTable || !arrayTypeTable->init()) {
2427 arrayTypeTable = nullptr;
2428 return;
2429 }
2430 }
2431
2432 ArrayTableKey key(elementType, obj->getProto());
2433 DependentAddPtr<ArrayTypeTable> p(cx, *arrayTypeTable, key);
2434 if (p) {
2435 obj->setType(p->value());
2436 } else {
2437 /* Make a new type to use for future arrays with the same elements. */
2438 RootedObject objProto(cx, obj->getProto());
2439 TypeObject *objType = newTypeObject(cx, &ArrayObject::class_, objProto);
2440 if (!objType)
2441 return;
2442 obj->setType(objType);
2443
2444 if (!objType->unknownProperties())
2445 objType->addPropertyType(cx, JSID_VOID, elementType);
2446
2447 key.proto = objProto;
2448 (void) p.add(cx, *arrayTypeTable, key, objType);
2449 }
2450 }
2451
2452 void
2453 TypeCompartment::fixArrayType(ExclusiveContext *cx, JSObject *obj)
2454 {
2455 AutoEnterAnalysis enter(cx);
2456
2457 /*
2458 * If the array is of homogenous type, pick a type object which will be
2459 * shared with all other singleton/JSON arrays of the same type.
2460 * If the array is heterogenous, keep the existing type object, which has
2461 * unknown properties.
2462 */
2463 JS_ASSERT(obj->is<ArrayObject>());
2464
2465 unsigned len = obj->getDenseInitializedLength();
2466 if (len == 0)
2467 return;
2468
2469 Type type = GetValueTypeForTable(obj->getDenseElement(0));
2470
2471 for (unsigned i = 1; i < len; i++) {
2472 Type ntype = GetValueTypeForTable(obj->getDenseElement(i));
2473 if (ntype != type) {
2474 if (NumberTypes(type, ntype))
2475 type = Type::DoubleType();
2476 else
2477 return;
2478 }
2479 }
2480
2481 setTypeToHomogenousArray(cx, obj, type);
2482 }
2483
2484 void
2485 types::FixRestArgumentsType(ExclusiveContext *cx, JSObject *obj)
2486 {
2487 cx->compartment()->types.fixRestArgumentsType(cx, obj);
2488 }
2489
2490 void
2491 TypeCompartment::fixRestArgumentsType(ExclusiveContext *cx, JSObject *obj)
2492 {
2493 AutoEnterAnalysis enter(cx);
2494
2495 /*
2496 * Tracking element types for rest argument arrays is not worth it, but we
2497 * still want it to be known that it's a dense array.
2498 */
2499 JS_ASSERT(obj->is<ArrayObject>());
2500
2501 setTypeToHomogenousArray(cx, obj, Type::UnknownType());
2502 }
2503
2504 /*
2505 * N.B. We could also use the initial shape of the object (before its type is
2506 * fixed) as the key in the object table, but since all references in the table
2507 * are weak the hash entries would usually be collected on GC even if objects
2508 * with the new type/shape are still live.
2509 */
2510 struct types::ObjectTableKey
2511 {
2512 jsid *properties;
2513 uint32_t nproperties;
2514 uint32_t nfixed;
2515
2516 struct Lookup {
2517 IdValuePair *properties;
2518 uint32_t nproperties;
2519 uint32_t nfixed;
2520
2521 Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed)
2522 : properties(properties), nproperties(nproperties), nfixed(nfixed)
2523 {}
2524 };
2525
2526 static inline HashNumber hash(const Lookup &lookup) {
2527 return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
2528 lookup.nproperties ^
2529 lookup.nfixed);
2530 }
2531
2532 static inline bool match(const ObjectTableKey &v, const Lookup &lookup) {
2533 if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed)
2534 return false;
2535 for (size_t i = 0; i < lookup.nproperties; i++) {
2536 if (lookup.properties[i].id != v.properties[i])
2537 return false;
2538 }
2539 return true;
2540 }
2541 };
2542
2543 struct types::ObjectTableEntry
2544 {
2545 ReadBarriered<TypeObject> object;
2546 ReadBarriered<Shape> shape;
2547 Type *types;
2548 };
2549
2550 static inline void
2551 UpdateObjectTableEntryTypes(ExclusiveContext *cx, ObjectTableEntry &entry,
2552 IdValuePair *properties, size_t nproperties)
2553 {
2554 if (entry.object->unknownProperties())
2555 return;
2556 for (size_t i = 0; i < nproperties; i++) {
2557 Type type = entry.types[i];
2558 Type ntype = GetValueTypeForTable(properties[i].value);
2559 if (ntype == type)
2560 continue;
2561 if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
2562 type.isPrimitive(JSVAL_TYPE_DOUBLE))
2563 {
2564 /* The property types already reflect 'int32'. */
2565 } else {
2566 if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
2567 type.isPrimitive(JSVAL_TYPE_INT32))
2568 {
2569 /* Include 'double' in the property types to avoid the update below later. */
2570 entry.types[i] = Type::DoubleType();
2571 }
2572 entry.object->addPropertyType(cx, IdToTypeId(properties[i].id), ntype);
2573 }
2574 }
2575 }
2576
2577 void
2578 TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj)
2579 {
2580 AutoEnterAnalysis enter(cx);
2581
2582 if (!objectTypeTable) {
2583 objectTypeTable = cx->new_<ObjectTypeTable>();
2584 if (!objectTypeTable || !objectTypeTable->init()) {
2585 js_delete(objectTypeTable);
2586 objectTypeTable = nullptr;
2587 return;
2588 }
2589 }
2590
2591 /*
2592 * Use the same type object for all singleton/JSON objects with the same
2593 * base shape, i.e. the same fields written in the same order.
2594 */
2595 JS_ASSERT(obj->is<JSObject>());
2596
2597 /*
2598 * Exclude some objects we can't readily associate common types for based on their
2599 * shape. Objects with metadata are excluded so that the metadata does not need to
2600 * be included in the table lookup (the metadata object might be in the nursery).
2601 */
2602 if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata())
2603 return;
2604
2605 Vector<IdValuePair> properties(cx);
2606 if (!properties.resize(obj->slotSpan()))
2607 return;
2608
2609 Shape *shape = obj->lastProperty();
2610 while (!shape->isEmptyShape()) {
2611 IdValuePair &entry = properties[shape->slot()];
2612 entry.id = shape->propid();
2613 entry.value = obj->getSlot(shape->slot());
2614 shape = shape->previous();
2615 }
2616
2617 ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots());
2618 ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
2619
2620 if (p) {
2621 JS_ASSERT(obj->getProto() == p->value().object->proto().toObject());
2622 JS_ASSERT(obj->lastProperty() == p->value().shape);
2623
2624 UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length());
2625 obj->setType(p->value().object);
2626 return;
2627 }
2628
2629 /* Make a new type to use for the object and similar future ones. */
2630 Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
2631 TypeObject *objType = newTypeObject(cx, &JSObject::class_, objProto);
2632 if (!objType || !objType->addDefiniteProperties(cx, obj))
2633 return;
2634
2635 if (obj->isIndexed())
2636 objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
2637
2638 ScopedJSFreePtr<jsid> ids(cx->pod_calloc<jsid>(properties.length()));
2639 if (!ids)
2640 return;
2641
2642 ScopedJSFreePtr<Type> types(cx->pod_calloc<Type>(properties.length()));
2643 if (!types)
2644 return;
2645
2646 for (size_t i = 0; i < properties.length(); i++) {
2647 ids[i] = properties[i].id;
2648 types[i] = GetValueTypeForTable(obj->getSlot(i));
2649 if (!objType->unknownProperties())
2650 objType->addPropertyType(cx, IdToTypeId(ids[i]), types[i]);
2651 }
2652
2653 ObjectTableKey key;
2654 key.properties = ids;
2655 key.nproperties = properties.length();
2656 key.nfixed = obj->numFixedSlots();
2657 JS_ASSERT(ObjectTableKey::match(key, lookup));
2658
2659 ObjectTableEntry entry;
2660 entry.object = objType;
2661 entry.shape = obj->lastProperty();
2662 entry.types = types;
2663
2664 obj->setType(objType);
2665
2666 p = objectTypeTable->lookupForAdd(lookup);
2667 if (objectTypeTable->add(p, key, entry)) {
2668 ids.forget();
2669 types.forget();
2670 }
2671 }
2672
2673 JSObject *
2674 TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties)
2675 {
2676 AutoEnterAnalysis enter(cx);
2677
2678 if (!objectTypeTable) {
2679 objectTypeTable = cx->new_<ObjectTypeTable>();
2680 if (!objectTypeTable || !objectTypeTable->init()) {
2681 js_delete(objectTypeTable);
2682 objectTypeTable = nullptr;
2683 return nullptr;
2684 }
2685 }
2686
2687 /*
2688 * Use the object type table to allocate an object with the specified
2689 * properties, filling in its final type and shape and failing if no cache
2690 * entry could be found for the properties.
2691 */
2692
2693 /*
2694 * Filter out a few cases where we don't want to use the object type table.
2695 * Note that if the properties contain any duplicates or dense indexes,
2696 * the lookup below will fail as such arrays of properties cannot be stored
2697 * in the object type table --- fixObjectType populates the table with
2698 * properties read off its input object, which cannot be duplicates, and
2699 * ignores objects with dense indexes.
2700 */
2701 if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT)
2702 return nullptr;
2703
2704 gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
2705 size_t nfixed = gc::GetGCKindSlots(allocKind, &JSObject::class_);
2706
2707 ObjectTableKey::Lookup lookup(properties, nproperties, nfixed);
2708 ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
2709
2710 if (!p)
2711 return nullptr;
2712
2713 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind));
2714 if (!obj) {
2715 cx->clearPendingException();
2716 return nullptr;
2717 }
2718 JS_ASSERT(obj->getProto() == p->value().object->proto().toObject());
2719
2720 RootedShape shape(cx, p->value().shape);
2721 if (!JSObject::setLastProperty(cx, obj, shape)) {
2722 cx->clearPendingException();
2723 return nullptr;
2724 }
2725
2726 UpdateObjectTableEntryTypes(cx, p->value(), properties, nproperties);
2727
2728 for (size_t i = 0; i < nproperties; i++)
2729 obj->setSlot(i, properties[i].value);
2730
2731 obj->setType(p->value().object);
2732 return obj;
2733 }
2734
2735 /////////////////////////////////////////////////////////////////////
2736 // TypeObject
2737 /////////////////////////////////////////////////////////////////////
2738
2739 void
2740 TypeObject::setProto(JSContext *cx, TaggedProto proto)
2741 {
2742 JS_ASSERT(singleton());
2743
2744 if (proto.isObject() && IsInsideNursery(cx->runtime(), proto.toObject()))
2745 addFlags(OBJECT_FLAG_NURSERY_PROTO);
2746
2747 setProtoUnchecked(proto);
2748 }
2749
2750 static inline void
2751 UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shape *shape,
2752 bool indexed)
2753 {
2754 if (!shape->writable())
2755 types->setNonWritableProperty(cx);
2756
2757 if (shape->hasGetterValue() || shape->hasSetterValue()) {
2758 types->setNonDataProperty(cx);
2759 types->TypeSet::addType(Type::UnknownType(), &cx->typeLifoAlloc());
2760 } else if (shape->hasDefaultGetter() && shape->hasSlot()) {
2761 if (!indexed && types->canSetDefinite(shape->slot()))
2762 types->setDefinite(shape->slot());
2763
2764 const Value &value = obj->nativeGetSlot(shape->slot());
2765
2766 /*
2767 * Don't add initial undefined types for properties of global objects
2768 * that are not collated into the JSID_VOID property (see propertySet
2769 * comment).
2770 */
2771 if (indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) {
2772 Type type = GetValueType(value);
2773 types->TypeSet::addType(type, &cx->typeLifoAlloc());
2774 }
2775 }
2776 }
2777
2778 void
2779 TypeObject::updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types)
2780 {
2781 InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
2782 InferSpewColor(types), types, InferSpewColorReset(),
2783 TypeObjectString(this), TypeIdString(id));
2784
2785 if (!singleton() || !singleton()->isNative())
2786 return;
2787
2788 /*
2789 * Fill the property in with any type the object already has in an own
2790 * property. We are only interested in plain native properties and
2791 * dense elements which don't go through a barrier when read by the VM
2792 * or jitcode.
2793 */
2794
2795 if (JSID_IS_VOID(id)) {
2796 /* Go through all shapes on the object to get integer-valued properties. */
2797 RootedShape shape(cx, singleton()->lastProperty());
2798 while (!shape->isEmptyShape()) {
2799 if (JSID_IS_VOID(IdToTypeId(shape->propid())))
2800 UpdatePropertyType(cx, types, singleton(), shape, true);
2801 shape = shape->previous();
2802 }
2803
2804 /* Also get values of any dense elements in the object. */
2805 for (size_t i = 0; i < singleton()->getDenseInitializedLength(); i++) {
2806 const Value &value = singleton()->getDenseElement(i);
2807 if (!value.isMagic(JS_ELEMENTS_HOLE)) {
2808 Type type = GetValueType(value);
2809 types->TypeSet::addType(type, &cx->typeLifoAlloc());
2810 }
2811 }
2812 } else if (!JSID_IS_EMPTY(id)) {
2813 RootedId rootedId(cx, id);
2814 Shape *shape = singleton()->nativeLookup(cx, rootedId);
2815 if (shape)
2816 UpdatePropertyType(cx, types, singleton(), shape, false);
2817 }
2818
2819 if (singleton()->watched()) {
2820 /*
2821 * Mark the property as non-data, to inhibit optimizations on it
2822 * and avoid bypassing the watchpoint handler.
2823 */
2824 types->setNonDataProperty(cx);
2825 }
2826 }
2827
2828 bool
2829 TypeObject::addDefiniteProperties(ExclusiveContext *cx, JSObject *obj)
2830 {
2831 if (unknownProperties())
2832 return true;
2833
2834 /* Mark all properties of obj as definite properties of this type. */
2835 AutoEnterAnalysis enter(cx);
2836
2837 RootedShape shape(cx, obj->lastProperty());
2838 while (!shape->isEmptyShape()) {
2839 jsid id = IdToTypeId(shape->propid());
2840 if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot())) {
2841 TypeSet *types = getProperty(cx, id);
2842 if (!types)
2843 return false;
2844 types->setDefinite(shape->slot());
2845 }
2846 shape = shape->previous();
2847 }
2848
2849 return true;
2850 }
2851
2852 bool
2853 TypeObject::matchDefiniteProperties(HandleObject obj)
2854 {
2855 unsigned count = getPropertyCount();
2856 for (unsigned i = 0; i < count; i++) {
2857 Property *prop = getProperty(i);
2858 if (!prop)
2859 continue;
2860 if (prop->types.definiteProperty()) {
2861 unsigned slot = prop->types.definiteSlot();
2862
2863 bool found = false;
2864 Shape *shape = obj->lastProperty();
2865 while (!shape->isEmptyShape()) {
2866 if (shape->slot() == slot && shape->propid() == prop->id) {
2867 found = true;
2868 break;
2869 }
2870 shape = shape->previous();
2871 }
2872 if (!found)
2873 return false;
2874 }
2875 }
2876
2877 return true;
2878 }
2879
2880 static inline void
2881 InlineAddTypeProperty(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type)
2882 {
2883 JS_ASSERT(id == IdToTypeId(id));
2884
2885 AutoEnterAnalysis enter(cx);
2886
2887 HeapTypeSet *types = obj->getProperty(cx, id);
2888 if (!types || types->hasType(type))
2889 return;
2890
2891 InferSpew(ISpewOps, "externalType: property %s %s: %s",
2892 TypeObjectString(obj), TypeIdString(id), TypeString(type));
2893 types->addType(cx, type);
2894 }
2895
2896 void
2897 TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, Type type)
2898 {
2899 InlineAddTypeProperty(cx, this, id, type);
2900 }
2901
2902 void
2903 TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, const Value &value)
2904 {
2905 InlineAddTypeProperty(cx, this, id, GetValueType(value));
2906 }
2907
2908 void
2909 TypeObject::markPropertyNonData(ExclusiveContext *cx, jsid id)
2910 {
2911 AutoEnterAnalysis enter(cx);
2912
2913 id = IdToTypeId(id);
2914
2915 HeapTypeSet *types = getProperty(cx, id);
2916 if (types)
2917 types->setNonDataProperty(cx);
2918 }
2919
2920 void
2921 TypeObject::markPropertyNonWritable(ExclusiveContext *cx, jsid id)
2922 {
2923 AutoEnterAnalysis enter(cx);
2924
2925 id = IdToTypeId(id);
2926
2927 HeapTypeSet *types = getProperty(cx, id);
2928 if (types)
2929 types->setNonWritableProperty(cx);
2930 }
2931
2932 bool
2933 TypeObject::isPropertyNonData(jsid id)
2934 {
2935 TypeSet *types = maybeGetProperty(id);
2936 if (types)
2937 return types->nonDataProperty();
2938 return false;
2939 }
2940
2941 bool
2942 TypeObject::isPropertyNonWritable(jsid id)
2943 {
2944 TypeSet *types = maybeGetProperty(id);
2945 if (types)
2946 return types->nonWritableProperty();
2947 return false;
2948 }
2949
2950 void
2951 TypeObject::markStateChange(ExclusiveContext *cxArg)
2952 {
2953 if (unknownProperties())
2954 return;
2955
2956 AutoEnterAnalysis enter(cxArg);
2957 HeapTypeSet *types = maybeGetProperty(JSID_EMPTY);
2958 if (types) {
2959 if (JSContext *cx = cxArg->maybeJSContext()) {
2960 TypeConstraint *constraint = types->constraintList;
2961 while (constraint) {
2962 constraint->newObjectState(cx, this);
2963 constraint = constraint->next;
2964 }
2965 } else {
2966 JS_ASSERT(!types->constraintList);
2967 }
2968 }
2969 }
2970
2971 void
2972 TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags)
2973 {
2974 if (hasAllFlags(flags))
2975 return;
2976
2977 AutoEnterAnalysis enter(cx);
2978
2979 if (singleton()) {
2980 /* Make sure flags are consistent with persistent object state. */
2981 JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
2982 singleton()->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
2983 }
2984
2985 addFlags(flags);
2986
2987 InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
2988
2989 ObjectStateChange(cx, this, false);
2990 }
2991
2992 void
2993 TypeObject::markUnknown(ExclusiveContext *cx)
2994 {
2995 AutoEnterAnalysis enter(cx);
2996
2997 JS_ASSERT(cx->compartment()->activeAnalysis);
2998 JS_ASSERT(!unknownProperties());
2999
3000 if (!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED))
3001 clearAddendum(cx);
3002
3003 InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
3004
3005 ObjectStateChange(cx, this, true);
3006
3007 /*
3008 * Existing constraints may have already been added to this object, which we need
3009 * to do the right thing for. We can't ensure that we will mark all unknown
3010 * objects before they have been accessed, as the __proto__ of a known object
3011 * could be dynamically set to an unknown object, and we can decide to ignore
3012 * properties of an object during analysis (i.e. hashmaps). Adding unknown for
3013 * any properties accessed already accounts for possible values read from them.
3014 */
3015
3016 unsigned count = getPropertyCount();
3017 for (unsigned i = 0; i < count; i++) {
3018 Property *prop = getProperty(i);
3019 if (prop) {
3020 prop->types.addType(cx, Type::UnknownType());
3021 prop->types.setNonDataProperty(cx);
3022 }
3023 }
3024 }
3025
3026 void
3027 TypeObject::clearAddendum(ExclusiveContext *cx)
3028 {
3029 JS_ASSERT(!(flags() & OBJECT_FLAG_ADDENDUM_CLEARED));
3030
3031 addFlags(OBJECT_FLAG_ADDENDUM_CLEARED);
3032
3033 /*
3034 * It is possible for the object to not have a new script or other
3035 * addendum yet, but to have one added in the future. When
3036 * analyzing properties of new scripts we mix in adding
3037 * constraints to trigger clearNewScript with changes to the type
3038 * sets themselves (from breakTypeBarriers). It is possible that
3039 * we could trigger one of these constraints before
3040 * AnalyzeNewScriptProperties has finished, in which case we want
3041 * to make sure that call fails.
3042 */
3043 if (!addendum)
3044 return;
3045
3046 switch (addendum->kind) {
3047 case TypeObjectAddendum::NewScript:
3048 clearNewScriptAddendum(cx);
3049 break;
3050
3051 case TypeObjectAddendum::TypedObject:
3052 clearTypedObjectAddendum(cx);
3053 break;
3054 }
3055
3056 /* We nullptr out addendum *before* freeing it so the write barrier works. */
3057 TypeObjectAddendum *savedAddendum = addendum;
3058 addendum = nullptr;
3059 js_free(savedAddendum);
3060
3061 markStateChange(cx);
3062 }
3063
3064 void
3065 TypeObject::clearNewScriptAddendum(ExclusiveContext *cx)
3066 {
3067 AutoEnterAnalysis enter(cx);
3068
3069 /*
3070 * Any definite properties we added due to analysis of the new script when
3071 * the type object was created are now invalid: objects with the same type
3072 * can be created by using 'new' on a different script or through some
3073 * other mechanism (e.g. Object.create). Rather than clear out the definite
3074 * bits on the object's properties, just mark such properties as having
3075 * been deleted/reconfigured, which will have the same effect on JITs
3076 * wanting to use the definite bits to optimize property accesses.
3077 */
3078 for (unsigned i = 0; i < getPropertyCount(); i++) {
3079 Property *prop = getProperty(i);
3080 if (!prop)
3081 continue;
3082 if (prop->types.definiteProperty())
3083 prop->types.setNonDataProperty(cx);
3084 }
3085
3086 /*
3087 * If we cleared the new script while in the middle of initializing an
3088 * object, it will still have the new script's shape and reflect the no
3089 * longer correct state of the object once its initialization is completed.
3090 * We can't really detect the possibility of this statically, but the new
3091 * script keeps track of where each property is initialized so we can walk
3092 * the stack and fix up any such objects.
3093 */
3094 if (cx->isJSContext()) {
3095 Vector<uint32_t, 32> pcOffsets(cx);
3096 for (ScriptFrameIter iter(cx->asJSContext()); !iter.done(); ++iter) {
3097 pcOffsets.append(iter.script()->pcToOffset(iter.pc()));
3098 if (!iter.isConstructing() ||
3099 iter.callee() != newScript()->fun ||
3100 !iter.thisv().isObject() ||
3101 iter.thisv().toObject().hasLazyType() ||
3102 iter.thisv().toObject().type() != this)
3103 {
3104 continue;
3105 }
3106
3107 // Found a matching frame.
3108 RootedObject obj(cx, &iter.thisv().toObject());
3109
3110 // Whether all identified 'new' properties have been initialized.
3111 bool finished = false;
3112
3113 // If not finished, number of properties that have been added.
3114 uint32_t numProperties = 0;
3115
3116 // Whether the current SETPROP is within an inner frame which has
3117 // finished entirely.
3118 bool pastProperty = false;
3119
3120 // Index in pcOffsets of the outermost frame.
3121 int callDepth = pcOffsets.length() - 1;
3122
3123 // Index in pcOffsets of the frame currently being checked for a SETPROP.
3124 int setpropDepth = callDepth;
3125
3126 for (TypeNewScript::Initializer *init = newScript()->initializerList;; init++) {
3127 if (init->kind == TypeNewScript::Initializer::SETPROP) {
3128 if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
3129 // Have not yet reached this setprop.
3130 break;
3131 }
3132 // This setprop has executed, reset state for the next one.
3133 numProperties++;
3134 pastProperty = false;
3135 setpropDepth = callDepth;
3136 } else if (init->kind == TypeNewScript::Initializer::SETPROP_FRAME) {
3137 if (!pastProperty) {
3138 if (pcOffsets[setpropDepth] < init->offset) {
3139 // Have not yet reached this inner call.
3140 break;
3141 } else if (pcOffsets[setpropDepth] > init->offset) {
3142 // Have advanced past this inner call.
3143 pastProperty = true;
3144 } else if (setpropDepth == 0) {
3145 // Have reached this call but not yet in it.
3146 break;
3147 } else {
3148 // Somewhere inside this inner call.
3149 setpropDepth--;
3150 }
3151 }
3152 } else {
3153 JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE);
3154 finished = true;
3155 break;
3156 }
3157 }
3158
3159 if (!finished)
3160 (void) JSObject::rollbackProperties(cx, obj, numProperties);
3161 }
3162 } else {
3163 // Threads with an ExclusiveContext are not allowed to run scripts.
3164 JS_ASSERT(!cx->perThreadData->activation());
3165 }
3166 }
3167
3168 void
3169 TypeObject::maybeClearNewScriptAddendumOnOOM()
3170 {
3171 if (!isMarked())
3172 return;
3173
3174 if (!addendum || addendum->kind != TypeObjectAddendum::NewScript)
3175 return;
3176
3177 for (unsigned i = 0; i < getPropertyCount(); i++) {
3178 Property *prop = getProperty(i);
3179 if (!prop)
3180 continue;
3181 if (prop->types.definiteProperty())
3182 prop->types.setNonDataPropertyIgnoringConstraints();
3183 }
3184
3185 // This method is called during GC sweeping, so there is no write barrier
3186 // that needs to be triggered.
3187 js_free(addendum);
3188 addendum.unsafeSet(nullptr);
3189 }
3190
3191 void
3192 TypeObject::clearTypedObjectAddendum(ExclusiveContext *cx)
3193 {
3194 }
3195
3196 void
3197 TypeObject::print()
3198 {
3199 TaggedProto tagged(proto());
3200 fprintf(stderr, "%s : %s",
3201 TypeObjectString(this),
3202 tagged.isObject() ? TypeString(Type::ObjectType(tagged.toObject()))
3203 : (tagged.isLazy() ? "(lazy)" : "(null)"));
3204
3205 if (unknownProperties()) {
3206 fprintf(stderr, " unknown");
3207 } else {
3208 if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES))
3209 fprintf(stderr, " dense");
3210 if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED))
3211 fprintf(stderr, " packed");
3212 if (!hasAnyFlags(OBJECT_FLAG_LENGTH_OVERFLOW))
3213 fprintf(stderr, " noLengthOverflow");
3214 if (hasAnyFlags(OBJECT_FLAG_ITERATED))
3215 fprintf(stderr, " iterated");
3216 if (interpretedFunction)
3217 fprintf(stderr, " ifun");
3218 }
3219
3220 unsigned count = getPropertyCount();
3221
3222 if (count == 0) {
3223 fprintf(stderr, " {}\n");
3224 return;
3225 }
3226
3227 fprintf(stderr, " {");
3228
3229 for (unsigned i = 0; i < count; i++) {
3230 Property *prop = getProperty(i);
3231 if (prop) {
3232 fprintf(stderr, "\n %s:", TypeIdString(prop->id));
3233 prop->types.print();
3234 }
3235 }
3236
3237 fprintf(stderr, "\n}\n");
3238 }
3239
3240 /////////////////////////////////////////////////////////////////////
3241 // Type Analysis
3242 /////////////////////////////////////////////////////////////////////
3243
3244 /*
3245 * Persistent constraint clearing out newScript and definite properties from
3246 * an object should a property on another object get a getter or setter.
3247 */
3248 class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
3249 {
3250 public:
3251 TypeObject *object;
3252
3253 TypeConstraintClearDefiniteGetterSetter(TypeObject *object)
3254 : object(object)
3255 {}
3256
3257 const char *kind() { return "clearDefiniteGetterSetter"; }
3258
3259 void newPropertyState(JSContext *cx, TypeSet *source)
3260 {
3261 if (!object->hasNewScript())
3262 return;
3263 /*
3264 * Clear out the newScript shape and definite property information from
3265 * an object if the source type set could be a setter or could be
3266 * non-writable.
3267 */
3268 if (!(object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED) &&
3269 (source->nonDataProperty() || source->nonWritableProperty()))
3270 {
3271 object->clearAddendum(cx);
3272 }
3273 }
3274
3275 void newType(JSContext *cx, TypeSet *source, Type type) {}
3276
3277 bool sweep(TypeZone &zone, TypeConstraint **res) {
3278 if (IsTypeObjectAboutToBeFinalized(&object))
3279 return false;
3280 *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object);
3281 return true;
3282 }
3283 };
3284
3285 bool
3286 types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, HandleId id)
3287 {
3288 /*
3289 * Ensure that if the properties named here could have a getter, setter or
3290 * a permanent property in any transitive prototype, the definite
3291 * properties get cleared from the type.
3292 */
3293 RootedObject parent(cx, type->proto().toObjectOrNull());
3294 while (parent) {
3295 TypeObject *parentObject = parent->getType(cx);
3296 if (!parentObject || parentObject->unknownProperties())
3297 return false;
3298 HeapTypeSet *parentTypes = parentObject->getProperty(cx, id);
3299 if (!parentTypes || parentTypes->nonDataProperty() || parentTypes->nonWritableProperty())
3300 return false;
3301 if (!parentTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(type)))
3302 return false;
3303 parent = parent->getProto();
3304 }
3305 return true;
3306 }
3307
3308 /*
3309 * Constraint which clears definite properties on an object should a type set
3310 * contain any types other than a single object.
3311 */
3312 class TypeConstraintClearDefiniteSingle : public TypeConstraint
3313 {
3314 public:
3315 TypeObject *object;
3316
3317 TypeConstraintClearDefiniteSingle(TypeObject *object)
3318 : object(object)
3319 {}
3320
3321 const char *kind() { return "clearDefiniteSingle"; }
3322
3323 void newType(JSContext *cx, TypeSet *source, Type type) {
3324 if (object->flags() & OBJECT_FLAG_ADDENDUM_CLEARED)
3325 return;
3326
3327 if (source->baseFlags() || source->getObjectCount() > 1)
3328 object->clearAddendum(cx);
3329 }
3330
3331 bool sweep(TypeZone &zone, TypeConstraint **res) {
3332 if (IsTypeObjectAboutToBeFinalized(&object))
3333 return false;
3334 *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object);
3335 return true;
3336 }
3337 };
3338
3339 bool
3340 types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
3341 JSScript *script, JSScript *calleeScript)
3342 {
3343 // Look for any uses of the specified calleeScript in type sets for
3344 // |script|, and add constraints to ensure that if the type sets' contents
3345 // change then the definite properties are cleared from the type.
3346 // This ensures that the inlining performed when the definite properties
3347 // analysis was done is stable.
3348
3349 TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey();
3350
3351 unsigned count = TypeScript::NumTypeSets(script);
3352 StackTypeSet *typeArray = script->types->typeArray();
3353
3354 for (unsigned i = 0; i < count; i++) {
3355 StackTypeSet *types = &typeArray[i];
3356 if (!types->unknownObject() && types->getObjectCount() == 1) {
3357 if (calleeKey != types->getObject(0)) {
3358 // Also check if the object is the Function.call or
3359 // Function.apply native. IonBuilder uses the presence of these
3360 // functions during inlining.
3361 JSObject *singleton = types->getSingleObject(0);
3362 if (!singleton || !singleton->is<JSFunction>())
3363 continue;
3364 JSFunction *fun = &singleton->as<JSFunction>();
3365 if (!fun->isNative())
3366 continue;
3367 if (fun->native() != js_fun_call && fun->native() != js_fun_apply)
3368 continue;
3369 }
3370 // This is a type set that might have been used when inlining
3371 // |calleeScript| into |script|.
3372 if (!types->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type)))
3373 return false;
3374 }
3375 }
3376
3377 return true;
3378 }
3379
3380 /*
3381 * Either make the newScript information for type when it is constructed
3382 * by the specified script, or regenerate the constraints for an existing
3383 * newScript on the type after they were cleared by a GC.
3384 */
3385 static void
3386 CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
3387 {
3388 JS_ASSERT(cx->compartment()->activeAnalysis);
3389
3390 #ifdef JS_ION
3391 if (type->unknownProperties())
3392 return;
3393
3394 /* Strawman object to add properties to and watch for duplicates. */
3395 RootedObject baseobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, gc::FINALIZE_OBJECT16));
3396 if (!baseobj)
3397 return;
3398
3399 Vector<TypeNewScript::Initializer> initializerList(cx);
3400
3401 if (!jit::AnalyzeNewScriptProperties(cx, fun, type, baseobj, &initializerList) ||
3402 baseobj->slotSpan() == 0 ||
3403 !!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED))
3404 {
3405 if (type->hasNewScript())
3406 type->clearAddendum(cx);
3407 return;
3408 }
3409
3410 /*
3411 * If the type already has a new script, we are just regenerating the type
3412 * constraints and don't need to make another TypeNewScript. Make sure that
3413 * the properties added to baseobj match the type's definite properties.
3414 */
3415 if (type->hasNewScript()) {
3416 if (!type->matchDefiniteProperties(baseobj))
3417 type->clearAddendum(cx);
3418 return;
3419 }
3420 JS_ASSERT(!type->hasNewScript());
3421 JS_ASSERT(!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED));
3422
3423 gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan());
3424
3425 /* We should not have overflowed the maximum number of fixed slots for an object. */
3426 JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan());
3427
3428 TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0);
3429
3430 /*
3431 * The base object may have been created with a different finalize kind
3432 * than we will use for subsequent new objects. Generate an object with the
3433 * appropriate final shape.
3434 */
3435 Rooted<TypeObject *> rootedType(cx, type);
3436 RootedShape shape(cx, baseobj->lastProperty());
3437 baseobj = NewReshapedObject(cx, rootedType, baseobj->getParent(), kind, shape, MaybeSingletonObject);
3438 if (!baseobj ||
3439 !type->addDefiniteProperties(cx, baseobj) ||
3440 !initializerList.append(done))
3441 {
3442 return;
3443 }
3444
3445 size_t numBytes = sizeof(TypeNewScript)
3446 + (initializerList.length() * sizeof(TypeNewScript::Initializer));
3447 TypeNewScript *newScript = (TypeNewScript *) cx->calloc_(numBytes);
3448 if (!newScript)
3449 return;
3450
3451 new (newScript) TypeNewScript();
3452
3453 type->setAddendum(newScript);
3454
3455 newScript->fun = fun;
3456 newScript->templateObject = baseobj;
3457
3458 newScript->initializerList = (TypeNewScript::Initializer *)
3459 ((char *) newScript + sizeof(TypeNewScript));
3460 PodCopy(newScript->initializerList,
3461 initializerList.begin(),
3462 initializerList.length());
3463 #endif // JS_ION
3464 }
3465
3466 /////////////////////////////////////////////////////////////////////
3467 // Interface functions
3468 /////////////////////////////////////////////////////////////////////
3469
3470 void
3471 types::TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args,
3472 bool constructing)
3473 {
3474 unsigned nargs = callee->as<JSFunction>().nargs();
3475 JSScript *script = callee->as<JSFunction>().nonLazyScript();
3476
3477 if (!constructing)
3478 TypeScript::SetThis(cx, script, args.thisv());
3479
3480 /*
3481 * Add constraints going up to the minimum of the actual and formal count.
3482 * If there are more actuals than formals the later values can only be
3483 * accessed through the arguments object, which is monitored.
3484 */
3485 unsigned arg = 0;
3486 for (; arg < args.length() && arg < nargs; arg++)
3487 TypeScript::SetArgument(cx, script, arg, args[arg]);
3488
3489 /* Watch for fewer actuals than formals to the call. */
3490 for (; arg < nargs; arg++)
3491 TypeScript::SetArgument(cx, script, arg, UndefinedValue());
3492 }
3493
3494 static inline bool
3495 IsAboutToBeFinalized(TypeObjectKey *key)
3496 {
3497 /* Mask out the low bit indicating whether this is a type or JS object. */
3498 gc::Cell *tmp = reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1);
3499 bool isAboutToBeFinalized = IsCellAboutToBeFinalized(&tmp);
3500 JS_ASSERT(tmp == reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1));
3501 return isAboutToBeFinalized;
3502 }
3503
3504 void
3505 types::FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap)
3506 {
3507 uint32_t added = 0;
3508 for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
3509 JSOp op = JSOp(*pc);
3510 if (js_CodeSpec[op].format & JOF_TYPESET) {
3511 bytecodeMap[added++] = script->pcToOffset(pc);
3512 if (added == script->nTypeSets())
3513 break;
3514 }
3515 }
3516 JS_ASSERT(added == script->nTypeSets());
3517 }
3518
3519 void
3520 types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
3521 {
3522 /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
3523 if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
3524 return;
3525
3526 if (!script->hasBaselineScript())
3527 return;
3528
3529 AutoEnterAnalysis enter(cx);
3530
3531 Type type = GetValueType(rval);
3532 StackTypeSet *types = TypeScript::BytecodeTypes(script, pc);
3533 if (types->hasType(type))
3534 return;
3535
3536 InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s",
3537 script->id(), script->pcToOffset(pc), TypeString(type));
3538 types->addType(cx, type);
3539 }
3540
3541 bool
3542 types::UseNewTypeForClone(JSFunction *fun)
3543 {
3544 if (!fun->isInterpreted())
3545 return false;
3546
3547 if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite())
3548 return true;
3549
3550 if (fun->isArrow())
3551 return false;
3552
3553 if (fun->hasSingletonType())
3554 return false;
3555
3556 /*
3557 * When a function is being used as a wrapper for another function, it
3558 * improves precision greatly to distinguish between different instances of
3559 * the wrapper; otherwise we will conflate much of the information about
3560 * the wrapped functions.
3561 *
3562 * An important example is the Class.create function at the core of the
3563 * Prototype.js library, which looks like:
3564 *
3565 * var Class = {
3566 * create: function() {
3567 * return function() {
3568 * this.initialize.apply(this, arguments);
3569 * }
3570 * }
3571 * };
3572 *
3573 * Each instance of the innermost function will have a different wrapped
3574 * initialize method. We capture this, along with similar cases, by looking
3575 * for short scripts which use both .apply and arguments. For such scripts,
3576 * whenever creating a new instance of the function we both give that
3577 * instance a singleton type and clone the underlying script.
3578 */
3579
3580 uint32_t begin, end;
3581 if (fun->hasScript()) {
3582 if (!fun->nonLazyScript()->usesArgumentsAndApply())
3583 return false;
3584 begin = fun->nonLazyScript()->sourceStart();
3585 end = fun->nonLazyScript()->sourceEnd();
3586 } else {
3587 if (!fun->lazyScript()->usesArgumentsAndApply())
3588 return false;
3589 begin = fun->lazyScript()->begin();
3590 end = fun->lazyScript()->end();
3591 }
3592
3593 return end - begin <= 100;
3594 }
3595 /////////////////////////////////////////////////////////////////////
3596 // TypeScript
3597 /////////////////////////////////////////////////////////////////////
3598
3599 bool
3600 JSScript::makeTypes(JSContext *cx)
3601 {
3602 JS_ASSERT(!types);
3603
3604 AutoEnterAnalysis enter(cx);
3605
3606 unsigned count = TypeScript::NumTypeSets(this);
3607
3608 TypeScript *typeScript = (TypeScript *)
3609 cx->calloc_(TypeScript::SizeIncludingTypeArray(count));
3610 if (!typeScript)
3611 return false;
3612
3613 new(typeScript) TypeScript();
3614
3615 TypeSet *typeArray = typeScript->typeArray();
3616
3617 for (unsigned i = 0; i < count; i++)
3618 new (&typeArray[i]) StackTypeSet();
3619
3620 types = typeScript;
3621
3622 #ifdef DEBUG
3623 for (unsigned i = 0; i < nTypeSets(); i++) {
3624 InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
3625 InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
3626 i, id());
3627 }
3628 TypeSet *thisTypes = TypeScript::ThisTypes(this);
3629 InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
3630 InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
3631 id());
3632 unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0;
3633 for (unsigned i = 0; i < nargs; i++) {
3634 TypeSet *types = TypeScript::ArgTypes(this, i);
3635 InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u",
3636 InferSpewColor(types), types, InferSpewColorReset(),
3637 i, id());
3638 }
3639 #endif
3640
3641 return true;
3642 }
3643
3644 /* static */ bool
3645 JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun,
3646 bool singleton /* = false */)
3647 {
3648 if (singleton) {
3649 if (!setSingletonType(cx, fun))
3650 return false;
3651 } else {
3652 RootedObject funProto(cx, fun->getProto());
3653 TypeObject *type =
3654 cx->compartment()->types.newTypeObject(cx, &JSFunction::class_, funProto);
3655 if (!type)
3656 return false;
3657
3658 fun->setType(type);
3659 type->interpretedFunction = fun;
3660 }
3661
3662 return true;
3663 }
3664
3665 /////////////////////////////////////////////////////////////////////
3666 // JSObject
3667 /////////////////////////////////////////////////////////////////////
3668
3669 bool
3670 JSObject::shouldSplicePrototype(JSContext *cx)
3671 {
3672 /*
3673 * During bootstrapping, if inference is enabled we need to make sure not
3674 * to splice a new prototype in for Function.prototype or the global
3675 * object if their __proto__ had previously been set to null, as this
3676 * will change the prototype for all other objects with the same type.
3677 */
3678 if (getProto() != nullptr)
3679 return false;
3680 return hasSingletonType();
3681 }
3682
3683 bool
3684 JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle<TaggedProto> proto)
3685 {
3686 JS_ASSERT(cx->compartment() == compartment());
3687
3688 RootedObject self(cx, this);
3689
3690 /*
3691 * For singleton types representing only a single JSObject, the proto
3692 * can be rearranged as needed without destroying type information for
3693 * the old or new types.
3694 */
3695 JS_ASSERT(self->hasSingletonType());
3696
3697 /* Inner objects may not appear on prototype chains. */
3698 JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
3699
3700 /*
3701 * Force type instantiation when splicing lazy types. This may fail,
3702 * in which case inference will be disabled for the compartment.
3703 */
3704 Rooted<TypeObject*> type(cx, self->getType(cx));
3705 if (!type)
3706 return false;
3707 Rooted<TypeObject*> protoType(cx, nullptr);
3708 if (proto.isObject()) {
3709 protoType = proto.toObject()->getType(cx);
3710 if (!protoType)
3711 return false;
3712 }
3713
3714 type->setClasp(clasp);
3715 type->setProto(cx, proto);
3716 return true;
3717 }
3718
3719 /* static */ TypeObject *
3720 JSObject::makeLazyType(JSContext *cx, HandleObject obj)
3721 {
3722 JS_ASSERT(obj->hasLazyType());
3723 JS_ASSERT(cx->compartment() == obj->compartment());
3724
3725 /* De-lazification of functions can GC, so we need to do it up here. */
3726 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
3727 RootedFunction fun(cx, &obj->as<JSFunction>());
3728 if (!fun->getOrCreateScript(cx))
3729 return nullptr;
3730 }
3731
3732 // Find flags which need to be specified immediately on the object.
3733 // Don't track whether singletons are packed.
3734 TypeObjectFlags initialFlags = OBJECT_FLAG_NON_PACKED;
3735
3736 if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
3737 initialFlags |= OBJECT_FLAG_ITERATED;
3738
3739 if (obj->isIndexed())
3740 initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
3741
3742 if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
3743 initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
3744
3745 Rooted<TaggedProto> proto(cx, obj->getTaggedProto());
3746 TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto, initialFlags);
3747 if (!type)
3748 return nullptr;
3749
3750 AutoEnterAnalysis enter(cx);
3751
3752 /* Fill in the type according to the state of this object. */
3753
3754 type->initSingleton(obj);
3755
3756 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted())
3757 type->interpretedFunction = &obj->as<JSFunction>();
3758
3759 obj->type_ = type;
3760
3761 return type;
3762 }
3763
3764 /* static */ inline HashNumber
3765 TypeObjectWithNewScriptEntry::hash(const Lookup &lookup)
3766 {
3767 return PointerHasher<JSObject *, 3>::hash(lookup.hashProto.raw()) ^
3768 PointerHasher<const Class *, 3>::hash(lookup.clasp) ^
3769 PointerHasher<JSFunction *, 3>::hash(lookup.newFunction);
3770 }
3771
3772 /* static */ inline bool
3773 TypeObjectWithNewScriptEntry::match(const TypeObjectWithNewScriptEntry &key, const Lookup &lookup)
3774 {
3775 return key.object->proto() == lookup.matchProto &&
3776 key.object->clasp() == lookup.clasp &&
3777 key.newFunction == lookup.newFunction;
3778 }
3779
3780 #ifdef DEBUG
3781 bool
3782 JSObject::hasNewType(const Class *clasp, TypeObject *type)
3783 {
3784 TypeObjectWithNewScriptSet &table = compartment()->newTypeObjects;
3785
3786 if (!table.initialized())
3787 return false;
3788
3789 TypeObjectWithNewScriptSet::Ptr p = table.lookup(TypeObjectWithNewScriptSet::Lookup(clasp, this, nullptr));
3790 return p && p->object == type;
3791 }
3792 #endif /* DEBUG */
3793
3794 /* static */ bool
3795 JSObject::setNewTypeUnknown(JSContext *cx, const Class *clasp, HandleObject obj)
3796 {
3797 if (!obj->setFlag(cx, js::BaseShape::NEW_TYPE_UNKNOWN))
3798 return false;
3799
3800 /*
3801 * If the object already has a new type, mark that type as unknown. It will
3802 * not have the SETS_MARKED_UNKNOWN bit set, so may require a type set
3803 * crawl if prototypes of the object change dynamically in the future.
3804 */
3805 TypeObjectWithNewScriptSet &table = cx->compartment()->newTypeObjects;
3806 if (table.initialized()) {
3807 if (TypeObjectWithNewScriptSet::Ptr p = table.lookup(TypeObjectWithNewScriptSet::Lookup(clasp, obj.get(), nullptr)))
3808 MarkTypeObjectUnknownProperties(cx, p->object);
3809 }
3810
3811 return true;
3812 }
3813
3814 #ifdef JSGC_GENERATIONAL
3815 /*
3816 * This class is used to add a post barrier on the newTypeObjects set, as the
3817 * key is calculated from a prototype object which may be moved by generational
3818 * GC.
3819 */
3820 class NewTypeObjectsSetRef : public BufferableRef
3821 {
3822 TypeObjectWithNewScriptSet *set;
3823 const Class *clasp;
3824 JSObject *proto;
3825 JSFunction *newFunction;
3826
3827 public:
3828 NewTypeObjectsSetRef(TypeObjectWithNewScriptSet *s, const Class *clasp, JSObject *proto, JSFunction *newFunction)
3829 : set(s), clasp(clasp), proto(proto), newFunction(newFunction)
3830 {}
3831
3832 void mark(JSTracer *trc) {
3833 JSObject *prior = proto;
3834 trc->setTracingLocation(&*prior);
3835 Mark(trc, &proto, "newTypeObjects set prototype");
3836 if (prior == proto)
3837 return;
3838
3839 TypeObjectWithNewScriptSet::Ptr p = set->lookup(TypeObjectWithNewScriptSet::Lookup(clasp, prior, proto, newFunction));
3840 JS_ASSERT(p); // newTypeObjects set must still contain original entry.
3841
3842 set->rekeyAs(TypeObjectWithNewScriptSet::Lookup(clasp, prior, proto, newFunction),
3843 TypeObjectWithNewScriptSet::Lookup(clasp, proto, newFunction), *p);
3844 }
3845 };
3846 #endif
3847
3848 TypeObject *
3849 ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction *fun)
3850 {
3851 JS_ASSERT_IF(fun, proto.isObject());
3852 JS_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject()));
3853
3854 TypeObjectWithNewScriptSet &newTypeObjects = compartment()->newTypeObjects;
3855
3856 if (!newTypeObjects.initialized() && !newTypeObjects.init())
3857 return nullptr;
3858
3859 // Canonicalize new functions to use the original one associated with its script.
3860 if (fun) {
3861 if (fun->hasScript())
3862 fun = fun->nonLazyScript()->functionNonDelazifying();
3863 else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
3864 fun = fun->lazyScript()->functionNonDelazifying();
3865 else
3866 fun = nullptr;
3867 }
3868
3869 TypeObjectWithNewScriptSet::AddPtr p =
3870 newTypeObjects.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, fun));
3871 if (p) {
3872 TypeObject *type = p->object;
3873 JS_ASSERT(type->clasp() == clasp);
3874 JS_ASSERT(type->proto() == proto);
3875 JS_ASSERT_IF(type->hasNewScript(), type->newScript()->fun == fun);
3876 return type;
3877 }
3878
3879 AutoEnterAnalysis enter(this);
3880
3881 if (proto.isObject() && !proto.toObject()->setDelegate(this))
3882 return nullptr;
3883
3884 TypeObjectFlags initialFlags = 0;
3885 if (!proto.isObject() || proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)) {
3886 // The new type is not present in any type sets, so mark the object as
3887 // unknown in all type sets it appears in. This allows the prototype of
3888 // such objects to mutate freely without triggering an expensive walk of
3889 // the compartment's type sets. (While scripts normally don't mutate
3890 // __proto__, the browser will for proxies and such, and we need to
3891 // accommodate this behavior).
3892 initialFlags = OBJECT_FLAG_UNKNOWN_MASK | OBJECT_FLAG_SETS_MARKED_UNKNOWN;
3893 }
3894
3895 Rooted<TaggedProto> protoRoot(this, proto);
3896 TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot, initialFlags);
3897 if (!type)
3898 return nullptr;
3899
3900 if (!newTypeObjects.add(p, TypeObjectWithNewScriptEntry(type, fun)))
3901 return nullptr;
3902
3903 #ifdef JSGC_GENERATIONAL
3904 if (proto.isObject() && hasNursery() && nursery().isInside(proto.toObject())) {
3905 asJSContext()->runtime()->gcStoreBuffer.putGeneric(
3906 NewTypeObjectsSetRef(&newTypeObjects, clasp, proto.toObject(), fun));
3907 }
3908 #endif
3909
3910 if (proto.isObject()) {
3911 RootedObject obj(this, proto.toObject());
3912
3913 if (fun)
3914 CheckNewScriptProperties(asJSContext(), type, fun);
3915
3916 /*
3917 * Some builtin objects have slotful native properties baked in at
3918 * creation via the Shape::{insert,get}initialShape mechanism. Since
3919 * these properties are never explicitly defined on new objects, update
3920 * the type information for them here.
3921 */
3922
3923 if (obj->is<RegExpObject>()) {
3924 AddTypePropertyId(this, type, NameToId(names().source), Type::StringType());
3925 AddTypePropertyId(this, type, NameToId(names().global), Type::BooleanType());
3926 AddTypePropertyId(this, type, NameToId(names().ignoreCase), Type::BooleanType());
3927 AddTypePropertyId(this, type, NameToId(names().multiline), Type::BooleanType());
3928 AddTypePropertyId(this, type, NameToId(names().sticky), Type::BooleanType());
3929 AddTypePropertyId(this, type, NameToId(names().lastIndex), Type::Int32Type());
3930 }
3931
3932 if (obj->is<StringObject>())
3933 AddTypePropertyId(this, type, NameToId(names().length), Type::Int32Type());
3934
3935 if (obj->is<ErrorObject>()) {
3936 AddTypePropertyId(this, type, NameToId(names().fileName), Type::StringType());
3937 AddTypePropertyId(this, type, NameToId(names().lineNumber), Type::Int32Type());
3938 AddTypePropertyId(this, type, NameToId(names().columnNumber), Type::Int32Type());
3939 AddTypePropertyId(this, type, NameToId(names().stack), Type::StringType());
3940 }
3941 }
3942
3943 return type;
3944 }
3945
3946 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
3947 void
3948 JSCompartment::checkNewTypeObjectTableAfterMovingGC()
3949 {
3950 /*
3951 * Assert that the postbarriers have worked and that nothing is left in
3952 * newTypeObjects that points into the nursery, and that the hash table
3953 * entries are discoverable.
3954 */
3955 JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtimeFromMainThread());
3956 for (TypeObjectWithNewScriptSet::Enum e(newTypeObjects); !e.empty(); e.popFront()) {
3957 TypeObjectWithNewScriptEntry entry = e.front();
3958 JS_ASSERT(!IsInsideNursery(rt, entry.newFunction));
3959 TaggedProto proto = entry.object->proto();
3960 JS_ASSERT_IF(proto.isObject(), !IsInsideNursery(rt, proto.toObject()));
3961 TypeObjectWithNewScriptEntry::Lookup
3962 lookup(entry.object->clasp(), proto, entry.newFunction);
3963 TypeObjectWithNewScriptSet::Ptr ptr = newTypeObjects.lookup(lookup);
3964 JS_ASSERT(ptr.found() && &*ptr == &e.front());
3965 }
3966 }
3967 #endif
3968
3969 TypeObject *
3970 ExclusiveContext::getSingletonType(const Class *clasp, TaggedProto proto)
3971 {
3972 JS_ASSERT_IF(proto.isObject(), compartment() == proto.toObject()->compartment());
3973
3974 AutoEnterAnalysis enter(this);
3975
3976 TypeObjectWithNewScriptSet &table = compartment()->lazyTypeObjects;
3977
3978 if (!table.initialized() && !table.init())
3979 return nullptr;
3980
3981 TypeObjectWithNewScriptSet::AddPtr p = table.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, nullptr));
3982 if (p) {
3983 TypeObject *type = p->object;
3984 JS_ASSERT(type->lazy());
3985
3986 return type;
3987 }
3988
3989 Rooted<TaggedProto> protoRoot(this, proto);
3990 TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot);
3991 if (!type)
3992 return nullptr;
3993
3994 if (!table.add(p, TypeObjectWithNewScriptEntry(type, nullptr)))
3995 return nullptr;
3996
3997 type->initSingleton((JSObject *) TypeObject::LAZY_SINGLETON);
3998 MOZ_ASSERT(type->singleton(), "created type must be a proper singleton");
3999
4000 return type;
4001 }
4002
4003 /////////////////////////////////////////////////////////////////////
4004 // Tracing
4005 /////////////////////////////////////////////////////////////////////
4006
4007 void
4008 ConstraintTypeSet::sweep(Zone *zone, bool *oom)
4009 {
4010 /*
4011 * Purge references to type objects that are no longer live. Type sets hold
4012 * only weak references. For type sets containing more than one object,
4013 * live entries in the object hash need to be copied to the zone's
4014 * new arena.
4015 */
4016 unsigned objectCount = baseObjectCount();
4017 if (objectCount >= 2) {
4018 unsigned oldCapacity = HashSetCapacity(objectCount);
4019 TypeObjectKey **oldArray = objectSet;
4020
4021 clearObjects();
4022 objectCount = 0;
4023 for (unsigned i = 0; i < oldCapacity; i++) {
4024 TypeObjectKey *object = oldArray[i];
4025 if (object && !IsAboutToBeFinalized(object)) {
4026 TypeObjectKey **pentry =
4027 HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
4028 (zone->types.typeLifoAlloc, objectSet, objectCount, object);
4029 if (pentry) {
4030 *pentry = object;
4031 } else {
4032 *oom = true;
4033 flags |= TYPE_FLAG_ANYOBJECT;
4034 clearObjects();
4035 objectCount = 0;
4036 break;
4037 }
4038 }
4039 }
4040 setBaseObjectCount(objectCount);
4041 } else if (objectCount == 1) {
4042 TypeObjectKey *object = (TypeObjectKey *) objectSet;
4043 if (IsAboutToBeFinalized(object)) {
4044 objectSet = nullptr;
4045 setBaseObjectCount(0);
4046 }
4047 }
4048
4049 /*
4050 * Type constraints only hold weak references. Copy constraints referring
4051 * to data that is still live into the zone's new arena.
4052 */
4053 TypeConstraint *constraint = constraintList;
4054 constraintList = nullptr;
4055 while (constraint) {
4056 TypeConstraint *copy;
4057 if (constraint->sweep(zone->types, &copy)) {
4058 if (copy) {
4059 copy->next = constraintList;
4060 constraintList = copy;
4061 } else {
4062 *oom = true;
4063 }
4064 }
4065 constraint = constraint->next;
4066 }
4067 }
4068
4069 inline void
4070 TypeObject::clearProperties()
4071 {
4072 setBasePropertyCount(0);
4073 propertySet = nullptr;
4074 }
4075
4076 /*
4077 * Before sweeping the arenas themselves, scan all type objects in a
4078 * compartment to fixup weak references: property type sets referencing dead
4079 * JS and type objects, and singleton JS objects whose type is not referenced
4080 * elsewhere. This also releases memory associated with dead type objects,
4081 * so that type objects do not need later finalization.
4082 */
4083 inline void
4084 TypeObject::sweep(FreeOp *fop, bool *oom)
4085 {
4086 if (!isMarked()) {
4087 if (addendum)
4088 fop->free_(addendum);
4089 return;
4090 }
4091
4092 LifoAlloc &typeLifoAlloc = zone()->types.typeLifoAlloc;
4093
4094 /*
4095 * Properties were allocated from the old arena, and need to be copied over
4096 * to the new one.
4097 */
4098 unsigned propertyCount = basePropertyCount();
4099 if (propertyCount >= 2) {
4100 unsigned oldCapacity = HashSetCapacity(propertyCount);
4101 Property **oldArray = propertySet;
4102
4103 clearProperties();
4104 propertyCount = 0;
4105 for (unsigned i = 0; i < oldCapacity; i++) {
4106 Property *prop = oldArray[i];
4107 if (prop) {
4108 if (singleton() && !prop->types.constraintList && !zone()->isPreservingCode()) {
4109 /*
4110 * Don't copy over properties of singleton objects when their
4111 * presence will not be required by jitcode or type constraints
4112 * (i.e. for the definite properties analysis). The contents of
4113 * these type sets will be regenerated as necessary.
4114 */
4115 continue;
4116 }
4117
4118 Property *newProp = typeLifoAlloc.new_<Property>(*prop);
4119 if (newProp) {
4120 Property **pentry =
4121 HashSetInsert<jsid,Property,Property>
4122 (typeLifoAlloc, propertySet, propertyCount, prop->id);
4123 if (pentry) {
4124 *pentry = newProp;
4125 newProp->types.sweep(zone(), oom);
4126 continue;
4127 }
4128 }
4129
4130 *oom = true;
4131 addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
4132 clearProperties();
4133 return;
4134 }
4135 }
4136 setBasePropertyCount(propertyCount);
4137 } else if (propertyCount == 1) {
4138 Property *prop = (Property *) propertySet;
4139 if (singleton() && !prop->types.constraintList && !zone()->isPreservingCode()) {
4140 // Skip, as above.
4141 clearProperties();
4142 } else {
4143 Property *newProp = typeLifoAlloc.new_<Property>(*prop);
4144 if (newProp) {
4145 propertySet = (Property **) newProp;
4146 newProp->types.sweep(zone(), oom);
4147 } else {
4148 *oom = true;
4149 addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
4150 clearProperties();
4151 return;
4152 }
4153 }
4154 }
4155 }
4156
4157 void
4158 TypeCompartment::clearTables()
4159 {
4160 if (allocationSiteTable && allocationSiteTable->initialized())
4161 allocationSiteTable->clear();
4162 if (arrayTypeTable && arrayTypeTable->initialized())
4163 arrayTypeTable->clear();
4164 if (objectTypeTable && objectTypeTable->initialized())
4165 objectTypeTable->clear();
4166 }
4167
4168 void
4169 TypeCompartment::sweep(FreeOp *fop)
4170 {
4171 /*
4172 * Iterate through the array/object type tables and remove all entries
4173 * referencing collected data. These tables only hold weak references.
4174 */
4175
4176 if (arrayTypeTable) {
4177 for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
4178 const ArrayTableKey &key = e.front().key();
4179 JS_ASSERT(key.type.isUnknown() || !key.type.isSingleObject());
4180
4181 bool remove = false;
4182 TypeObject *typeObject = nullptr;
4183 if (!key.type.isUnknown() && key.type.isTypeObject()) {
4184 typeObject = key.type.typeObject();
4185 if (IsTypeObjectAboutToBeFinalized(&typeObject))
4186 remove = true;
4187 }
4188 if (IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet()))
4189 remove = true;
4190
4191 if (remove) {
4192 e.removeFront();
4193 } else if (typeObject && typeObject != key.type.typeObject()) {
4194 ArrayTableKey newKey;
4195 newKey.type = Type::ObjectType(typeObject);
4196 newKey.proto = key.proto;
4197 e.rekeyFront(newKey);
4198 }
4199 }
4200 }
4201
4202 if (objectTypeTable) {
4203 for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
4204 const ObjectTableKey &key = e.front().key();
4205 ObjectTableEntry &entry = e.front().value();
4206
4207 bool remove = false;
4208 if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet()))
4209 remove = true;
4210 if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet()))
4211 remove = true;
4212 for (unsigned i = 0; !remove && i < key.nproperties; i++) {
4213 if (JSID_IS_STRING(key.properties[i])) {
4214 JSString *str = JSID_TO_STRING(key.properties[i]);
4215 if (IsStringAboutToBeFinalized(&str))
4216 remove = true;
4217 JS_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]);
4218 }
4219 JS_ASSERT(!entry.types[i].isSingleObject());
4220 TypeObject *typeObject = nullptr;
4221 if (entry.types[i].isTypeObject()) {
4222 typeObject = entry.types[i].typeObject();
4223 if (IsTypeObjectAboutToBeFinalized(&typeObject))
4224 remove = true;
4225 else if (typeObject != entry.types[i].typeObject())
4226 entry.types[i] = Type::ObjectType(typeObject);
4227 }
4228 }
4229
4230 if (remove) {
4231 js_free(key.properties);
4232 js_free(entry.types);
4233 e.removeFront();
4234 }
4235 }
4236 }
4237
4238 if (allocationSiteTable) {
4239 for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
4240 AllocationSiteKey key = e.front().key();
4241 bool keyDying = IsScriptAboutToBeFinalized(&key.script);
4242 bool valDying = IsTypeObjectAboutToBeFinalized(e.front().value().unsafeGet());
4243 if (keyDying || valDying)
4244 e.removeFront();
4245 else if (key.script != e.front().key().script)
4246 e.rekeyFront(key);
4247 }
4248 }
4249 }
4250
4251 void
4252 JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
4253 {
4254 gcstats::AutoPhase ap(runtimeFromMainThread()->gcStats,
4255 gcstats::PHASE_SWEEP_TABLES_TYPE_OBJECT);
4256
4257 JS_ASSERT(zone()->isGCSweeping());
4258 if (table.initialized()) {
4259 for (TypeObjectWithNewScriptSet::Enum e(table); !e.empty(); e.popFront()) {
4260 TypeObjectWithNewScriptEntry entry = e.front();
4261 if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet())) {
4262 e.removeFront();
4263 } else if (entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)) {
4264 e.removeFront();
4265 } else if (entry.object != e.front().object) {
4266 TypeObjectWithNewScriptSet::Lookup lookup(entry.object->clasp(),
4267 entry.object->proto(),
4268 entry.newFunction);
4269 e.rekeyFront(lookup, entry);
4270 }
4271 }
4272 }
4273 }
4274
4275 TypeCompartment::~TypeCompartment()
4276 {
4277 js_delete(arrayTypeTable);
4278 js_delete(objectTypeTable);
4279 js_delete(allocationSiteTable);
4280 }
4281
4282 /* static */ void
4283 TypeScript::Sweep(FreeOp *fop, JSScript *script, bool *oom)
4284 {
4285 JSCompartment *compartment = script->compartment();
4286 JS_ASSERT(compartment->zone()->isGCSweeping());
4287
4288 unsigned num = NumTypeSets(script);
4289 StackTypeSet *typeArray = script->types->typeArray();
4290
4291 /* Remove constraints and references to dead objects from the persistent type sets. */
4292 for (unsigned i = 0; i < num; i++)
4293 typeArray[i].sweep(compartment->zone(), oom);
4294 }
4295
4296 void
4297 TypeScript::destroy()
4298 {
4299 js_free(this);
4300 }
4301
4302 void
4303 Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
4304 size_t *typePool,
4305 size_t *baselineStubsOptimized)
4306 {
4307 *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
4308 #ifdef JS_ION
4309 if (jitZone()) {
4310 *baselineStubsOptimized +=
4311 jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf);
4312 }
4313 #endif
4314 }
4315
4316 void
4317 TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
4318 size_t *allocationSiteTables,
4319 size_t *arrayTypeTables,
4320 size_t *objectTypeTables)
4321 {
4322 if (allocationSiteTable)
4323 *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
4324
4325 if (arrayTypeTable)
4326 *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf);
4327
4328 if (objectTypeTable) {
4329 *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf);
4330
4331 for (ObjectTypeTable::Enum e(*objectTypeTable);
4332 !e.empty();
4333 e.popFront())
4334 {
4335 const ObjectTableKey &key = e.front().key();
4336 const ObjectTableEntry &value = e.front().value();
4337
4338 /* key.ids and values.types have the same length. */
4339 *objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
4340 }
4341 }
4342 }
4343
4344 size_t
4345 TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
4346 {
4347 return mallocSizeOf(addendum);
4348 }
4349
4350 TypeZone::TypeZone(Zone *zone)
4351 : zone_(zone),
4352 typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
4353 compilerOutputs(nullptr),
4354 pendingRecompiles(nullptr)
4355 {
4356 }
4357
4358 TypeZone::~TypeZone()
4359 {
4360 js_delete(compilerOutputs);
4361 js_delete(pendingRecompiles);
4362 }
4363
4364 void
4365 TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
4366 {
4367 JS_ASSERT(zone()->isGCSweeping());
4368
4369 JSRuntime *rt = fop->runtime();
4370
4371 /*
4372 * Clear the analysis pool, but don't release its data yet. While
4373 * sweeping types any live data will be allocated into the pool.
4374 */
4375 LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize());
4376 oldAlloc.steal(&typeLifoAlloc);
4377
4378 /* Sweep and find compressed indexes for each compiler output. */
4379 size_t newCompilerOutputCount = 0;
4380
4381 #ifdef JS_ION
4382 if (compilerOutputs) {
4383 for (size_t i = 0; i < compilerOutputs->length(); i++) {
4384 CompilerOutput &output = (*compilerOutputs)[i];
4385 if (output.isValid()) {
4386 JSScript *script = output.script();
4387 if (IsScriptAboutToBeFinalized(&script)) {
4388 jit::GetIonScript(script, output.mode())->recompileInfoRef() = uint32_t(-1);
4389 output.invalidate();
4390 } else {
4391 output.setSweepIndex(newCompilerOutputCount++);
4392 }
4393 }
4394 }
4395 }
4396 #endif
4397
4398 {
4399 gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
4400
4401 for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
4402 JSScript *script = i.get<JSScript>();
4403 if (script->types) {
4404 types::TypeScript::Sweep(fop, script, oom);
4405
4406 if (releaseTypes) {
4407 if (script->hasParallelIonScript()) {
4408 #ifdef JS_ION
4409 // It's possible that we preserved the parallel
4410 // IonScript. The heuristic for their preservation is
4411 // independent of general JIT code preservation.
4412 MOZ_ASSERT(jit::ShouldPreserveParallelJITCode(rt, script));
4413 script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
4414 #else
4415 MOZ_CRASH();
4416 #endif
4417 } else {
4418 script->types->destroy();
4419 script->types = nullptr;
4420
4421 /*
4422 * Freeze constraints on stack type sets need to be
4423 * regenerated the next time the script is analyzed.
4424 */
4425 script->clearHasFreezeConstraints();
4426 }
4427
4428 JS_ASSERT(!script->hasIonScript());
4429 } else {
4430 /* Update the recompile indexes in any IonScripts still on the script. */
4431 if (script->hasIonScript())
4432 script->ionScript()->recompileInfoRef().shouldSweep(*this);
4433 if (script->hasParallelIonScript())
4434 script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
4435 }
4436 }
4437 }
4438 }
4439
4440 {
4441 gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES);
4442
4443 for (gc::CellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT);
4444 !iter.done(); iter.next())
4445 {
4446 TypeObject *object = iter.get<TypeObject>();
4447 object->sweep(fop, oom);
4448 }
4449
4450 for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
4451 comp->types.sweep(fop);
4452 }
4453
4454 if (compilerOutputs) {
4455 size_t sweepIndex = 0;
4456 for (size_t i = 0; i < compilerOutputs->length(); i++) {
4457 CompilerOutput output = (*compilerOutputs)[i];
4458 if (output.isValid()) {
4459 JS_ASSERT(sweepIndex == output.sweepIndex());
4460 output.invalidateSweepIndex();
4461 (*compilerOutputs)[sweepIndex++] = output;
4462 }
4463 }
4464 JS_ASSERT(sweepIndex == newCompilerOutputCount);
4465 JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
4466 }
4467
4468 {
4469 gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA);
4470 rt->freeLifoAlloc.transferFrom(&oldAlloc);
4471 }
4472 }
4473
4474 void
4475 TypeZone::clearAllNewScriptAddendumsOnOOM()
4476 {
4477 for (gc::CellIterUnderGC iter(zone(), gc::FINALIZE_TYPE_OBJECT);
4478 !iter.done(); iter.next())
4479 {
4480 TypeObject *object = iter.get<TypeObject>();
4481 object->maybeClearNewScriptAddendumOnOOM();
4482 }
4483 }
4484
4485 #ifdef DEBUG
4486 void
4487 TypeScript::printTypes(JSContext *cx, HandleScript script) const
4488 {
4489 JS_ASSERT(script->types == this);
4490
4491 if (!script->hasBaselineScript())
4492 return;
4493
4494 AutoEnterAnalysis enter(nullptr, script->compartment());
4495
4496 if (script->functionNonDelazifying())
4497 fprintf(stderr, "Function");
4498 else if (script->isForEval())
4499 fprintf(stderr, "Eval");
4500 else
4501 fprintf(stderr, "Main");
4502 fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno());
4503
4504 if (script->functionNonDelazifying()) {
4505 if (js::PropertyName *name = script->functionNonDelazifying()->name()) {
4506 const jschar *chars = name->getChars(nullptr);
4507 JSString::dumpChars(chars, name->length());
4508 }
4509 }
4510
4511 fprintf(stderr, "\n this:");
4512 TypeScript::ThisTypes(script)->print();
4513
4514 for (unsigned i = 0;
4515 script->functionNonDelazifying() && i < script->functionNonDelazifying()->nargs();
4516 i++)
4517 {
4518 fprintf(stderr, "\n arg%u:", i);
4519 TypeScript::ArgTypes(script, i)->print();
4520 }
4521 fprintf(stderr, "\n");
4522
4523 for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
4524 PrintBytecode(cx, script, pc);
4525
4526 if (js_CodeSpec[*pc].format & JOF_TYPESET) {
4527 StackTypeSet *types = TypeScript::BytecodeTypes(script, pc);
4528 fprintf(stderr, " typeset %u:", unsigned(types - typeArray()));
4529 types->print();
4530 fprintf(stderr, "\n");
4531 }
4532 }
4533
4534 fprintf(stderr, "\n");
4535 }
4536 #endif /* DEBUG */
4537
4538 /////////////////////////////////////////////////////////////////////
4539 // Binary data
4540 /////////////////////////////////////////////////////////////////////
4541
4542 void
4543 TypeObject::setAddendum(TypeObjectAddendum *addendum)
4544 {
4545 this->addendum = addendum;
4546 }
4547
4548 bool
4549 TypeObject::addTypedObjectAddendum(JSContext *cx, Handle<TypeDescr*> descr)
4550 {
4551 // Type descriptors are always pre-tenured. This is both because
4552 // we expect them to live a long time and so that they can be
4553 // safely accessed during ion compilation.
4554 JS_ASSERT(!IsInsideNursery(cx->runtime(), descr));
4555 JS_ASSERT(descr);
4556
4557 if (flags() & OBJECT_FLAG_ADDENDUM_CLEARED)
4558 return true;
4559
4560 JS_ASSERT(!unknownProperties());
4561
4562 if (addendum) {
4563 JS_ASSERT(hasTypedObject());
4564 JS_ASSERT(&typedObject()->descr() == descr);
4565 return true;
4566 }
4567
4568 TypeTypedObject *typedObject = js_new<TypeTypedObject>(descr);
4569 if (!typedObject)
4570 return false;
4571 addendum = typedObject;
4572 return true;
4573 }
4574
4575 /////////////////////////////////////////////////////////////////////
4576 // Type object addenda constructor
4577 /////////////////////////////////////////////////////////////////////
4578
4579 TypeObjectAddendum::TypeObjectAddendum(Kind kind)
4580 : kind(kind)
4581 {}
4582
4583 TypeNewScript::TypeNewScript()
4584 : TypeObjectAddendum(NewScript)
4585 {}
4586
4587 TypeTypedObject::TypeTypedObject(Handle<TypeDescr*> descr)
4588 : TypeObjectAddendum(TypedObject),
4589 descr_(descr)
4590 {
4591 }
4592
4593 TypeDescr &
4594 js::types::TypeTypedObject::descr() {
4595 return descr_->as<TypeDescr>();
4596 }

mercurial