|
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, ©)) { |
|
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 } |