1 : /* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
2 : /* vim: set ts=40 sw=4 et tw=99: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is the Mozilla SpiderMonkey bytecode type inference
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Foundation
20 : * Portions created by the Initial Developer are Copyright (C) 2010
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Brian Hackett <bhackett@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "jsapi.h"
41 : #include "jsautooplen.h"
42 : #include "jsbool.h"
43 : #include "jsdate.h"
44 : #include "jsexn.h"
45 : #include "jsfriendapi.h"
46 : #include "jsgc.h"
47 : #include "jsgcmark.h"
48 : #include "jsinfer.h"
49 : #include "jsmath.h"
50 : #include "jsnum.h"
51 : #include "jsobj.h"
52 : #include "jsscript.h"
53 : #include "jscntxt.h"
54 : #include "jsscope.h"
55 : #include "jsstr.h"
56 : #include "jsiter.h"
57 :
58 : #include "frontend/TokenStream.h"
59 : #include "js/MemoryMetrics.h"
60 : #include "methodjit/MethodJIT.h"
61 : #include "methodjit/Retcon.h"
62 : #ifdef JS_METHODJIT
63 : # include "assembler/assembler/MacroAssembler.h"
64 : #endif
65 :
66 : #include "jsatominlines.h"
67 : #include "jsgcinlines.h"
68 : #include "jsinferinlines.h"
69 : #include "jsobjinlines.h"
70 : #include "jsscriptinlines.h"
71 : #include "vm/Stack-inl.h"
72 :
73 : #ifdef JS_HAS_XML_SUPPORT
74 : #include "jsxml.h"
75 : #endif
76 :
77 : #ifdef __SUNPRO_CC
78 : #include <alloca.h>
79 : #endif
80 :
81 : using namespace js;
82 : using namespace js::types;
83 : using namespace js::analyze;
84 :
85 : static inline jsid
86 29756 : id_prototype(JSContext *cx) {
87 29756 : return ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
88 : }
89 :
90 : static inline jsid
91 : id_arguments(JSContext *cx) {
92 : return ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
93 : }
94 :
95 : static inline jsid
96 91 : id_length(JSContext *cx) {
97 91 : return ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
98 : }
99 :
100 : static inline jsid
101 766230 : id___proto__(JSContext *cx) {
102 766230 : return ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
103 : }
104 :
105 : static inline jsid
106 762024 : id_constructor(JSContext *cx) {
107 762024 : return ATOM_TO_JSID(cx->runtime->atomState.constructorAtom);
108 : }
109 :
110 : static inline jsid
111 760973 : id_caller(JSContext *cx) {
112 760973 : return ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
113 : }
114 :
115 : static inline jsid
116 : id_toString(JSContext *cx)
117 : {
118 : return ATOM_TO_JSID(cx->runtime->atomState.toStringAtom);
119 : }
120 :
121 : static inline jsid
122 : id_toSource(JSContext *cx)
123 : {
124 : return ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom);
125 : }
126 :
127 : #ifdef DEBUG
128 : const char *
129 194854 : types::TypeIdStringImpl(jsid id)
130 : {
131 194854 : if (JSID_IS_VOID(id))
132 21611 : return "(index)";
133 173243 : if (JSID_IS_EMPTY(id))
134 14627 : return "(new)";
135 : static char bufs[4][100];
136 : static unsigned which = 0;
137 158616 : which = (which + 1) & 3;
138 158616 : PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0);
139 158616 : return bufs[which];
140 : }
141 : #endif
142 :
143 : /////////////////////////////////////////////////////////////////////
144 : // Logging
145 : /////////////////////////////////////////////////////////////////////
146 :
147 24755550 : static bool InferSpewActive(SpewChannel channel)
148 : {
149 : static bool active[SPEW_COUNT];
150 : static bool checked = false;
151 24755550 : if (!checked) {
152 18667 : checked = true;
153 18667 : PodArrayZero(active);
154 18667 : const char *env = getenv("INFERFLAGS");
155 18667 : if (!env)
156 18667 : return false;
157 0 : if (strstr(env, "ops"))
158 0 : active[ISpewOps] = true;
159 0 : if (strstr(env, "result"))
160 0 : active[ISpewResult] = true;
161 0 : if (strstr(env, "full")) {
162 0 : for (unsigned i = 0; i < SPEW_COUNT; i++)
163 0 : active[i] = true;
164 : }
165 : }
166 24736883 : return active[channel];
167 : }
168 :
169 : #ifdef DEBUG
170 :
171 43707110 : static bool InferSpewColorable()
172 : {
173 : /* Only spew colors on xterm-color to not screw up emacs. */
174 43707110 : const char *env = getenv("TERM");
175 43707110 : if (!env)
176 0 : return false;
177 43707110 : return strcmp(env, "xterm-color") == 0;
178 : }
179 :
180 : const char *
181 21853555 : types::InferSpewColorReset()
182 : {
183 21853555 : if (!InferSpewColorable())
184 21853555 : return "";
185 0 : return "\x1b[0m";
186 : }
187 :
188 : const char *
189 10980490 : types::InferSpewColor(TypeConstraint *constraint)
190 : {
191 : /* Type constraints are printed out using foreground colors. */
192 : static const char *colors[] = { "\x1b[31m", "\x1b[32m", "\x1b[33m",
193 : "\x1b[34m", "\x1b[35m", "\x1b[36m",
194 : "\x1b[37m" };
195 10980490 : if (!InferSpewColorable())
196 10980490 : return "";
197 0 : return colors[DefaultHasher<TypeConstraint *>::hash(constraint) % 7];
198 : }
199 :
200 : const char *
201 10873065 : types::InferSpewColor(TypeSet *types)
202 : {
203 : /* Type sets are printed out using bold colors. */
204 : static const char *colors[] = { "\x1b[1;31m", "\x1b[1;32m", "\x1b[1;33m",
205 : "\x1b[1;34m", "\x1b[1;35m", "\x1b[1;36m",
206 : "\x1b[1;37m" };
207 10873065 : if (!InferSpewColorable())
208 10873065 : return "";
209 0 : return colors[DefaultHasher<TypeSet *>::hash(types) % 7];
210 : }
211 :
212 : const char *
213 14383362 : types::TypeString(Type type)
214 : {
215 14383362 : if (type.isPrimitive()) {
216 7156857 : switch (type.primitive()) {
217 : case JSVAL_TYPE_UNDEFINED:
218 4001858 : return "void";
219 : case JSVAL_TYPE_NULL:
220 67484 : return "null";
221 : case JSVAL_TYPE_BOOLEAN:
222 146142 : return "bool";
223 : case JSVAL_TYPE_INT32:
224 2220203 : return "int";
225 : case JSVAL_TYPE_DOUBLE:
226 240052 : return "float";
227 : case JSVAL_TYPE_STRING:
228 479759 : return "string";
229 : case JSVAL_TYPE_MAGIC:
230 1359 : return "lazyargs";
231 : default:
232 0 : JS_NOT_REACHED("Bad type");
233 : return "";
234 : }
235 : }
236 7226505 : if (type.isUnknown())
237 149954 : return "unknown";
238 7076551 : if (type.isAnyObject())
239 256335 : return " object";
240 :
241 : static char bufs[4][40];
242 : static unsigned which = 0;
243 6820216 : which = (which + 1) & 3;
244 :
245 6820216 : if (type.isSingleObject())
246 3581661 : JS_snprintf(bufs[which], 40, "<0x%p>", (void *) type.singleObject());
247 : else
248 3238555 : JS_snprintf(bufs[which], 40, "[0x%p]", (void *) type.typeObject());
249 :
250 6820216 : return bufs[which];
251 : }
252 :
253 : const char *
254 705168 : types::TypeObjectString(TypeObject *type)
255 : {
256 705168 : return TypeString(Type::ObjectType(type));
257 : }
258 :
259 10855057 : unsigned JSScript::id() {
260 10855057 : if (!id_) {
261 85313 : id_ = ++compartment()->types.scriptCount;
262 : InferSpew(ISpewOps, "script #%u: %p %s:%d",
263 85313 : id_, this, filename ? filename : "<null>", lineno);
264 : }
265 10855057 : return id_;
266 : }
267 :
268 : void
269 24714289 : types::InferSpew(SpewChannel channel, const char *fmt, ...)
270 : {
271 24714289 : if (!InferSpewActive(channel))
272 24714289 : return;
273 :
274 : va_list ap;
275 0 : va_start(ap, fmt);
276 0 : fprintf(stdout, "[infer] ");
277 0 : vfprintf(stdout, fmt, ap);
278 0 : fprintf(stdout, "\n");
279 0 : va_end(ap);
280 : }
281 :
282 : bool
283 9985003 : types::TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value)
284 : {
285 : /*
286 : * Check the correctness of the type information in the object's property
287 : * against an actual value.
288 : */
289 9985003 : if (cx->typeInferenceEnabled() && !obj->unknownProperties() && !value.isUndefined()) {
290 760973 : id = MakeTypeId(cx, id);
291 :
292 : /* Watch for properties which inference does not monitor. */
293 760973 : if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
294 0 : return true;
295 :
296 : /*
297 : * If we called in here while resolving a type constraint, we may be in the
298 : * middle of resolving a standard class and the type sets will not be updated
299 : * until the outer TypeSet::add finishes.
300 : */
301 760973 : if (cx->compartment->types.pendingCount)
302 0 : return true;
303 :
304 760973 : Type type = GetValueType(cx, value);
305 :
306 1521946 : AutoEnterTypeInference enter(cx);
307 :
308 : /*
309 : * We don't track types for properties inherited from prototypes which
310 : * haven't yet been accessed during analysis of the inheriting object.
311 : * Don't do the property instantiation now.
312 : */
313 760973 : TypeSet *types = obj->maybeGetProperty(cx, id);
314 760973 : if (!types)
315 0 : return true;
316 :
317 : /*
318 : * If the types inherited from prototypes are not being propagated into
319 : * this set (because we haven't analyzed code which accesses the
320 : * property), skip.
321 : */
322 760973 : if (!types->hasPropagatedProperty())
323 613985 : return true;
324 :
325 146988 : if (!types->hasType(type)) {
326 : TypeFailure(cx, "Missing type in object %s %s: %s",
327 0 : TypeObjectString(obj), TypeIdString(id), TypeString(type));
328 : }
329 : }
330 9371018 : return true;
331 : }
332 :
333 : #endif
334 :
335 : void
336 0 : types::TypeFailure(JSContext *cx, const char *fmt, ...)
337 : {
338 : char msgbuf[1024]; /* Larger error messages will be truncated */
339 : char errbuf[1024];
340 :
341 : va_list ap;
342 0 : va_start(ap, fmt);
343 0 : JS_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
344 0 : va_end(ap);
345 :
346 0 : JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf);
347 :
348 : /* Dump type state, even if INFERFLAGS is unset. */
349 0 : cx->compartment->types.print(cx, true);
350 :
351 : /* Always active, even in release builds */
352 0 : MOZ_Assert(msgbuf, __FILE__, __LINE__);
353 :
354 0 : *((volatile int *)NULL) = 0; /* Should never be reached */
355 0 : }
356 :
357 : /////////////////////////////////////////////////////////////////////
358 : // TypeSet
359 : /////////////////////////////////////////////////////////////////////
360 :
361 : TypeSet *
362 1112 : TypeSet::make(JSContext *cx, const char *name)
363 : {
364 1112 : JS_ASSERT(cx->compartment->activeInference);
365 :
366 1112 : TypeSet *res = cx->typeLifoAlloc().new_<TypeSet>();
367 1112 : if (!res) {
368 0 : cx->compartment->types.setPendingNukeTypes(cx);
369 0 : return NULL;
370 : }
371 :
372 : InferSpew(ISpewOps, "typeSet: %sT%p%s intermediate %s",
373 : InferSpewColor(res), res, InferSpewColorReset(),
374 1112 : name);
375 :
376 1112 : return res;
377 : }
378 :
379 : inline void
380 4659922 : TypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
381 : {
382 4659922 : if (!constraint) {
383 : /* OOM failure while constructing the constraint. */
384 0 : cx->compartment->types.setPendingNukeTypes(cx);
385 0 : return;
386 : }
387 :
388 4659922 : JS_ASSERT(cx->compartment->activeInference);
389 :
390 : InferSpew(ISpewOps, "addConstraint: %sT%p%s %sC%p%s %s",
391 : InferSpewColor(this), this, InferSpewColorReset(),
392 : InferSpewColor(constraint), constraint, InferSpewColorReset(),
393 4659922 : constraint->kind());
394 :
395 4659922 : JS_ASSERT(constraint->next == NULL);
396 4659922 : constraint->next = constraintList;
397 4659922 : constraintList = constraint;
398 :
399 4659922 : if (!callExisting)
400 2074818 : return;
401 :
402 : /* If any type is possible, there's no need to worry about specifics. */
403 2585104 : if (flags & TYPE_FLAG_UNKNOWN) {
404 23499 : cx->compartment->types.addPending(cx, constraint, this, Type::UnknownType());
405 : } else {
406 : /* Enqueue type set members stored as bits. */
407 20492840 : for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
408 17931235 : if (flags & flag) {
409 774498 : Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
410 774498 : cx->compartment->types.addPending(cx, constraint, this, type);
411 : }
412 : }
413 :
414 : /* If any object is possible, skip specifics. */
415 2561605 : if (flags & TYPE_FLAG_ANYOBJECT) {
416 11284 : cx->compartment->types.addPending(cx, constraint, this, Type::AnyObjectType());
417 : } else {
418 : /* Enqueue specific object types. */
419 2550321 : unsigned count = getObjectCount();
420 3582371 : for (unsigned i = 0; i < count; i++) {
421 1032050 : TypeObjectKey *object = getObject(i);
422 1032050 : if (object)
423 : cx->compartment->types.addPending(cx, constraint, this,
424 583578 : Type::ObjectType(object));
425 : }
426 : }
427 : }
428 :
429 2585104 : cx->compartment->types.resolvePending(cx);
430 : }
431 :
432 : void
433 0 : TypeSet::print(JSContext *cx)
434 : {
435 0 : if (flags & TYPE_FLAG_OWN_PROPERTY)
436 0 : printf(" [own]");
437 0 : if (flags & TYPE_FLAG_CONFIGURED_PROPERTY)
438 0 : printf(" [configured]");
439 :
440 0 : if (isDefiniteProperty())
441 0 : printf(" [definite:%d]", definiteSlot());
442 :
443 0 : if (baseFlags() == 0 && !baseObjectCount()) {
444 0 : printf(" missing");
445 0 : return;
446 : }
447 :
448 0 : if (flags & TYPE_FLAG_UNKNOWN)
449 0 : printf(" unknown");
450 0 : if (flags & TYPE_FLAG_ANYOBJECT)
451 0 : printf(" object");
452 :
453 0 : if (flags & TYPE_FLAG_UNDEFINED)
454 0 : printf(" void");
455 0 : if (flags & TYPE_FLAG_NULL)
456 0 : printf(" null");
457 0 : if (flags & TYPE_FLAG_BOOLEAN)
458 0 : printf(" bool");
459 0 : if (flags & TYPE_FLAG_INT32)
460 0 : printf(" int");
461 0 : if (flags & TYPE_FLAG_DOUBLE)
462 0 : printf(" float");
463 0 : if (flags & TYPE_FLAG_STRING)
464 0 : printf(" string");
465 0 : if (flags & TYPE_FLAG_LAZYARGS)
466 0 : printf(" lazyargs");
467 :
468 0 : uint32_t objectCount = baseObjectCount();
469 0 : if (objectCount) {
470 0 : printf(" object[%u]", objectCount);
471 :
472 0 : unsigned count = getObjectCount();
473 0 : for (unsigned i = 0; i < count; i++) {
474 0 : TypeObjectKey *object = getObject(i);
475 0 : if (object)
476 0 : printf(" %s", TypeString(Type::ObjectType(object)));
477 : }
478 : }
479 : }
480 :
481 : bool
482 38 : TypeSet::propertyNeedsBarrier(JSContext *cx, jsid id)
483 : {
484 38 : id = MakeTypeId(cx, id);
485 :
486 38 : if (unknownObject())
487 24 : return true;
488 :
489 14 : for (unsigned i = 0; i < getObjectCount(); i++) {
490 6 : if (getSingleObject(i))
491 0 : return true;
492 :
493 6 : if (types::TypeObject *otype = getTypeObject(i)) {
494 6 : if (otype->unknownProperties())
495 0 : return true;
496 :
497 6 : if (types::TypeSet *propTypes = otype->maybeGetProperty(cx, id)) {
498 6 : if (propTypes->needsBarrier(cx))
499 6 : return true;
500 : }
501 : }
502 : }
503 :
504 8 : addFreeze(cx);
505 8 : return false;
506 : }
507 :
508 : /////////////////////////////////////////////////////////////////////
509 : // TypeSet constraints
510 : /////////////////////////////////////////////////////////////////////
511 :
512 : /* Standard subset constraint, propagate all types from one set to another. */
513 : class TypeConstraintSubset : public TypeConstraint
514 : {
515 : public:
516 : TypeSet *target;
517 :
518 1096064 : TypeConstraintSubset(TypeSet *target)
519 1096064 : : TypeConstraint("subset"), target(target)
520 : {
521 1096064 : JS_ASSERT(target);
522 1096064 : }
523 :
524 1040367 : void newType(JSContext *cx, TypeSet *source, Type type)
525 : {
526 : /* Basic subset constraint, move all types to the target. */
527 1040367 : target->addType(cx, type);
528 1040367 : }
529 : };
530 :
531 : void
532 1096064 : TypeSet::addSubset(JSContext *cx, TypeSet *target)
533 : {
534 1096064 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubset>(target));
535 1096064 : }
536 :
537 : /* Constraints for reads/writes on object properties. */
538 : class TypeConstraintProp : public TypeConstraint
539 : {
540 : public:
541 : JSScript *script;
542 : jsbytecode *pc;
543 :
544 : /*
545 : * If assign is true, the target is used to update a property of the object.
546 : * If assign is false, the target is assigned the value of the property.
547 : */
548 : bool assign;
549 : TypeSet *target;
550 :
551 : /* Property being accessed. */
552 : jsid id;
553 :
554 54633 : TypeConstraintProp(JSScript *script, jsbytecode *pc,
555 : TypeSet *target, jsid id, bool assign)
556 : : TypeConstraint("prop"), script(script), pc(pc),
557 54633 : assign(assign), target(target), id(id)
558 : {
559 54633 : JS_ASSERT(script && pc && target);
560 54633 : }
561 :
562 : void newType(JSContext *cx, TypeSet *source, Type type);
563 : };
564 :
565 : void
566 43716 : TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
567 : TypeSet *target, jsid id)
568 : {
569 43716 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintProp>(script, pc, target, id, false));
570 43716 : }
571 :
572 : void
573 10917 : TypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
574 : TypeSet *target, jsid id)
575 : {
576 10917 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintProp>(script, pc, target, id, true));
577 10917 : }
578 :
579 : /*
580 : * Constraints for updating the 'this' types of callees on CALLPROP/CALLELEM.
581 : * These are derived from the types on the properties themselves, rather than
582 : * those pushed in the 'this' slot at the call site, which allows us to retain
583 : * correlations between the type of the 'this' object and the associated
584 : * callee scripts at polymorphic call sites.
585 : */
586 : class TypeConstraintCallProp : public TypeConstraint
587 : {
588 : public:
589 : JSScript *script;
590 : jsbytecode *callpc;
591 :
592 : /* Property being accessed. */
593 : jsid id;
594 :
595 16450 : TypeConstraintCallProp(JSScript *script, jsbytecode *callpc, jsid id)
596 16450 : : TypeConstraint("callprop"), script(script), callpc(callpc), id(id)
597 : {
598 16450 : JS_ASSERT(script && callpc);
599 16450 : }
600 :
601 : void newType(JSContext *cx, TypeSet *source, Type type);
602 : };
603 :
604 : void
605 16450 : TypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id)
606 : {
607 : /*
608 : * For calls which will go through JSOP_NEW, don't add any constraints to
609 : * modify the 'this' types of callees. The initial 'this' value will be
610 : * outright ignored.
611 : */
612 16450 : jsbytecode *callpc = script->analysis()->getCallPC(pc);
613 16450 : if (JSOp(*callpc) == JSOP_NEW)
614 0 : return;
615 :
616 16450 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintCallProp>(script, callpc, id));
617 : }
618 :
619 : /*
620 : * Constraints for generating 'set' property constraints on a SETELEM only if
621 : * the element type may be a number. For SETELEM we only account for integer
622 : * indexes, and if the element cannot be an integer (e.g. it must be a string)
623 : * then we lose precision by treating it like one.
624 : */
625 : class TypeConstraintSetElement : public TypeConstraint
626 : {
627 : public:
628 : JSScript *script;
629 : jsbytecode *pc;
630 :
631 : TypeSet *objectTypes;
632 : TypeSet *valueTypes;
633 :
634 4268 : TypeConstraintSetElement(JSScript *script, jsbytecode *pc,
635 : TypeSet *objectTypes, TypeSet *valueTypes)
636 : : TypeConstraint("setelement"), script(script), pc(pc),
637 4268 : objectTypes(objectTypes), valueTypes(valueTypes)
638 : {
639 4268 : JS_ASSERT(script && pc);
640 4268 : }
641 :
642 : void newType(JSContext *cx, TypeSet *source, Type type);
643 : };
644 :
645 : void
646 4268 : TypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
647 : TypeSet *objectTypes, TypeSet *valueTypes)
648 : {
649 4268 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintSetElement>(script, pc, objectTypes,
650 4268 : valueTypes));
651 4268 : }
652 :
653 : /*
654 : * Constraints for watching call edges as they are discovered and invoking native
655 : * function handlers, adding constraints for arguments, receiver objects and the
656 : * return value, and updating script foundOffsets.
657 : */
658 : class TypeConstraintCall : public TypeConstraint
659 : {
660 : public:
661 : /* Call site being tracked. */
662 : TypeCallsite *callsite;
663 :
664 57521 : TypeConstraintCall(TypeCallsite *callsite)
665 57521 : : TypeConstraint("call"), callsite(callsite)
666 57521 : {}
667 :
668 : void newType(JSContext *cx, TypeSet *source, Type type);
669 : };
670 :
671 : void
672 57521 : TypeSet::addCall(JSContext *cx, TypeCallsite *site)
673 : {
674 57521 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintCall>(site));
675 57521 : }
676 :
677 : /* Constraints for arithmetic operations. */
678 : class TypeConstraintArith : public TypeConstraint
679 : {
680 : public:
681 : JSScript *script;
682 : jsbytecode *pc;
683 :
684 : /* Type set receiving the result of the arithmetic. */
685 : TypeSet *target;
686 :
687 : /* For addition operations, the other operand. */
688 : TypeSet *other;
689 :
690 699973 : TypeConstraintArith(JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
691 699973 : : TypeConstraint("arith"), script(script), pc(pc), target(target), other(other)
692 : {
693 699973 : JS_ASSERT(target);
694 699973 : }
695 :
696 : void newType(JSContext *cx, TypeSet *source, Type type);
697 : };
698 :
699 : void
700 699973 : TypeSet::addArith(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
701 : {
702 699973 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintArith>(script, pc, target, other));
703 699973 : }
704 :
705 : /* Subset constraint which transforms primitive values into appropriate objects. */
706 : class TypeConstraintTransformThis : public TypeConstraint
707 : {
708 : public:
709 : JSScript *script;
710 : TypeSet *target;
711 :
712 21585 : TypeConstraintTransformThis(JSScript *script, TypeSet *target)
713 21585 : : TypeConstraint("transformthis"), script(script), target(target)
714 21585 : {}
715 :
716 : void newType(JSContext *cx, TypeSet *source, Type type);
717 : };
718 :
719 : void
720 21585 : TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
721 : {
722 21585 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintTransformThis>(script, target));
723 21585 : }
724 :
725 : /*
726 : * Constraint which adds a particular type to the 'this' types of all
727 : * discovered scripted functions.
728 : */
729 : class TypeConstraintPropagateThis : public TypeConstraint
730 : {
731 : public:
732 : JSScript *script;
733 : jsbytecode *callpc;
734 : Type type;
735 : TypeSet *types;
736 :
737 47178 : TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, TypeSet *types)
738 47178 : : TypeConstraint("propagatethis"), script(script), callpc(callpc), type(type), types(types)
739 47178 : {}
740 :
741 : void newType(JSContext *cx, TypeSet *source, Type type);
742 : };
743 :
744 : void
745 31217 : TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type, TypeSet *types)
746 : {
747 : /* Don't add constraints when the call will be 'new' (see addCallProperty). */
748 31217 : jsbytecode *callpc = script->analysis()->getCallPC(pc);
749 31217 : if (JSOp(*callpc) == JSOP_NEW)
750 0 : return;
751 :
752 31217 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintPropagateThis>(script, callpc, type, types));
753 : }
754 :
755 : /* Subset constraint which filters out primitive types. */
756 : class TypeConstraintFilterPrimitive : public TypeConstraint
757 : {
758 : public:
759 : TypeSet *target;
760 : TypeSet::FilterKind filter;
761 :
762 4693 : TypeConstraintFilterPrimitive(TypeSet *target, TypeSet::FilterKind filter)
763 4693 : : TypeConstraint("filter"), target(target), filter(filter)
764 4693 : {}
765 :
766 4703 : void newType(JSContext *cx, TypeSet *source, Type type)
767 : {
768 4703 : switch (filter) {
769 : case TypeSet::FILTER_ALL_PRIMITIVES:
770 4703 : if (type.isPrimitive())
771 4695 : return;
772 8 : break;
773 :
774 : case TypeSet::FILTER_NULL_VOID:
775 0 : if (type.isPrimitive(JSVAL_TYPE_NULL) || type.isPrimitive(JSVAL_TYPE_UNDEFINED))
776 0 : return;
777 0 : break;
778 :
779 : case TypeSet::FILTER_VOID:
780 0 : if (type.isPrimitive(JSVAL_TYPE_UNDEFINED))
781 0 : return;
782 0 : break;
783 :
784 : default:
785 0 : JS_NOT_REACHED("Bad filter");
786 : }
787 :
788 8 : target->addType(cx, type);
789 : }
790 : };
791 :
792 : void
793 4693 : TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter)
794 : {
795 4693 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintFilterPrimitive>(target, filter));
796 4693 : }
797 :
798 : /* If id is a normal slotful 'own' property of an object, get its shape. */
799 : static inline const Shape *
800 426608 : GetSingletonShape(JSContext *cx, JSObject *obj, jsid id)
801 : {
802 426608 : const Shape *shape = obj->nativeLookup(cx, id);
803 426608 : if (shape && shape->hasDefaultGetter() && shape->hasSlot())
804 417230 : return shape;
805 9378 : return NULL;
806 : }
807 :
808 : void
809 326091 : ScriptAnalysis::pruneTypeBarriers(JSContext *cx, uint32_t offset)
810 : {
811 326091 : TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
812 1170568 : while (*pbarrier) {
813 518386 : TypeBarrier *barrier = *pbarrier;
814 518386 : if (barrier->target->hasType(barrier->type)) {
815 : /* Barrier is now obsolete, it can be removed. */
816 45280 : *pbarrier = barrier->next;
817 45280 : continue;
818 : }
819 473106 : if (barrier->singleton) {
820 18661 : JS_ASSERT(barrier->type.isPrimitive(JSVAL_TYPE_UNDEFINED));
821 18661 : const Shape *shape = GetSingletonShape(cx, barrier->singleton, barrier->singletonId);
822 18661 : if (shape && !barrier->singleton->nativeGetSlot(shape->slot()).isUndefined()) {
823 : /*
824 : * When we analyzed the script the singleton had an 'own'
825 : * property which was undefined (probably a 'var' variable
826 : * added to a global object), but now it is defined. The only
827 : * way it can become undefined again is if an explicit assign
828 : * or deletion on the property occurs, which will update the
829 : * type set for the property directly and trigger construction
830 : * of a normal type barrier.
831 : */
832 4988 : *pbarrier = barrier->next;
833 4988 : continue;
834 : }
835 : }
836 468118 : pbarrier = &barrier->next;
837 : }
838 326091 : }
839 :
840 : /*
841 : * Cheesy limit on the number of objects we will tolerate in an observed type
842 : * set before refusing to add new type barriers for objects.
843 : * :FIXME: this heuristic sucks, and doesn't handle calls.
844 : */
845 : static const uint32_t BARRIER_OBJECT_LIMIT = 10;
846 :
847 28097 : void ScriptAnalysis::breakTypeBarriers(JSContext *cx, uint32_t offset, bool all)
848 : {
849 28097 : pruneTypeBarriers(cx, offset);
850 :
851 28097 : bool resetResolving = !cx->compartment->types.resolving;
852 28097 : if (resetResolving)
853 28097 : cx->compartment->types.resolving = true;
854 :
855 28097 : TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
856 86239 : while (*pbarrier) {
857 30045 : TypeBarrier *barrier = *pbarrier;
858 30045 : if (barrier->target->hasType(barrier->type) ) {
859 : /*
860 : * Barrier is now obsolete, it can be removed. This is not
861 : * redundant with the pruneTypeBarriers() call above, as breaking
862 : * previous type barriers may have modified the target type set.
863 : */
864 0 : *pbarrier = barrier->next;
865 30045 : } else if (all) {
866 : /* Force removal of the barrier. */
867 124 : barrier->target->addType(cx, barrier->type);
868 124 : *pbarrier = barrier->next;
869 92175 : } else if (!barrier->type.isUnknown() &&
870 24220 : !barrier->type.isAnyObject() &&
871 23002 : barrier->type.isObject() &&
872 15032 : barrier->target->getObjectCount() >= BARRIER_OBJECT_LIMIT) {
873 : /* Maximum number of objects in the set exceeded. */
874 16 : barrier->target->addType(cx, barrier->type);
875 16 : *pbarrier = barrier->next;
876 : } else {
877 29905 : pbarrier = &barrier->next;
878 : }
879 : }
880 :
881 28097 : if (resetResolving) {
882 28097 : cx->compartment->types.resolving = false;
883 28097 : cx->compartment->types.resolvePending(cx);
884 : }
885 28097 : }
886 :
887 79 : void ScriptAnalysis::breakTypeBarriersSSA(JSContext *cx, const SSAValue &v)
888 : {
889 79 : if (v.kind() != SSAValue::PUSHED)
890 0 : return;
891 :
892 79 : uint32_t offset = v.pushedOffset();
893 79 : if (JSOp(script->code[offset]) == JSOP_GETPROP)
894 32 : breakTypeBarriersSSA(cx, poppedValue(offset, 0));
895 :
896 79 : breakTypeBarriers(cx, offset, true);
897 : }
898 :
899 : /*
900 : * Subset constraint for property reads and argument passing which can add type
901 : * barriers on the read instead of passing types along.
902 : */
903 : class TypeConstraintSubsetBarrier : public TypeConstraint
904 : {
905 : public:
906 : JSScript *script;
907 : jsbytecode *pc;
908 : TypeSet *target;
909 :
910 487008 : TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target)
911 487008 : : TypeConstraint("subsetBarrier"), script(script), pc(pc), target(target)
912 487008 : {}
913 :
914 663209 : void newType(JSContext *cx, TypeSet *source, Type type)
915 : {
916 663209 : if (!target->hasType(type))
917 519812 : script->analysis()->addTypeBarrier(cx, pc, target, type);
918 663209 : }
919 : };
920 :
921 : void
922 487008 : TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
923 : {
924 487008 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubsetBarrier>(script, pc, target));
925 487008 : }
926 :
927 : /////////////////////////////////////////////////////////////////////
928 : // TypeConstraint
929 : /////////////////////////////////////////////////////////////////////
930 :
931 : /* Get the object to use for a property access on type. */
932 : static inline TypeObject *
933 77881 : GetPropertyObject(JSContext *cx, JSScript *script, Type type)
934 : {
935 77881 : if (type.isTypeObject())
936 50503 : return type.typeObject();
937 :
938 : /* Force instantiation of lazy types for singleton objects. */
939 27378 : if (type.isSingleObject())
940 14667 : return type.singleObject()->getType(cx);
941 :
942 : /*
943 : * Handle properties attached to primitive types, treating this access as a
944 : * read on the primitive's new object.
945 : */
946 12711 : TypeObject *object = NULL;
947 12711 : switch (type.primitive()) {
948 :
949 : case JSVAL_TYPE_INT32:
950 : case JSVAL_TYPE_DOUBLE:
951 339 : object = TypeScript::StandardType(cx, script, JSProto_Number);
952 339 : break;
953 :
954 : case JSVAL_TYPE_BOOLEAN:
955 74 : object = TypeScript::StandardType(cx, script, JSProto_Boolean);
956 74 : break;
957 :
958 : case JSVAL_TYPE_STRING:
959 6248 : object = TypeScript::StandardType(cx, script, JSProto_String);
960 6248 : break;
961 :
962 : default:
963 : /* undefined, null and lazy arguments do not have properties. */
964 6050 : return NULL;
965 : }
966 :
967 6661 : if (!object)
968 0 : cx->compartment->types.setPendingNukeTypes(cx);
969 6661 : return object;
970 : }
971 :
972 : static inline bool
973 450960 : UsePropertyTypeBarrier(jsbytecode *pc)
974 : {
975 : /*
976 : * At call opcodes, type barriers can only be added for the call bindings,
977 : * which TypeConstraintCall will add barrier constraints for directly.
978 : */
979 450960 : uint32_t format = js_CodeSpec[*pc].format;
980 450960 : return (format & JOF_TYPESET) && !(format & JOF_INVOKE);
981 : }
982 :
983 : static inline void
984 6588 : MarkPropertyAccessUnknown(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
985 : {
986 6588 : if (UsePropertyTypeBarrier(pc))
987 6577 : script->analysis()->addTypeBarrier(cx, pc, target, Type::UnknownType());
988 : else
989 11 : target->addType(cx, Type::UnknownType());
990 6588 : }
991 :
992 : /*
993 : * Handle a property access on a specific object. All property accesses go through
994 : * here, whether via x.f, x[f], or global name accesses.
995 : */
996 : static inline void
997 493411 : PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object,
998 : bool assign, TypeSet *target, jsid id)
999 : {
1000 : /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
1001 493411 : if (object->unknownProperties()) {
1002 45 : if (!assign)
1003 27 : MarkPropertyAccessUnknown(cx, script, pc, target);
1004 45 : return;
1005 : }
1006 :
1007 : /* Capture the effects of a standard property access. */
1008 493366 : TypeSet *types = object->getProperty(cx, id, assign);
1009 493366 : if (!types)
1010 0 : return;
1011 493366 : if (assign) {
1012 48994 : target->addSubset(cx, types);
1013 : } else {
1014 444372 : if (!types->hasPropagatedProperty())
1015 43492 : object->getFromPrototypes(cx, id, types);
1016 444372 : if (UsePropertyTypeBarrier(pc)) {
1017 444272 : types->addSubsetBarrier(cx, script, pc, target);
1018 444272 : if (object->singleton && !JSID_IS_VOID(id)) {
1019 : /*
1020 : * Add a singleton type barrier on the object if it has an
1021 : * 'own' property which is currently undefined. We'll be able
1022 : * to remove the barrier after the property becomes defined,
1023 : * even if no undefined value is ever observed at pc.
1024 : */
1025 407947 : const Shape *shape = GetSingletonShape(cx, object->singleton, id);
1026 407947 : if (shape && object->singleton->nativeGetSlot(shape->slot()).isUndefined())
1027 14055 : script->analysis()->addSingletonTypeBarrier(cx, pc, target, object->singleton, id);
1028 : }
1029 : } else {
1030 100 : types->addSubset(cx, target);
1031 : }
1032 : }
1033 : }
1034 :
1035 : /* Whether the JSObject/TypeObject referent of an access on type cannot be determined. */
1036 : static inline bool
1037 87632 : UnknownPropertyAccess(JSScript *script, Type type)
1038 : {
1039 87632 : return type.isUnknown()
1040 86926 : || type.isAnyObject()
1041 174558 : || (!type.isObject() && !script->hasGlobal());
1042 : }
1043 :
1044 : void
1045 68673 : TypeConstraintProp::newType(JSContext *cx, TypeSet *source, Type type)
1046 : {
1047 68673 : if (UnknownPropertyAccess(script, type)) {
1048 : /*
1049 : * Access on an unknown object. Reads produce an unknown result, writes
1050 : * need to be monitored.
1051 : */
1052 7389 : if (assign)
1053 928 : cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
1054 : else
1055 6461 : MarkPropertyAccessUnknown(cx, script, pc, target);
1056 7389 : return;
1057 : }
1058 :
1059 61284 : if (type.isPrimitive(JSVAL_TYPE_MAGIC)) {
1060 : /* Ignore cases which will be accounted for by the followEscapingArguments analysis. */
1061 191 : if (assign || (id != JSID_VOID && id != id_length(cx)))
1062 0 : return;
1063 :
1064 191 : if (id == JSID_VOID)
1065 100 : MarkPropertyAccessUnknown(cx, script, pc, target);
1066 : else
1067 91 : target->addType(cx, Type::Int32Type());
1068 191 : return;
1069 : }
1070 :
1071 61093 : TypeObject *object = GetPropertyObject(cx, script, type);
1072 61093 : if (object)
1073 55868 : PropertyAccess(cx, script, pc, object, assign, target, id);
1074 : }
1075 :
1076 : void
1077 18959 : TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type)
1078 : {
1079 : /*
1080 : * For CALLPROP, we need to update not just the pushed types but also the
1081 : * 'this' types of possible callees. If we can't figure out that set of
1082 : * callees, monitor the call to make sure discovered callees get their
1083 : * 'this' types updated.
1084 : */
1085 :
1086 18959 : if (UnknownPropertyAccess(script, type)) {
1087 2171 : cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
1088 2171 : return;
1089 : }
1090 :
1091 16788 : TypeObject *object = GetPropertyObject(cx, script, type);
1092 16788 : if (object) {
1093 15963 : if (object->unknownProperties()) {
1094 2 : cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
1095 : } else {
1096 15961 : TypeSet *types = object->getProperty(cx, id, false);
1097 15961 : if (!types)
1098 0 : return;
1099 15961 : if (!types->hasPropagatedProperty())
1100 0 : object->getFromPrototypes(cx, id, types);
1101 : /* Bypass addPropagateThis, we already have the callpc. */
1102 15961 : types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintPropagateThis>(
1103 31922 : script, callpc, type, (TypeSet *) NULL));
1104 : }
1105 : }
1106 : }
1107 :
1108 : void
1109 5117 : TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type)
1110 : {
1111 11736 : if (type.isUnknown() ||
1112 5081 : type.isPrimitive(JSVAL_TYPE_INT32) ||
1113 1538 : type.isPrimitive(JSVAL_TYPE_DOUBLE)) {
1114 3589 : objectTypes->addSetProperty(cx, script, pc, valueTypes, JSID_VOID);
1115 : }
1116 5117 : }
1117 :
1118 : void
1119 63335 : TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
1120 : {
1121 63335 : JSScript *script = callsite->script;
1122 63335 : jsbytecode *pc = callsite->pc;
1123 :
1124 63335 : if (type.isUnknown() || type.isAnyObject()) {
1125 : /* Monitor calls on unknown functions. */
1126 2165 : cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
1127 2165 : return;
1128 : }
1129 :
1130 61170 : JSFunction *callee = NULL;
1131 :
1132 61170 : if (type.isSingleObject()) {
1133 56090 : JSObject *obj = type.singleObject();
1134 :
1135 56090 : if (!obj->isFunction()) {
1136 : /* Calls on non-functions are dynamically monitored. */
1137 6 : return;
1138 : }
1139 :
1140 56084 : if (obj->toFunction()->isNative()) {
1141 : /*
1142 : * The return value and all side effects within native calls should
1143 : * be dynamically monitored, except when the compiler is generating
1144 : * specialized inline code or stub calls for a specific natives and
1145 : * knows about the behavior of that native.
1146 : */
1147 31898 : cx->compartment->types.monitorBytecode(cx, script, pc - script->code, true);
1148 :
1149 : /*
1150 : * Add type constraints capturing the possible behavior of
1151 : * specialized natives which operate on properties. :XXX: use
1152 : * better factoring for both this and the compiler code itself
1153 : * which specializes particular natives.
1154 : */
1155 :
1156 31898 : Native native = obj->toFunction()->native();
1157 :
1158 31898 : if (native == js::array_push) {
1159 3522 : for (size_t i = 0; i < callsite->argumentCount; i++) {
1160 : callsite->thisTypes->addSetProperty(cx, script, pc,
1161 1762 : callsite->argumentTypes[i], JSID_VOID);
1162 : }
1163 : }
1164 :
1165 31898 : if (native == js::array_pop || native == js::array_shift)
1166 113 : callsite->thisTypes->addGetProperty(cx, script, pc, callsite->returnTypes, JSID_VOID);
1167 :
1168 31898 : if (native == js_Array) {
1169 424 : TypeObject *res = TypeScript::InitObject(cx, script, pc, JSProto_Array);
1170 424 : if (!res)
1171 0 : return;
1172 :
1173 424 : callsite->returnTypes->addType(cx, Type::ObjectType(res));
1174 :
1175 424 : if (callsite->argumentCount >= 2) {
1176 190 : for (unsigned i = 0; i < callsite->argumentCount; i++) {
1177 : PropertyAccess(cx, script, pc, res, true,
1178 148 : callsite->argumentTypes[i], JSID_VOID);
1179 : }
1180 : }
1181 : }
1182 :
1183 31898 : return;
1184 : }
1185 :
1186 24186 : callee = obj->toFunction();
1187 5080 : } else if (type.isTypeObject()) {
1188 3979 : callee = type.typeObject()->interpretedFunction;
1189 3979 : if (!callee)
1190 27 : return;
1191 : } else {
1192 : /* Calls on non-objects are dynamically monitored. */
1193 1101 : return;
1194 : }
1195 :
1196 28138 : if (!callee->script()->ensureHasTypes(cx))
1197 0 : return;
1198 :
1199 28138 : unsigned nargs = callee->nargs;
1200 :
1201 : /* Add bindings for the arguments of the call. */
1202 65036 : for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) {
1203 36898 : TypeSet *argTypes = callsite->argumentTypes[i];
1204 36898 : TypeSet *types = TypeScript::ArgTypes(callee->script(), i);
1205 36898 : argTypes->addSubsetBarrier(cx, script, pc, types);
1206 : }
1207 :
1208 : /* Add void type for any formals in the callee not supplied at the call site. */
1209 62586 : for (unsigned i = callsite->argumentCount; i < nargs; i++) {
1210 34448 : TypeSet *types = TypeScript::ArgTypes(callee->script(), i);
1211 34448 : types->addType(cx, Type::UndefinedType());
1212 : }
1213 :
1214 28138 : TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
1215 28138 : TypeSet *returnTypes = TypeScript::ReturnTypes(callee->script());
1216 :
1217 28138 : if (callsite->isNew) {
1218 : /*
1219 : * If the script does not return a value then the pushed value is the
1220 : * new object (typical case). Note that we don't model construction of
1221 : * the new value, which is done dynamically; we don't keep track of the
1222 : * possible 'new' types for a given prototype type object.
1223 : */
1224 4693 : thisTypes->addSubset(cx, callsite->returnTypes);
1225 : returnTypes->addFilterPrimitives(cx, callsite->returnTypes,
1226 4693 : TypeSet::FILTER_ALL_PRIMITIVES);
1227 : } else {
1228 : /*
1229 : * Add a binding for the return value of the call. We don't add a
1230 : * binding for the receiver object, as this is done with PropagateThis
1231 : * constraints added by the original JSOP_CALL* op. The type sets we
1232 : * manipulate here have lost any correlations between particular types
1233 : * in the 'this' and 'callee' sets, which we want to maintain for
1234 : * polymorphic JSOP_CALLPROP invocations.
1235 : */
1236 23445 : returnTypes->addSubset(cx, callsite->returnTypes);
1237 : }
1238 : }
1239 :
1240 : void
1241 53753 : TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type)
1242 : {
1243 53753 : if (type.isUnknown() || type.isAnyObject()) {
1244 : /*
1245 : * The callee is unknown, make sure the call is monitored so we pick up
1246 : * possible this/callee correlations. This only comes into play for
1247 : * CALLPROP, for other calls we are past the type barrier and a
1248 : * TypeConstraintCall will also monitor the call.
1249 : */
1250 307 : cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
1251 307 : return;
1252 : }
1253 :
1254 : /* Ignore calls to natives, these will be handled by TypeConstraintCall. */
1255 53446 : JSFunction *callee = NULL;
1256 :
1257 53446 : if (type.isSingleObject()) {
1258 49963 : JSObject *object = type.singleObject();
1259 49963 : if (!object->isFunction() || !object->toFunction()->isInterpreted())
1260 30366 : return;
1261 19597 : callee = object->toFunction();
1262 3483 : } else if (type.isTypeObject()) {
1263 2454 : TypeObject *object = type.typeObject();
1264 2454 : if (!object->interpretedFunction)
1265 14 : return;
1266 2440 : callee = object->interpretedFunction;
1267 : } else {
1268 : /* Ignore calls to primitives, these will go through a stub. */
1269 1029 : return;
1270 : }
1271 :
1272 22037 : if (!callee->script()->ensureHasTypes(cx))
1273 0 : return;
1274 :
1275 22037 : TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
1276 22037 : if (this->types)
1277 146 : this->types->addSubset(cx, thisTypes);
1278 : else
1279 21891 : thisTypes->addType(cx, this->type);
1280 : }
1281 :
1282 : void
1283 52203 : TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
1284 : {
1285 : /*
1286 : * We only model a subset of the arithmetic behavior that is actually
1287 : * possible. The following need to be watched for at runtime:
1288 : *
1289 : * 1. Operations producing a double where no operand was a double.
1290 : * 2. Operations producing a string where no operand was a string (addition only).
1291 : * 3. Operations producing a value other than int/double/string.
1292 : */
1293 52203 : if (other) {
1294 : /*
1295 : * Addition operation, consider these cases:
1296 : * {int,bool} x {int,bool} -> int
1297 : * double x {int,bool,double} -> double
1298 : * string x any -> string
1299 : */
1300 28744 : if (type.isUnknown() || other->unknown()) {
1301 958 : target->addType(cx, Type::UnknownType());
1302 27786 : } else if (type.isPrimitive(JSVAL_TYPE_DOUBLE)) {
1303 1318 : if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
1304 : TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_BOOLEAN |
1305 1318 : TYPE_FLAG_ANYOBJECT)) {
1306 1080 : target->addType(cx, Type::DoubleType());
1307 238 : } else if (other->getObjectCount() != 0) {
1308 0 : TypeDynamicResult(cx, script, pc, Type::DoubleType());
1309 : }
1310 26468 : } else if (type.isPrimitive(JSVAL_TYPE_STRING)) {
1311 9145 : target->addType(cx, Type::StringType());
1312 17323 : } else if (other->hasAnyFlag(TYPE_FLAG_DOUBLE)) {
1313 501 : target->addType(cx, Type::DoubleType());
1314 16822 : } else if (other->hasAnyFlag(TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL |
1315 : TYPE_FLAG_INT32 | TYPE_FLAG_BOOLEAN |
1316 16822 : TYPE_FLAG_ANYOBJECT)) {
1317 10195 : target->addType(cx, Type::Int32Type());
1318 6627 : } else if (other->getObjectCount() != 0) {
1319 27 : TypeDynamicResult(cx, script, pc, Type::Int32Type());
1320 : }
1321 : } else {
1322 23459 : if (type.isUnknown())
1323 104 : target->addType(cx, Type::UnknownType());
1324 23355 : else if (type.isPrimitive(JSVAL_TYPE_DOUBLE))
1325 3511 : target->addType(cx, Type::DoubleType());
1326 19844 : else if (!type.isAnyObject() && type.isObject())
1327 221 : TypeDynamicResult(cx, script, pc, Type::Int32Type());
1328 : else
1329 19623 : target->addType(cx, Type::Int32Type());
1330 : }
1331 52203 : }
1332 :
1333 : void
1334 22688 : TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, Type type)
1335 : {
1336 22688 : if (type.isUnknown() || type.isAnyObject() || type.isObject() || script->strictModeCode) {
1337 22480 : target->addType(cx, type);
1338 22480 : return;
1339 : }
1340 :
1341 : /*
1342 : * Note: if |this| is null or undefined, the pushed value is the outer window. We
1343 : * can't use script->getGlobalType() here because it refers to the inner window.
1344 : */
1345 542 : if (!script->hasGlobal() ||
1346 168 : type.isPrimitive(JSVAL_TYPE_NULL) ||
1347 166 : type.isPrimitive(JSVAL_TYPE_UNDEFINED)) {
1348 180 : target->addType(cx, Type::UnknownType());
1349 180 : return;
1350 : }
1351 :
1352 28 : TypeObject *object = NULL;
1353 28 : switch (type.primitive()) {
1354 : case JSVAL_TYPE_INT32:
1355 : case JSVAL_TYPE_DOUBLE:
1356 4 : object = TypeScript::StandardType(cx, script, JSProto_Number);
1357 4 : break;
1358 : case JSVAL_TYPE_BOOLEAN:
1359 2 : object = TypeScript::StandardType(cx, script, JSProto_Boolean);
1360 2 : break;
1361 : case JSVAL_TYPE_STRING:
1362 22 : object = TypeScript::StandardType(cx, script, JSProto_String);
1363 22 : break;
1364 : default:
1365 0 : return;
1366 : }
1367 :
1368 28 : if (!object) {
1369 0 : cx->compartment->types.setPendingNukeTypes(cx);
1370 0 : return;
1371 : }
1372 :
1373 28 : target->addType(cx, Type::ObjectType(object));
1374 : }
1375 :
1376 : /////////////////////////////////////////////////////////////////////
1377 : // Freeze constraints
1378 : /////////////////////////////////////////////////////////////////////
1379 :
1380 : /* Constraint which triggers recompilation of a script if any type is added to a type set. */
1381 : class TypeConstraintFreeze : public TypeConstraint
1382 : {
1383 : public:
1384 : RecompileInfo info;
1385 :
1386 : /* Whether a new type has already been added, triggering recompilation. */
1387 : bool typeAdded;
1388 :
1389 425429 : TypeConstraintFreeze(RecompileInfo info)
1390 425429 : : TypeConstraint("freeze"), info(info), typeAdded(false)
1391 425429 : {}
1392 :
1393 227313 : void newType(JSContext *cx, TypeSet *source, Type type)
1394 : {
1395 227313 : if (typeAdded)
1396 112807 : return;
1397 :
1398 114506 : typeAdded = true;
1399 114506 : cx->compartment->types.addPendingRecompile(cx, info);
1400 : }
1401 : };
1402 :
1403 : void
1404 332657 : TypeSet::addFreeze(JSContext *cx)
1405 : {
1406 332657 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
1407 665314 : cx->compartment->types.compiledInfo), false);
1408 332657 : }
1409 :
1410 : /*
1411 : * Constraint which triggers recompilation of a script if a possible new JSValueType
1412 : * tag is realized for a type set.
1413 : */
1414 : class TypeConstraintFreezeTypeTag : public TypeConstraint
1415 : {
1416 : public:
1417 : RecompileInfo info;
1418 :
1419 : /*
1420 : * Whether the type tag has been marked unknown due to a type change which
1421 : * occurred after this constraint was generated (and which triggered recompilation).
1422 : */
1423 : bool typeUnknown;
1424 :
1425 1224525 : TypeConstraintFreezeTypeTag(RecompileInfo info)
1426 1224525 : : TypeConstraint("freezeTypeTag"), info(info), typeUnknown(false)
1427 1224525 : {}
1428 :
1429 762869 : void newType(JSContext *cx, TypeSet *source, Type type)
1430 : {
1431 762869 : if (typeUnknown)
1432 54155 : return;
1433 :
1434 708714 : if (!type.isUnknown() && !type.isAnyObject() && type.isObject()) {
1435 : /* Ignore new objects when the type set already has other objects. */
1436 514657 : if (source->getObjectCount() >= 2)
1437 324016 : return;
1438 : }
1439 :
1440 384698 : typeUnknown = true;
1441 384698 : cx->compartment->types.addPendingRecompile(cx, info);
1442 : }
1443 : };
1444 :
1445 : static inline JSValueType
1446 1056972 : GetValueTypeFromTypeFlags(TypeFlags flags)
1447 : {
1448 1056972 : switch (flags) {
1449 : case TYPE_FLAG_UNDEFINED:
1450 89916 : return JSVAL_TYPE_UNDEFINED;
1451 : case TYPE_FLAG_NULL:
1452 9363 : return JSVAL_TYPE_NULL;
1453 : case TYPE_FLAG_BOOLEAN:
1454 9455 : return JSVAL_TYPE_BOOLEAN;
1455 : case TYPE_FLAG_INT32:
1456 331868 : return JSVAL_TYPE_INT32;
1457 : case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
1458 34515 : return JSVAL_TYPE_DOUBLE;
1459 : case TYPE_FLAG_STRING:
1460 85520 : return JSVAL_TYPE_STRING;
1461 : case TYPE_FLAG_LAZYARGS:
1462 655 : return JSVAL_TYPE_MAGIC;
1463 : case TYPE_FLAG_ANYOBJECT:
1464 25406 : return JSVAL_TYPE_OBJECT;
1465 : default:
1466 470274 : return JSVAL_TYPE_UNKNOWN;
1467 : }
1468 : }
1469 :
1470 : JSValueType
1471 1353704 : TypeSet::getKnownTypeTag(JSContext *cx)
1472 : {
1473 1353704 : TypeFlags flags = baseFlags();
1474 : JSValueType type;
1475 :
1476 1353704 : if (baseObjectCount())
1477 296732 : type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
1478 : else
1479 1056972 : type = GetValueTypeFromTypeFlags(flags);
1480 :
1481 : /*
1482 : * If the type set is totally empty then it will be treated as unknown,
1483 : * but we still need to record the dependency as adding a new type can give
1484 : * it a definite type tag. This is not needed if there are enough types
1485 : * that the exact tag is unknown, as it will stay unknown as more types are
1486 : * added to the set.
1487 : */
1488 1353704 : bool empty = flags == 0 && baseObjectCount() == 0;
1489 1353704 : JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
1490 :
1491 1353704 : if (cx->compartment->types.compiledInfo.script && (empty || type != JSVAL_TYPE_UNKNOWN)) {
1492 1224525 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeTypeTag>(
1493 2449050 : cx->compartment->types.compiledInfo), false);
1494 : }
1495 :
1496 1353704 : return type;
1497 : }
1498 :
1499 : /* Constraint which triggers recompilation if an object acquires particular flags. */
1500 : class TypeConstraintFreezeObjectFlags : public TypeConstraint
1501 : {
1502 : public:
1503 : RecompileInfo info;
1504 :
1505 : /* Flags we are watching for on this object. */
1506 : TypeObjectFlags flags;
1507 :
1508 : /* Whether the object has already been marked as having one of the flags. */
1509 : bool *pmarked;
1510 : bool localMarked;
1511 :
1512 145204 : TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags, bool *pmarked)
1513 : : TypeConstraint("freezeObjectFlags"), info(info), flags(flags),
1514 145204 : pmarked(pmarked), localMarked(false)
1515 145204 : {}
1516 :
1517 79530 : TypeConstraintFreezeObjectFlags(RecompileInfo info, TypeObjectFlags flags)
1518 : : TypeConstraint("freezeObjectFlags"), info(info), flags(flags),
1519 79530 : pmarked(&localMarked), localMarked(false)
1520 79530 : {}
1521 :
1522 82 : void newType(JSContext *cx, TypeSet *source, Type type) {}
1523 :
1524 122088 : void newObjectState(JSContext *cx, TypeObject *object, bool force)
1525 : {
1526 122088 : if (object->hasAnyFlags(flags) && !*pmarked) {
1527 5906 : *pmarked = true;
1528 5906 : cx->compartment->types.addPendingRecompile(cx, info);
1529 116182 : } else if (force) {
1530 115426 : cx->compartment->types.addPendingRecompile(cx, info);
1531 : }
1532 122088 : }
1533 : };
1534 :
1535 : /*
1536 : * Constraint which triggers recompilation if any object in a type set acquire
1537 : * particular flags.
1538 : */
1539 : class TypeConstraintFreezeObjectFlagsSet : public TypeConstraint
1540 : {
1541 : public:
1542 : RecompileInfo info;
1543 :
1544 : TypeObjectFlags flags;
1545 : bool marked;
1546 :
1547 70138 : TypeConstraintFreezeObjectFlagsSet(RecompileInfo info, TypeObjectFlags flags)
1548 70138 : : TypeConstraint("freezeObjectKindSet"), info(info), flags(flags), marked(false)
1549 70138 : {}
1550 :
1551 158594 : void newType(JSContext *cx, TypeSet *source, Type type)
1552 : {
1553 158594 : if (marked) {
1554 : /* Despecialized the kind we were interested in due to recompilation. */
1555 630 : return;
1556 : }
1557 :
1558 157964 : if (type.isUnknown() || type.isAnyObject()) {
1559 : /* Fallthrough and recompile. */
1560 154838 : } else if (type.isObject()) {
1561 145548 : TypeObject *object = type.isSingleObject()
1562 14 : ? type.singleObject()->getType(cx)
1563 145562 : : type.typeObject();
1564 145548 : if (!object->hasAnyFlags(flags)) {
1565 : /*
1566 : * Add a constraint on the the object to pick up changes in the
1567 : * object's properties.
1568 : */
1569 145204 : TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
1570 145204 : if (!types)
1571 0 : return;
1572 145204 : types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
1573 290408 : info, flags, &marked), false);
1574 145204 : return;
1575 : }
1576 : } else {
1577 9290 : return;
1578 : }
1579 :
1580 3470 : marked = true;
1581 3470 : cx->compartment->types.addPendingRecompile(cx, info);
1582 : }
1583 : };
1584 :
1585 : bool
1586 101697 : TypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags)
1587 : {
1588 101697 : if (unknownObject())
1589 4528 : return true;
1590 :
1591 : /*
1592 : * Treat type sets containing no objects as having all object flags,
1593 : * to spare callers from having to check this.
1594 : */
1595 97169 : if (baseObjectCount() == 0)
1596 14704 : return true;
1597 :
1598 82465 : unsigned count = getObjectCount();
1599 213418 : for (unsigned i = 0; i < count; i++) {
1600 143280 : TypeObject *object = getTypeObject(i);
1601 143280 : if (!object) {
1602 30392 : JSObject *obj = getSingleObject(i);
1603 30392 : if (obj)
1604 379 : object = obj->getType(cx);
1605 : }
1606 143280 : if (object && object->hasAnyFlags(flags))
1607 12327 : return true;
1608 : }
1609 :
1610 : /*
1611 : * Watch for new objects of different kind, and re-traverse existing types
1612 : * in this set to add any needed FreezeArray constraints.
1613 : */
1614 70138 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlagsSet>(
1615 140276 : cx->compartment->types.compiledInfo, flags));
1616 :
1617 70138 : return false;
1618 : }
1619 :
1620 : bool
1621 65980 : TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags)
1622 : {
1623 65980 : if (object->hasAnyFlags(flags))
1624 9754 : return true;
1625 :
1626 56226 : TypeSet *types = object->getProperty(cx, JSID_EMPTY, false);
1627 56226 : if (!types)
1628 0 : return true;
1629 56226 : types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
1630 112452 : cx->compartment->types.compiledInfo, flags), false);
1631 56226 : return false;
1632 : }
1633 :
1634 : static inline void
1635 219717 : ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown, bool force)
1636 : {
1637 219717 : if (object->unknownProperties())
1638 2 : return;
1639 :
1640 : /* All constraints listening to state changes are on the empty id. */
1641 219715 : TypeSet *types = object->maybeGetProperty(cx, JSID_EMPTY);
1642 :
1643 : /* Mark as unknown after getting the types, to avoid assertion. */
1644 219715 : if (markingUnknown)
1645 26645 : object->flags |= OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES;
1646 :
1647 219715 : if (types) {
1648 17807 : TypeConstraint *constraint = types->constraintList;
1649 157604 : while (constraint) {
1650 121990 : constraint->newObjectState(cx, object, force);
1651 121990 : constraint = constraint->next;
1652 : }
1653 : }
1654 : }
1655 :
1656 : void
1657 23304 : TypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj)
1658 : {
1659 23304 : JS_ASSERT(!obj->unknownProperties());
1660 23304 : TypeSet *types = obj->getProperty(cx, JSID_EMPTY, false);
1661 23304 : if (!types)
1662 0 : return;
1663 :
1664 : /*
1665 : * Use a constraint which triggers recompilation when markStateChange is
1666 : * called, which will set 'force' to true.
1667 : */
1668 23304 : types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
1669 : cx->compartment->types.compiledInfo,
1670 46608 : 0));
1671 : }
1672 :
1673 : class TypeConstraintFreezeOwnProperty : public TypeConstraint
1674 : {
1675 : public:
1676 : RecompileInfo info;
1677 :
1678 : bool updated;
1679 : bool configurable;
1680 :
1681 223434 : TypeConstraintFreezeOwnProperty(RecompileInfo info, bool configurable)
1682 : : TypeConstraint("freezeOwnProperty"),
1683 223434 : info(info), updated(false), configurable(configurable)
1684 223434 : {}
1685 :
1686 18271 : void newType(JSContext *cx, TypeSet *source, Type type) {}
1687 :
1688 288 : void newPropertyState(JSContext *cx, TypeSet *source)
1689 : {
1690 288 : if (updated)
1691 0 : return;
1692 288 : if (source->isOwnProperty(configurable)) {
1693 274 : updated = true;
1694 274 : cx->compartment->types.addPendingRecompile(cx, info);
1695 : }
1696 : }
1697 : };
1698 :
1699 : static void
1700 : CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun);
1701 :
1702 : bool
1703 224104 : TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable)
1704 : {
1705 : /*
1706 : * Everywhere compiled code depends on definite properties associated with
1707 : * a type object's newScript, we need to make sure there are constraints
1708 : * in place which will mark those properties as configured should the
1709 : * definite properties be invalidated.
1710 : */
1711 224104 : if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) {
1712 2 : if (object->newScript) {
1713 2 : CheckNewScriptProperties(cx, object, object->newScript->fun);
1714 : } else {
1715 0 : JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED);
1716 0 : object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
1717 : }
1718 : }
1719 :
1720 224104 : if (isOwnProperty(configurable))
1721 670 : return true;
1722 :
1723 223434 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeOwnProperty>(
1724 : cx->compartment->types.compiledInfo,
1725 446868 : configurable), false);
1726 223434 : return false;
1727 : }
1728 :
1729 : bool
1730 70963 : TypeSet::knownNonEmpty(JSContext *cx)
1731 : {
1732 70963 : if (baseFlags() != 0 || baseObjectCount() != 0)
1733 36 : return true;
1734 :
1735 70927 : addFreeze(cx);
1736 :
1737 70927 : return false;
1738 : }
1739 :
1740 : bool
1741 24 : TypeSet::knownSubset(JSContext *cx, TypeSet *other)
1742 : {
1743 24 : if ((baseFlags() & other->baseFlags()) != baseFlags())
1744 2 : return false;
1745 :
1746 22 : if (unknownObject()) {
1747 0 : JS_ASSERT(other->unknownObject());
1748 : } else {
1749 22 : for (unsigned i = 0; i < getObjectCount(); i++) {
1750 0 : TypeObjectKey *obj = getObject(i);
1751 0 : if (!obj)
1752 0 : continue;
1753 0 : if (!other->hasType(Type::ObjectType(obj)))
1754 0 : return false;
1755 : }
1756 : }
1757 :
1758 22 : addFreeze(cx);
1759 :
1760 22 : return true;
1761 : }
1762 :
1763 : int
1764 2332 : TypeSet::getTypedArrayType(JSContext *cx)
1765 : {
1766 2332 : int arrayType = TypedArray::TYPE_MAX;
1767 2332 : unsigned count = getObjectCount();
1768 :
1769 4776 : for (unsigned i = 0; i < count; i++) {
1770 2738 : JSObject *proto = NULL;
1771 2738 : if (JSObject *object = getSingleObject(i)) {
1772 0 : proto = object->getProto();
1773 2738 : } else if (TypeObject *object = getTypeObject(i)) {
1774 2738 : JS_ASSERT(!object->hasAnyFlags(OBJECT_FLAG_NON_TYPED_ARRAY));
1775 2738 : proto = object->proto;
1776 : }
1777 2738 : if (!proto)
1778 0 : continue;
1779 :
1780 2738 : int objArrayType = proto->getClass() - TypedArray::slowClasses;
1781 2738 : JS_ASSERT(objArrayType >= 0 && objArrayType < TypedArray::TYPE_MAX);
1782 :
1783 : /*
1784 : * Set arrayType to the type of the first array. Return if there is an array
1785 : * of another type.
1786 : */
1787 2738 : if (arrayType == TypedArray::TYPE_MAX)
1788 2332 : arrayType = objArrayType;
1789 406 : else if (arrayType != objArrayType)
1790 294 : return TypedArray::TYPE_MAX;
1791 : }
1792 :
1793 : /*
1794 : * Assume the caller checked that OBJECT_FLAG_NON_TYPED_ARRAY is not set.
1795 : * This means the set contains at least one object because sets with no
1796 : * objects have all object flags.
1797 : */
1798 2038 : JS_ASSERT(arrayType != TypedArray::TYPE_MAX);
1799 :
1800 : /* Recompile when another typed array is added to this set. */
1801 2038 : addFreeze(cx);
1802 :
1803 2038 : return arrayType;
1804 : }
1805 :
1806 : JSObject *
1807 526811 : TypeSet::getSingleton(JSContext *cx, bool freeze)
1808 : {
1809 526811 : if (baseFlags() != 0 || baseObjectCount() != 1)
1810 348025 : return NULL;
1811 :
1812 178786 : JSObject *obj = getSingleObject(0);
1813 178786 : if (!obj)
1814 85928 : return NULL;
1815 :
1816 92858 : if (freeze) {
1817 92772 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
1818 185544 : cx->compartment->types.compiledInfo), false);
1819 : }
1820 :
1821 92858 : return obj;
1822 : }
1823 :
1824 : static inline bool
1825 0 : TypeHasGlobal(Type type, JSObject *global)
1826 : {
1827 0 : if (type.isUnknown() || type.isAnyObject())
1828 0 : return false;
1829 :
1830 0 : if (type.isSingleObject())
1831 0 : return &type.singleObject()->global() == global;
1832 :
1833 0 : if (type.isTypeObject())
1834 0 : return type.typeObject()->getGlobal() == global;
1835 :
1836 0 : JS_ASSERT(type.isPrimitive());
1837 0 : return true;
1838 : }
1839 :
1840 : class TypeConstraintFreezeGlobal : public TypeConstraint
1841 : {
1842 : public:
1843 : RecompileInfo info;
1844 : JSObject *global;
1845 :
1846 0 : TypeConstraintFreezeGlobal(RecompileInfo info, JSObject *global)
1847 0 : : TypeConstraint("freezeGlobal"), info(info), global(global)
1848 : {
1849 0 : JS_ASSERT(global);
1850 0 : }
1851 :
1852 0 : void newType(JSContext *cx, TypeSet *source, Type type)
1853 : {
1854 0 : if (!global || TypeHasGlobal(type, global))
1855 0 : return;
1856 :
1857 0 : global = NULL;
1858 0 : cx->compartment->types.addPendingRecompile(cx, info);
1859 : }
1860 : };
1861 :
1862 : bool
1863 0 : TypeSet::hasGlobalObject(JSContext *cx, JSObject *global)
1864 : {
1865 0 : if (unknownObject())
1866 0 : return false;
1867 :
1868 0 : unsigned count = getObjectCount();
1869 0 : for (unsigned i = 0; i < count; i++) {
1870 0 : TypeObjectKey *object = getObject(i);
1871 0 : if (object && !TypeHasGlobal(Type::ObjectType(object), global))
1872 0 : return false;
1873 : }
1874 :
1875 0 : add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeGlobal>(
1876 0 : cx->compartment->types.compiledInfo, global), false);
1877 :
1878 0 : return true;
1879 : }
1880 :
1881 : bool
1882 143 : TypeSet::needsBarrier(JSContext *cx)
1883 : {
1884 143 : bool result = unknownObject()
1885 118 : || getObjectCount() > 0
1886 261 : || hasAnyFlag(TYPE_FLAG_STRING);
1887 143 : if (!result)
1888 68 : addFreeze(cx);
1889 143 : return result;
1890 : }
1891 :
1892 : /////////////////////////////////////////////////////////////////////
1893 : // TypeCompartment
1894 : /////////////////////////////////////////////////////////////////////
1895 :
1896 : void
1897 41285 : TypeCompartment::init(JSContext *cx)
1898 : {
1899 41285 : PodZero(this);
1900 :
1901 41285 : if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE) {
1902 : #ifdef JS_METHODJIT
1903 24820 : JSC::MacroAssembler masm;
1904 12410 : if (masm.supportsFloatingPoint())
1905 : #endif
1906 12410 : inferenceEnabled = true;
1907 : }
1908 41285 : }
1909 :
1910 : TypeObject *
1911 346138 : TypeCompartment::newTypeObject(JSContext *cx, JSScript *script,
1912 : JSProtoKey key, JSObject *proto, bool unknown)
1913 : {
1914 692276 : RootObject root(cx, &proto);
1915 :
1916 346138 : TypeObject *object = gc::NewGCThing<TypeObject>(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject));
1917 346138 : if (!object)
1918 0 : return NULL;
1919 346138 : new(object) TypeObject(proto, key == JSProto_Function, unknown);
1920 :
1921 346138 : if (!cx->typeInferenceEnabled())
1922 99297 : object->flags |= OBJECT_FLAG_UNKNOWN_MASK;
1923 : else
1924 246841 : object->setFlagsFromKey(cx, key);
1925 :
1926 346138 : return object;
1927 : }
1928 :
1929 : TypeObject *
1930 12585 : TypeCompartment::newAllocationSiteTypeObject(JSContext *cx, const AllocationSiteKey &key)
1931 : {
1932 25170 : AutoEnterTypeInference enter(cx);
1933 :
1934 12585 : if (!allocationSiteTable) {
1935 4333 : allocationSiteTable = cx->new_<AllocationSiteTable>();
1936 4333 : if (!allocationSiteTable || !allocationSiteTable->init()) {
1937 0 : cx->compartment->types.setPendingNukeTypes(cx);
1938 0 : return NULL;
1939 : }
1940 : }
1941 :
1942 25170 : AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key);
1943 12585 : JS_ASSERT(!p);
1944 :
1945 : JSObject *proto;
1946 12585 : if (!js_GetClassPrototype(cx, key.script->global(), key.kind, &proto, NULL))
1947 0 : return NULL;
1948 :
1949 12585 : TypeObject *res = newTypeObject(cx, key.script, key.kind, proto);
1950 12585 : if (!res) {
1951 0 : cx->compartment->types.setPendingNukeTypes(cx);
1952 0 : return NULL;
1953 : }
1954 :
1955 12585 : jsbytecode *pc = key.script->code + key.offset;
1956 :
1957 12585 : if (JSOp(*pc) == JSOP_NEWOBJECT) {
1958 : /*
1959 : * This object is always constructed the same way and will not be
1960 : * observed by other code before all properties have been added. Mark
1961 : * all the properties as definite properties of the object.
1962 : */
1963 2749 : JSObject *baseobj = key.script->getObject(GET_UINT32_INDEX(pc));
1964 :
1965 2749 : if (!res->addDefiniteProperties(cx, baseobj))
1966 0 : return NULL;
1967 : }
1968 :
1969 12585 : if (!allocationSiteTable->add(p, key, res)) {
1970 0 : cx->compartment->types.setPendingNukeTypes(cx);
1971 0 : return NULL;
1972 : }
1973 :
1974 12585 : return res;
1975 : }
1976 :
1977 : static inline jsid
1978 520138 : GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
1979 : {
1980 520138 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc + offset));
1981 520138 : return MakeTypeId(cx, ATOM_TO_JSID(atom));
1982 : }
1983 :
1984 : bool
1985 5621523 : types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc)
1986 : {
1987 5621523 : JS_ASSERT(cx->typeInferenceEnabled());
1988 :
1989 : /*
1990 : * Make a heuristic guess at a use of JSOP_NEW that the constructed object
1991 : * should have a fresh type object. We do this when the NEW is immediately
1992 : * followed by a simple assignment to an object's .prototype field.
1993 : * This is designed to catch common patterns for subclassing in JS:
1994 : *
1995 : * function Super() { ... }
1996 : * function Sub1() { ... }
1997 : * function Sub2() { ... }
1998 : *
1999 : * Sub1.prototype = new Super();
2000 : * Sub2.prototype = new Super();
2001 : *
2002 : * Using distinct type objects for the particular prototypes of Sub1 and
2003 : * Sub2 lets us continue to distinguish the two subclasses and any extra
2004 : * properties added to those prototype objects.
2005 : */
2006 5621523 : if (JSOp(*pc) != JSOP_NEW)
2007 4749571 : return false;
2008 871952 : pc += JSOP_NEW_LENGTH;
2009 871952 : if (JSOp(*pc) == JSOP_SETPROP) {
2010 24505 : jsid id = GetAtomId(cx, script, pc, 0);
2011 24505 : if (id == id_prototype(cx))
2012 55 : return true;
2013 : }
2014 :
2015 871897 : return false;
2016 : }
2017 :
2018 : bool
2019 1992318 : types::UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc)
2020 : {
2021 : /*
2022 : * Objects created outside loops in global and eval scripts should have
2023 : * singleton types. For now this is only done for plain objects, not arrays.
2024 : */
2025 :
2026 1992318 : if (!cx->typeInferenceEnabled() || script->function())
2027 1940930 : return false;
2028 :
2029 51388 : JSOp op = JSOp(*pc);
2030 51388 : if (op == JSOP_NEWOBJECT || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Object)) {
2031 21524 : AutoEnterTypeInference enter(cx);
2032 :
2033 10762 : if (!script->ensureRanAnalysis(cx, NULL))
2034 0 : return false;
2035 :
2036 10762 : return !script->analysis()->getCode(pc).inLoop;
2037 : }
2038 :
2039 40626 : return false;
2040 : }
2041 :
2042 : bool
2043 35538 : types::ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script)
2044 : {
2045 35538 : if (!cx->typeInferenceEnabled() || !script->hasGlobal())
2046 32 : return true;
2047 :
2048 35506 : JSObject *proto = script->global()->getOrCreateArrayPrototype(cx);
2049 35506 : if (!proto)
2050 0 : return true;
2051 :
2052 70927 : do {
2053 70974 : TypeObject *type = proto->getType(cx);
2054 70974 : if (type->unknownProperties())
2055 6 : return true;
2056 70968 : TypeSet *indexTypes = type->getProperty(cx, JSID_VOID, false);
2057 70968 : if (!indexTypes || indexTypes->isOwnProperty(cx, type, true) || indexTypes->knownNonEmpty(cx))
2058 41 : return true;
2059 70927 : proto = proto->getProto();
2060 : } while (proto);
2061 :
2062 35459 : return false;
2063 : }
2064 :
2065 : bool
2066 7509 : TypeCompartment::growPendingArray(JSContext *cx)
2067 : {
2068 7509 : unsigned newCapacity = js::Max(unsigned(100), pendingCapacity * 2);
2069 7509 : PendingWork *newArray = (PendingWork *) OffTheBooks::calloc_(newCapacity * sizeof(PendingWork));
2070 7509 : if (!newArray) {
2071 0 : cx->compartment->types.setPendingNukeTypes(cx);
2072 0 : return false;
2073 : }
2074 :
2075 7509 : PodCopy(newArray, pendingArray, pendingCount);
2076 7509 : cx->free_(pendingArray);
2077 :
2078 7509 : pendingArray = newArray;
2079 7509 : pendingCapacity = newCapacity;
2080 :
2081 7509 : return true;
2082 : }
2083 :
2084 : void
2085 32935 : TypeCompartment::processPendingRecompiles(FreeOp *fop)
2086 : {
2087 : /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
2088 32935 : Vector<RecompileInfo> *pending = pendingRecompiles;
2089 32935 : pendingRecompiles = NULL;
2090 :
2091 32935 : JS_ASSERT(!pending->empty());
2092 :
2093 : #ifdef JS_METHODJIT
2094 :
2095 32935 : mjit::ExpandInlineFrames(compartment());
2096 :
2097 66685 : for (unsigned i = 0; i < pending->length(); i++) {
2098 33750 : const RecompileInfo &info = (*pending)[i];
2099 33750 : mjit::JITScript *jit = info.script->getJIT(info.constructing);
2100 33750 : if (jit && jit->chunkDescriptor(info.chunkIndex).chunk)
2101 33750 : mjit::Recompiler::clearStackReferencesAndChunk(fop, info.script, jit, info.chunkIndex);
2102 : }
2103 :
2104 : #endif /* JS_METHODJIT */
2105 :
2106 32935 : fop->delete_(pending);
2107 32935 : }
2108 :
2109 : void
2110 0 : TypeCompartment::setPendingNukeTypes(JSContext *cx)
2111 : {
2112 0 : JS_ASSERT(compartment()->activeInference);
2113 0 : if (!pendingNukeTypes) {
2114 0 : if (cx->compartment)
2115 0 : js_ReportOutOfMemory(cx);
2116 0 : pendingNukeTypes = true;
2117 : }
2118 0 : }
2119 :
2120 : void
2121 0 : TypeCompartment::setPendingNukeTypesNoReport()
2122 : {
2123 0 : JS_ASSERT(compartment()->activeInference);
2124 0 : if (!pendingNukeTypes)
2125 0 : pendingNukeTypes = true;
2126 0 : }
2127 :
2128 : void
2129 0 : TypeCompartment::nukeTypes(FreeOp *fop)
2130 : {
2131 : /*
2132 : * This is the usual response if we encounter an OOM while adding a type
2133 : * or resolving type constraints. Reset the compartment to not use type
2134 : * inference, and recompile all scripts.
2135 : *
2136 : * Because of the nature of constraint-based analysis (add constraints, and
2137 : * iterate them until reaching a fixpoint), we can't undo an add of a type set,
2138 : * and merely aborting the operation which triggered the add will not be
2139 : * sufficient for correct behavior as we will be leaving the types in an
2140 : * inconsistent state.
2141 : */
2142 0 : JS_ASSERT(pendingNukeTypes);
2143 0 : if (pendingRecompiles) {
2144 0 : fop->free_(pendingRecompiles);
2145 0 : pendingRecompiles = NULL;
2146 : }
2147 :
2148 0 : inferenceEnabled = false;
2149 :
2150 : /* Update the cached inferenceEnabled bit in all contexts. */
2151 0 : for (ContextIter acx(fop->runtime()); !acx.done(); acx.next())
2152 0 : acx->setCompartment(acx->compartment);
2153 :
2154 : #ifdef JS_METHODJIT
2155 :
2156 0 : JSCompartment *compartment = this->compartment();
2157 0 : mjit::ExpandInlineFrames(compartment);
2158 0 : mjit::ClearAllFrames(compartment);
2159 :
2160 : /* Throw away all JIT code in the compartment, but leave everything else alone. */
2161 :
2162 0 : for (gc::CellIter i(compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
2163 0 : JSScript *script = i.get<JSScript>();
2164 0 : if (script->hasJITCode())
2165 0 : mjit::ReleaseScriptCode(fop, script);
2166 : }
2167 : #endif /* JS_METHODJIT */
2168 :
2169 0 : }
2170 :
2171 : void
2172 693414 : TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
2173 : {
2174 : #ifdef JS_METHODJIT
2175 693414 : mjit::JITScript *jit = info.script->getJIT(info.constructing);
2176 693414 : if (!jit || !jit->chunkDescriptor(info.chunkIndex).chunk) {
2177 : /* Scripts which haven't been compiled yet don't need to be recompiled. */
2178 466207 : return;
2179 : }
2180 :
2181 227207 : if (!pendingRecompiles) {
2182 32935 : pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
2183 32935 : if (!pendingRecompiles) {
2184 0 : cx->compartment->types.setPendingNukeTypes(cx);
2185 0 : return;
2186 : }
2187 : }
2188 :
2189 296243 : for (unsigned i = 0; i < pendingRecompiles->length(); i++) {
2190 262493 : if (info == (*pendingRecompiles)[i])
2191 193457 : return;
2192 : }
2193 :
2194 33750 : if (!pendingRecompiles->append(info)) {
2195 0 : cx->compartment->types.setPendingNukeTypes(cx);
2196 0 : return;
2197 : }
2198 : #endif
2199 : }
2200 :
2201 : void
2202 492510 : TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc)
2203 : {
2204 : #ifdef JS_METHODJIT
2205 : RecompileInfo info;
2206 492510 : info.script = script;
2207 :
2208 492510 : if (script->jitHandleNormal.isValid()) {
2209 68493 : info.constructing = false;
2210 68493 : info.chunkIndex = script->jitHandleNormal.getValid()->chunkIndex(pc);
2211 68493 : addPendingRecompile(cx, info);
2212 : }
2213 :
2214 492510 : if (script->jitHandleCtor.isValid()) {
2215 641 : info.constructing = true;
2216 641 : info.chunkIndex = script->jitHandleCtor.getValid()->chunkIndex(pc);
2217 641 : addPendingRecompile(cx, info);
2218 : }
2219 : #endif
2220 492510 : }
2221 :
2222 : void
2223 52833 : TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
2224 : bool returnOnly)
2225 : {
2226 52833 : ScriptAnalysis *analysis = script->analysis();
2227 52833 : JS_ASSERT(analysis->ranInference());
2228 :
2229 52833 : jsbytecode *pc = script->code + offset;
2230 :
2231 52833 : JS_ASSERT_IF(returnOnly, js_CodeSpec[*pc].format & JOF_INVOKE);
2232 :
2233 52833 : Bytecode &code = analysis->getCode(pc);
2234 :
2235 52833 : if (returnOnly ? code.monitoredTypesReturn : code.monitoredTypes)
2236 3167 : return;
2237 :
2238 : InferSpew(ISpewOps, "addMonitorNeeded:%s #%u:%05u",
2239 49666 : returnOnly ? " returnOnly" : "", script->id(), offset);
2240 :
2241 : /* Dynamically monitor this call to keep track of its result types. */
2242 49666 : if (js_CodeSpec[*pc].format & JOF_INVOKE)
2243 34210 : code.monitoredTypesReturn = true;
2244 :
2245 49666 : if (!returnOnly)
2246 18903 : code.monitoredTypes = true;
2247 :
2248 49666 : cx->compartment->types.addPendingRecompile(cx, script, pc);
2249 :
2250 : /* Trigger recompilation of any inline callers. */
2251 49666 : if (script->function() && !script->function()->hasLazyType())
2252 10888 : ObjectStateChange(cx, script->function()->type(), false, true);
2253 : }
2254 :
2255 : void
2256 65 : TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
2257 : {
2258 65 : JS_ASSERT(this == &cx->compartment->types);
2259 65 : JS_ASSERT(!(target->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
2260 65 : JS_ASSERT(!target->singleton);
2261 65 : JS_ASSERT(target->unknownProperties());
2262 65 : target->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
2263 :
2264 130 : AutoEnterTypeInference enter(cx);
2265 :
2266 : /*
2267 : * Mark both persistent and transient type sets which contain obj as having
2268 : * a generic object type. It is not sufficient to mark just the persistent
2269 : * sets, as analysis of individual opcodes can pull type objects from
2270 : * static information (like initializer objects at various offsets).
2271 : *
2272 : * We make a list of properties to update and fix them afterwards, as adding
2273 : * types can't be done while iterating over cells as it can potentially make
2274 : * new type objects as well or trigger GC.
2275 : */
2276 130 : Vector<TypeSet *> pending(cx);
2277 1301 : for (gc::CellIter i(cx->compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
2278 1236 : TypeObject *object = i.get<TypeObject>();
2279 :
2280 1236 : unsigned count = object->getPropertyCount();
2281 1992 : for (unsigned i = 0; i < count; i++) {
2282 756 : Property *prop = object->getProperty(i);
2283 756 : if (prop && prop->types.hasType(Type::ObjectType(target))) {
2284 110 : if (!pending.append(&prop->types))
2285 0 : cx->compartment->types.setPendingNukeTypes(cx);
2286 : }
2287 : }
2288 : }
2289 :
2290 175 : for (unsigned i = 0; i < pending.length(); i++)
2291 110 : pending[i]->addType(cx, Type::AnyObjectType());
2292 :
2293 740 : for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
2294 675 : JSScript *script = i.get<JSScript>();
2295 675 : if (script->types) {
2296 264 : unsigned count = TypeScript::NumTypeSets(script);
2297 264 : TypeSet *typeArray = script->types->typeArray();
2298 1754 : for (unsigned i = 0; i < count; i++) {
2299 1490 : if (typeArray[i].hasType(Type::ObjectType(target)))
2300 119 : typeArray[i].addType(cx, Type::AnyObjectType());
2301 : }
2302 : }
2303 675 : if (script->hasAnalysis() && script->analysis()->ranInference()) {
2304 8854 : for (unsigned i = 0; i < script->length; i++) {
2305 8754 : if (!script->analysis()->maybeCode(i))
2306 5778 : continue;
2307 2976 : jsbytecode *pc = script->code + i;
2308 2976 : if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
2309 4 : continue;
2310 2972 : unsigned defCount = GetDefCount(script, i);
2311 2972 : if (ExtendedDef(pc))
2312 102 : defCount++;
2313 5116 : for (unsigned j = 0; j < defCount; j++) {
2314 2144 : TypeSet *types = script->analysis()->pushedTypes(pc, j);
2315 2144 : if (types->hasType(Type::ObjectType(target)))
2316 189 : types->addType(cx, Type::AnyObjectType());
2317 : }
2318 : }
2319 : }
2320 : }
2321 65 : }
2322 :
2323 : void
2324 534968 : ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, Type type)
2325 : {
2326 534968 : Bytecode &code = getCode(pc);
2327 :
2328 1174366 : if (!type.isUnknown() && !type.isAnyObject() &&
2329 639398 : type.isObject() && target->getObjectCount() >= BARRIER_OBJECT_LIMIT) {
2330 : /* Ignore this barrier, just add the type to the target. */
2331 3115 : target->addType(cx, type);
2332 3115 : return;
2333 : }
2334 :
2335 531853 : if (!code.typeBarriers) {
2336 : /*
2337 : * Adding type barriers at a bytecode which did not have them before
2338 : * will trigger recompilation. If there were already type barriers,
2339 : * however, do not trigger recompilation (the script will be recompiled
2340 : * if any of the barriers is ever violated).
2341 : */
2342 434220 : cx->compartment->types.addPendingRecompile(cx, script, const_cast<jsbytecode*>(pc));
2343 :
2344 : /* Trigger recompilation of any inline callers. */
2345 434220 : if (script->function() && !script->function()->hasLazyType())
2346 25425 : ObjectStateChange(cx, script->function()->type(), false, true);
2347 : }
2348 :
2349 : /* Ignore duplicate barriers. */
2350 531853 : TypeBarrier *barrier = code.typeBarriers;
2351 10080798 : while (barrier) {
2352 9019721 : if (barrier->target == target && barrier->type == type && !barrier->singleton)
2353 2629 : return;
2354 9017092 : barrier = barrier->next;
2355 : }
2356 :
2357 : InferSpew(ISpewOps, "typeBarrier: #%u:%05u: %sT%p%s %s",
2358 : script->id(), pc - script->code,
2359 : InferSpewColor(target), target, InferSpewColorReset(),
2360 529224 : TypeString(type));
2361 :
2362 529224 : barrier = cx->typeLifoAlloc().new_<TypeBarrier>(target, type, (JSObject *) NULL, JSID_VOID);
2363 :
2364 529224 : if (!barrier) {
2365 0 : cx->compartment->types.setPendingNukeTypes(cx);
2366 0 : return;
2367 : }
2368 :
2369 529224 : barrier->next = code.typeBarriers;
2370 529224 : code.typeBarriers = barrier;
2371 : }
2372 :
2373 : void
2374 14055 : ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *target, JSObject *singleton, jsid singletonId)
2375 : {
2376 14055 : JS_ASSERT(singletonId == MakeTypeId(cx, singletonId) && !JSID_IS_VOID(singletonId));
2377 :
2378 14055 : Bytecode &code = getCode(pc);
2379 :
2380 14055 : if (!code.typeBarriers) {
2381 : /* Trigger recompilation as for normal type barriers. */
2382 8624 : cx->compartment->types.addPendingRecompile(cx, script, const_cast<jsbytecode*>(pc));
2383 8624 : if (script->function() && !script->function()->hasLazyType())
2384 90 : ObjectStateChange(cx, script->function()->type(), false, true);
2385 : }
2386 :
2387 : InferSpew(ISpewOps, "singletonTypeBarrier: #%u:%05u: %sT%p%s %p %s",
2388 : script->id(), pc - script->code,
2389 : InferSpewColor(target), target, InferSpewColorReset(),
2390 14055 : (void *) singleton, TypeIdString(singletonId));
2391 :
2392 14055 : TypeBarrier *barrier = cx->typeLifoAlloc().new_<TypeBarrier>(target, Type::UndefinedType(),
2393 28110 : singleton, singletonId);
2394 :
2395 14055 : if (!barrier) {
2396 0 : cx->compartment->types.setPendingNukeTypes(cx);
2397 0 : return;
2398 : }
2399 :
2400 14055 : barrier->next = code.typeBarriers;
2401 14055 : code.typeBarriers = barrier;
2402 : }
2403 :
2404 : void
2405 41261 : TypeCompartment::print(JSContext *cx, bool force)
2406 : {
2407 41261 : JSCompartment *compartment = this->compartment();
2408 82522 : AutoEnterAnalysis enter(compartment);
2409 :
2410 41261 : if (!force && !InferSpewActive(ISpewResult))
2411 : return;
2412 :
2413 0 : for (gc::CellIter i(compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
2414 0 : JSScript *script = i.get<JSScript>();
2415 0 : if (script->hasAnalysis() && script->analysis()->ranInference())
2416 0 : script->analysis()->printTypes(cx);
2417 : }
2418 :
2419 : #ifdef DEBUG
2420 0 : for (gc::CellIter i(compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
2421 0 : TypeObject *object = i.get<TypeObject>();
2422 0 : object->print(cx);
2423 : }
2424 : #endif
2425 :
2426 0 : printf("Counts: ");
2427 0 : for (unsigned count = 0; count < TYPE_COUNT_LIMIT; count++) {
2428 0 : if (count)
2429 0 : printf("/");
2430 0 : printf("%u", typeCounts[count]);
2431 : }
2432 0 : printf(" (%u over)\n", typeCountOver);
2433 :
2434 0 : printf("Recompilations: %u\n", recompilations);
2435 : }
2436 :
2437 : /////////////////////////////////////////////////////////////////////
2438 : // TypeCompartment tables
2439 : /////////////////////////////////////////////////////////////////////
2440 :
2441 : /*
2442 : * The arrayTypeTable and objectTypeTable are per-compartment tables for making
2443 : * common type objects to model the contents of large script singletons and
2444 : * JSON objects. These are vanilla Arrays and native Objects, so we distinguish
2445 : * the types of different ones by looking at the types of their properties.
2446 : *
2447 : * All singleton/JSON arrays which have the same prototype, are homogenous and
2448 : * of the same element type will share a type object. All singleton/JSON
2449 : * objects which have the same shape and property types will also share a type
2450 : * object. We don't try to collate arrays or objects that have type mismatches.
2451 : */
2452 :
2453 : static inline bool
2454 320 : NumberTypes(Type a, Type b)
2455 : {
2456 580 : return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
2457 580 : && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
2458 : }
2459 :
2460 : /*
2461 : * As for GetValueType, but requires object types to be non-singletons with
2462 : * their default prototype. These are the only values that should appear in
2463 : * arrays and objects whose type can be fixed.
2464 : */
2465 : static inline Type
2466 10840 : GetValueTypeForTable(JSContext *cx, const Value &v)
2467 : {
2468 10840 : Type type = GetValueType(cx, v);
2469 10840 : JS_ASSERT(!type.isSingleObject());
2470 : return type;
2471 : }
2472 :
2473 : struct types::ArrayTableKey
2474 : {
2475 : Type type;
2476 : JSObject *proto;
2477 :
2478 38015 : ArrayTableKey()
2479 38015 : : type(Type::UndefinedType()), proto(NULL)
2480 38015 : {}
2481 :
2482 : typedef ArrayTableKey Lookup;
2483 :
2484 1585 : static inline uint32_t hash(const ArrayTableKey &v) {
2485 1585 : return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2));
2486 : }
2487 :
2488 755 : static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) {
2489 755 : return v1.type == v2.type && v1.proto == v2.proto;
2490 : }
2491 : };
2492 :
2493 : void
2494 1810 : TypeCompartment::fixArrayType(JSContext *cx, JSObject *obj)
2495 : {
2496 3620 : AutoEnterTypeInference enter(cx);
2497 :
2498 1810 : if (!arrayTypeTable) {
2499 755 : arrayTypeTable = cx->new_<ArrayTypeTable>();
2500 755 : if (!arrayTypeTable || !arrayTypeTable->init()) {
2501 0 : arrayTypeTable = NULL;
2502 0 : cx->compartment->types.setPendingNukeTypes(cx);
2503 : return;
2504 : }
2505 : }
2506 :
2507 : /*
2508 : * If the array is of homogenous type, pick a type object which will be
2509 : * shared with all other singleton/JSON arrays of the same type.
2510 : * If the array is heterogenous, keep the existing type object, which has
2511 : * unknown properties.
2512 : */
2513 1810 : JS_ASSERT(obj->isDenseArray());
2514 :
2515 1810 : unsigned len = obj->getDenseArrayInitializedLength();
2516 1810 : if (len == 0)
2517 : return;
2518 :
2519 1785 : Type type = GetValueTypeForTable(cx, obj->getDenseArrayElement(0));
2520 :
2521 7675 : for (unsigned i = 1; i < len; i++) {
2522 6090 : Type ntype = GetValueTypeForTable(cx, obj->getDenseArrayElement(i));
2523 6090 : if (ntype != type) {
2524 220 : if (NumberTypes(type, ntype))
2525 20 : type = Type::DoubleType();
2526 : else
2527 : return;
2528 : }
2529 : }
2530 :
2531 1585 : ArrayTableKey key;
2532 1585 : key.type = type;
2533 1585 : key.proto = obj->getProto();
2534 3170 : ArrayTypeTable::AddPtr p = arrayTypeTable->lookupForAdd(key);
2535 :
2536 1585 : if (p) {
2537 755 : obj->setType(p->value);
2538 : } else {
2539 : /* Make a new type to use for future arrays with the same elements. */
2540 830 : TypeObject *objType = newTypeObject(cx, NULL, JSProto_Array, obj->getProto());
2541 830 : if (!objType) {
2542 0 : cx->compartment->types.setPendingNukeTypes(cx);
2543 : return;
2544 : }
2545 830 : obj->setType(objType);
2546 :
2547 830 : if (!objType->unknownProperties())
2548 830 : objType->addPropertyType(cx, JSID_VOID, type);
2549 :
2550 830 : if (!arrayTypeTable->relookupOrAdd(p, key, objType)) {
2551 0 : cx->compartment->types.setPendingNukeTypes(cx);
2552 : return;
2553 : }
2554 : }
2555 : }
2556 :
2557 : /*
2558 : * N.B. We could also use the initial shape of the object (before its type is
2559 : * fixed) as the key in the object table, but since all references in the table
2560 : * are weak the hash entries would usually be collected on GC even if objects
2561 : * with the new type/shape are still live.
2562 : */
2563 : struct types::ObjectTableKey
2564 : {
2565 : jsid *ids;
2566 : uint32_t nslots;
2567 : uint32_t nfixed;
2568 : JSObject *proto;
2569 :
2570 : typedef JSObject * Lookup;
2571 :
2572 2185 : static inline uint32_t hash(JSObject *obj) {
2573 2185 : return (uint32_t) (JSID_BITS(obj->lastProperty()->propid().get()) ^
2574 4370 : obj->slotSpan() ^ obj->numFixedSlots() ^
2575 6555 : ((uint32_t)(size_t)obj->getProto() >> 2));
2576 : }
2577 :
2578 1360 : static inline bool match(const ObjectTableKey &v, JSObject *obj) {
2579 4080 : if (obj->slotSpan() != v.nslots ||
2580 1360 : obj->numFixedSlots() != v.nfixed ||
2581 1360 : obj->getProto() != v.proto) {
2582 0 : return false;
2583 : }
2584 1360 : const Shape *shape = obj->lastProperty();
2585 5745 : while (!shape->isEmptyShape()) {
2586 3025 : if (shape->propid() != v.ids[shape->slot()])
2587 0 : return false;
2588 3025 : shape = shape->previous();
2589 : }
2590 1360 : return true;
2591 : }
2592 : };
2593 :
2594 : struct types::ObjectTableEntry
2595 29170 : {
2596 : ReadBarriered<TypeObject> object;
2597 : Type *types;
2598 : };
2599 :
2600 : void
2601 1425 : TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
2602 : {
2603 2850 : AutoEnterTypeInference enter(cx);
2604 :
2605 1425 : if (!objectTypeTable) {
2606 575 : objectTypeTable = cx->new_<ObjectTypeTable>();
2607 575 : if (!objectTypeTable || !objectTypeTable->init()) {
2608 0 : objectTypeTable = NULL;
2609 0 : cx->compartment->types.setPendingNukeTypes(cx);
2610 : return;
2611 : }
2612 : }
2613 :
2614 : /*
2615 : * Use the same type object for all singleton/JSON arrays with the same
2616 : * base shape, i.e. the same fields written in the same order. If there
2617 : * is a type mismatch with previous objects of the same shape, use the
2618 : * generic unknown type.
2619 : */
2620 1425 : JS_ASSERT(obj->isObject());
2621 :
2622 1425 : if (obj->slotSpan() == 0 || obj->inDictionaryMode())
2623 : return;
2624 :
2625 2720 : ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(obj);
2626 1360 : const Shape *baseShape = obj->lastProperty();
2627 :
2628 1360 : if (p) {
2629 : /* The lookup ensures the shape matches, now check that the types match. */
2630 535 : Type *types = p->value.types;
2631 1340 : for (unsigned i = 0; i < obj->slotSpan(); i++) {
2632 890 : Type ntype = GetValueTypeForTable(cx, obj->getSlot(i));
2633 890 : if (ntype != types[i]) {
2634 100 : if (NumberTypes(ntype, types[i])) {
2635 15 : if (types[i].isPrimitive(JSVAL_TYPE_INT32)) {
2636 10 : types[i] = Type::DoubleType();
2637 10 : const Shape *shape = baseShape;
2638 20 : while (!shape->isEmptyShape()) {
2639 10 : if (shape->slot() == i) {
2640 10 : Type type = Type::DoubleType();
2641 10 : if (!p->value.object->unknownProperties()) {
2642 10 : jsid id = MakeTypeId(cx, shape->propid());
2643 10 : p->value.object->addPropertyType(cx, id, type);
2644 : }
2645 10 : break;
2646 : }
2647 0 : shape = shape->previous();
2648 : }
2649 : }
2650 : } else {
2651 : return;
2652 : }
2653 : }
2654 : }
2655 :
2656 450 : obj->setType(p->value.object);
2657 : } else {
2658 : /* Make a new type to use for the object and similar future ones. */
2659 825 : TypeObject *objType = newTypeObject(cx, NULL, JSProto_Object, obj->getProto());
2660 825 : if (!objType || !objType->addDefiniteProperties(cx, obj)) {
2661 0 : cx->compartment->types.setPendingNukeTypes(cx);
2662 : return;
2663 : }
2664 :
2665 825 : jsid *ids = (jsid *) cx->calloc_(obj->slotSpan() * sizeof(jsid));
2666 825 : if (!ids) {
2667 0 : cx->compartment->types.setPendingNukeTypes(cx);
2668 : return;
2669 : }
2670 :
2671 825 : Type *types = (Type *) cx->calloc_(obj->slotSpan() * sizeof(Type));
2672 825 : if (!types) {
2673 0 : cx->compartment->types.setPendingNukeTypes(cx);
2674 : return;
2675 : }
2676 :
2677 825 : const Shape *shape = baseShape;
2678 3725 : while (!shape->isEmptyShape()) {
2679 2075 : ids[shape->slot()] = shape->propid();
2680 2075 : types[shape->slot()] = GetValueTypeForTable(cx, obj->getSlot(shape->slot()));
2681 2075 : if (!objType->unknownProperties()) {
2682 2075 : jsid id = MakeTypeId(cx, shape->propid());
2683 2075 : objType->addPropertyType(cx, id, types[shape->slot()]);
2684 : }
2685 2075 : shape = shape->previous();
2686 : }
2687 :
2688 : ObjectTableKey key;
2689 825 : key.ids = ids;
2690 825 : key.nslots = obj->slotSpan();
2691 825 : key.nfixed = obj->numFixedSlots();
2692 825 : key.proto = obj->getProto();
2693 825 : JS_ASSERT(ObjectTableKey::match(key, obj));
2694 :
2695 825 : ObjectTableEntry entry;
2696 825 : entry.object = objType;
2697 825 : entry.types = types;
2698 :
2699 825 : p = objectTypeTable->lookupForAdd(obj);
2700 825 : if (!objectTypeTable->add(p, key, entry)) {
2701 0 : cx->compartment->types.setPendingNukeTypes(cx);
2702 : return;
2703 : }
2704 :
2705 825 : obj->setType(objType);
2706 : }
2707 : }
2708 :
2709 : /////////////////////////////////////////////////////////////////////
2710 : // TypeObject
2711 : /////////////////////////////////////////////////////////////////////
2712 :
2713 : void
2714 96439 : TypeObject::getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force)
2715 : {
2716 96439 : if (!force && types->hasPropagatedProperty())
2717 5363 : return;
2718 :
2719 91076 : types->setPropagatedProperty();
2720 :
2721 91076 : if (!proto)
2722 38103 : return;
2723 :
2724 52973 : if (proto->getType(cx)->unknownProperties()) {
2725 82 : types->addType(cx, Type::UnknownType());
2726 82 : return;
2727 : }
2728 :
2729 52891 : TypeSet *protoTypes = proto->type()->getProperty(cx, id, false);
2730 52891 : if (!protoTypes)
2731 0 : return;
2732 :
2733 52891 : protoTypes->addSubset(cx, types);
2734 :
2735 52891 : proto->type()->getFromPrototypes(cx, id, protoTypes);
2736 : }
2737 :
2738 : static inline void
2739 54055 : UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, const Shape *shape, bool force)
2740 : {
2741 54055 : types->setOwnProperty(cx, false);
2742 54055 : if (!shape->writable())
2743 1066 : types->setOwnProperty(cx, true);
2744 :
2745 54055 : if (shape->hasGetterValue() || shape->hasSetterValue()) {
2746 821 : types->setOwnProperty(cx, true);
2747 821 : types->addType(cx, Type::UnknownType());
2748 53234 : } else if (shape->hasDefaultGetter() && shape->hasSlot()) {
2749 52567 : const Value &value = obj->nativeGetSlot(shape->slot());
2750 :
2751 : /*
2752 : * Don't add initial undefined types for singleton properties that are
2753 : * not collated into the JSID_VOID property (see propertySet comment).
2754 : */
2755 52567 : if (force || !value.isUndefined()) {
2756 42077 : Type type = GetValueType(cx, value);
2757 42077 : types->addType(cx, type);
2758 : }
2759 : }
2760 54055 : }
2761 :
2762 : bool
2763 152184 : TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop)
2764 : {
2765 152184 : JS_ASSERT(!*pprop);
2766 152184 : Property *base = cx->typeLifoAlloc().new_<Property>(id);
2767 152184 : if (!base) {
2768 0 : cx->compartment->types.setPendingNukeTypes(cx);
2769 0 : return false;
2770 : }
2771 :
2772 152184 : if (singleton) {
2773 : /*
2774 : * Fill the property in with any type the object already has in an
2775 : * own property. We are only interested in plain native properties
2776 : * which don't go through a barrier when read by the VM or jitcode.
2777 : * We don't need to handle arrays or other JIT'ed non-natives as
2778 : * these are not (yet) singletons.
2779 : */
2780 :
2781 115224 : if (JSID_IS_VOID(id)) {
2782 : /* Go through all shapes on the object to get integer-valued properties. */
2783 3137 : const Shape *shape = singleton->lastProperty();
2784 63400 : while (!shape->isEmptyShape()) {
2785 57126 : if (JSID_IS_VOID(MakeTypeId(cx, shape->propid())))
2786 326 : UpdatePropertyType(cx, &base->types, singleton, shape, true);
2787 57126 : shape = shape->previous();
2788 : }
2789 112087 : } else if (!JSID_IS_EMPTY(id)) {
2790 100855 : const Shape *shape = singleton->nativeLookup(cx, id);
2791 100855 : if (shape)
2792 53729 : UpdatePropertyType(cx, &base->types, singleton, shape, false);
2793 : }
2794 :
2795 115224 : if (singleton->watched()) {
2796 : /*
2797 : * Mark the property as configured, to inhibit optimizations on it
2798 : * and avoid bypassing the watchpoint handler.
2799 : */
2800 12 : base->types.setOwnProperty(cx, true);
2801 : }
2802 : }
2803 :
2804 152184 : *pprop = base;
2805 :
2806 : InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
2807 : InferSpewColor(&base->types), &base->types, InferSpewColorReset(),
2808 152184 : TypeObjectString(this), TypeIdString(id));
2809 :
2810 152184 : return true;
2811 : }
2812 :
2813 : bool
2814 4153 : TypeObject::addDefiniteProperties(JSContext *cx, JSObject *obj)
2815 : {
2816 4153 : if (unknownProperties())
2817 0 : return true;
2818 :
2819 : /* Mark all properties of obj as definite properties of this type. */
2820 8306 : AutoEnterTypeInference enter(cx);
2821 :
2822 4153 : const Shape *shape = obj->lastProperty();
2823 14281 : while (!shape->isEmptyShape()) {
2824 5975 : jsid id = MakeTypeId(cx, shape->propid());
2825 11635 : if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot()) &&
2826 5660 : shape->slot() <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
2827 5660 : TypeSet *types = getProperty(cx, id, true);
2828 5660 : if (!types)
2829 0 : return false;
2830 5660 : types->setDefinite(shape->slot());
2831 : }
2832 5975 : shape = shape->previous();
2833 : }
2834 :
2835 4153 : return true;
2836 : }
2837 :
2838 : bool
2839 2 : TypeObject::matchDefiniteProperties(JSObject *obj)
2840 : {
2841 2 : unsigned count = getPropertyCount();
2842 8 : for (unsigned i = 0; i < count; i++) {
2843 6 : Property *prop = getProperty(i);
2844 6 : if (!prop)
2845 0 : continue;
2846 6 : if (prop->types.isDefiniteProperty()) {
2847 4 : unsigned slot = prop->types.definiteSlot();
2848 :
2849 4 : bool found = false;
2850 4 : const Shape *shape = obj->lastProperty();
2851 10 : while (!shape->isEmptyShape()) {
2852 6 : if (shape->slot() == slot && shape->propid() == prop->id) {
2853 4 : found = true;
2854 4 : break;
2855 : }
2856 2 : shape = shape->previous();
2857 : }
2858 4 : if (!found)
2859 0 : return false;
2860 : }
2861 : }
2862 :
2863 2 : return true;
2864 : }
2865 :
2866 : inline void
2867 13311186 : InlineAddTypeProperty(JSContext *cx, TypeObject *obj, jsid id, Type type)
2868 : {
2869 13311186 : JS_ASSERT(id == MakeTypeId(cx, id));
2870 :
2871 26622372 : AutoEnterTypeInference enter(cx);
2872 :
2873 13311186 : TypeSet *types = obj->getProperty(cx, id, true);
2874 13311186 : if (!types || types->hasType(type))
2875 : return;
2876 :
2877 : InferSpew(ISpewOps, "externalType: property %s %s: %s",
2878 24052 : TypeObjectString(obj), TypeIdString(id), TypeString(type));
2879 24052 : types->addType(cx, type);
2880 : }
2881 :
2882 : void
2883 275174 : TypeObject::addPropertyType(JSContext *cx, jsid id, Type type)
2884 : {
2885 275174 : InlineAddTypeProperty(cx, this, id, type);
2886 275174 : }
2887 :
2888 : void
2889 13018142 : TypeObject::addPropertyType(JSContext *cx, jsid id, const Value &value)
2890 : {
2891 13018142 : InlineAddTypeProperty(cx, this, id, GetValueType(cx, value));
2892 13018142 : }
2893 :
2894 : void
2895 17870 : TypeObject::addPropertyType(JSContext *cx, const char *name, Type type)
2896 : {
2897 17870 : jsid id = JSID_VOID;
2898 17870 : if (name) {
2899 6140 : JSAtom *atom = js_Atomize(cx, name, strlen(name));
2900 6140 : if (!atom) {
2901 0 : AutoEnterTypeInference enter(cx);
2902 0 : cx->compartment->types.setPendingNukeTypes(cx);
2903 : return;
2904 : }
2905 6140 : id = ATOM_TO_JSID(atom);
2906 : }
2907 17870 : InlineAddTypeProperty(cx, this, id, type);
2908 : }
2909 :
2910 : void
2911 0 : TypeObject::addPropertyType(JSContext *cx, const char *name, const Value &value)
2912 : {
2913 0 : addPropertyType(cx, name, GetValueType(cx, value));
2914 0 : }
2915 :
2916 : void
2917 274969 : TypeObject::markPropertyConfigured(JSContext *cx, jsid id)
2918 : {
2919 549938 : AutoEnterTypeInference enter(cx);
2920 :
2921 274969 : id = MakeTypeId(cx, id);
2922 :
2923 274969 : TypeSet *types = getProperty(cx, id, true);
2924 274969 : if (types)
2925 274969 : types->setOwnProperty(cx, true);
2926 274969 : }
2927 :
2928 : void
2929 13122 : TypeObject::markStateChange(JSContext *cx)
2930 : {
2931 13122 : if (unknownProperties())
2932 0 : return;
2933 :
2934 26244 : AutoEnterTypeInference enter(cx);
2935 13122 : TypeSet *types = maybeGetProperty(cx, JSID_EMPTY);
2936 13122 : if (types) {
2937 28 : TypeConstraint *constraint = types->constraintList;
2938 154 : while (constraint) {
2939 98 : constraint->newObjectState(cx, this, true);
2940 98 : constraint = constraint->next;
2941 : }
2942 : }
2943 : }
2944 :
2945 : void
2946 157083 : TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
2947 : {
2948 157083 : if ((this->flags & flags) == flags)
2949 934 : return;
2950 :
2951 312298 : AutoEnterTypeInference enter(cx);
2952 :
2953 156149 : if (singleton) {
2954 : /* Make sure flags are consistent with persistent object state. */
2955 1601 : JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE,
2956 3345 : interpretedFunction->script()->uninlineable);
2957 49 : JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION,
2958 1793 : interpretedFunction->script()->reentrantOuterFunction);
2959 89 : JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
2960 1833 : singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
2961 : }
2962 :
2963 156149 : this->flags |= flags;
2964 :
2965 156149 : InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
2966 :
2967 156149 : ObjectStateChange(cx, this, false, false);
2968 : }
2969 :
2970 : void
2971 26645 : TypeObject::markUnknown(JSContext *cx)
2972 : {
2973 53290 : AutoEnterTypeInference enter(cx);
2974 :
2975 26645 : JS_ASSERT(cx->compartment->activeInference);
2976 26645 : JS_ASSERT(!unknownProperties());
2977 :
2978 26645 : if (!(flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED))
2979 26640 : clearNewScript(cx);
2980 :
2981 26645 : InferSpew(ISpewOps, "UnknownProperties: %s", TypeObjectString(this));
2982 :
2983 26645 : ObjectStateChange(cx, this, true, true);
2984 :
2985 : /*
2986 : * Existing constraints may have already been added to this object, which we need
2987 : * to do the right thing for. We can't ensure that we will mark all unknown
2988 : * objects before they have been accessed, as the __proto__ of a known object
2989 : * could be dynamically set to an unknown object, and we can decide to ignore
2990 : * properties of an object during analysis (i.e. hashmaps). Adding unknown for
2991 : * any properties accessed already accounts for possible values read from them.
2992 : */
2993 :
2994 26645 : unsigned count = getPropertyCount();
2995 27749 : for (unsigned i = 0; i < count; i++) {
2996 1104 : Property *prop = getProperty(i);
2997 1104 : if (prop) {
2998 664 : prop->types.addType(cx, Type::UnknownType());
2999 664 : prop->types.setOwnProperty(cx, true);
3000 : }
3001 : }
3002 26645 : }
3003 :
3004 : void
3005 26680 : TypeObject::clearNewScript(JSContext *cx)
3006 : {
3007 26680 : JS_ASSERT(!(flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED));
3008 26680 : flags |= OBJECT_FLAG_NEW_SCRIPT_CLEARED;
3009 :
3010 : /*
3011 : * It is possible for the object to not have a new script yet but to have
3012 : * one added in the future. When analyzing properties of new scripts we mix
3013 : * in adding constraints to trigger clearNewScript with changes to the
3014 : * type sets themselves (from breakTypeBarriers). It is possible that we
3015 : * could trigger one of these constraints before AnalyzeNewScriptProperties
3016 : * has finished, in which case we want to make sure that call fails.
3017 : */
3018 26680 : if (!newScript)
3019 26640 : return;
3020 :
3021 80 : AutoEnterTypeInference enter(cx);
3022 :
3023 : /*
3024 : * Any definite properties we added due to analysis of the new script when
3025 : * the type object was created are now invalid: objects with the same type
3026 : * can be created by using 'new' on a different script or through some
3027 : * other mechanism (e.g. Object.create). Rather than clear out the definite
3028 : * bits on the object's properties, just mark such properties as having
3029 : * been deleted/reconfigured, which will have the same effect on JITs
3030 : * wanting to use the definite bits to optimize property accesses.
3031 : */
3032 140 : for (unsigned i = 0; i < getPropertyCount(); i++) {
3033 100 : Property *prop = getProperty(i);
3034 100 : if (!prop)
3035 0 : continue;
3036 100 : if (prop->types.isDefiniteProperty())
3037 100 : prop->types.setOwnProperty(cx, true);
3038 : }
3039 :
3040 : /*
3041 : * If we cleared the new script while in the middle of initializing an
3042 : * object, it will still have the new script's shape and reflect the no
3043 : * longer correct state of the object once its initialization is completed.
3044 : * We can't really detect the possibility of this statically, but the new
3045 : * script keeps track of where each property is initialized so we can walk
3046 : * the stack and fix up any such objects.
3047 : */
3048 130 : for (FrameRegsIter iter(cx); !iter.done(); ++iter) {
3049 90 : StackFrame *fp = iter.fp();
3050 175 : if (fp->isScriptFrame() && fp->isConstructing() &&
3051 45 : fp->fun() == newScript->fun && fp->thisValue().isObject() &&
3052 20 : !fp->thisValue().toObject().hasLazyType() &&
3053 20 : fp->thisValue().toObject().type() == this) {
3054 20 : JSObject *obj = &fp->thisValue().toObject();
3055 20 : jsbytecode *pc = iter.pc();
3056 :
3057 : /* Whether all identified 'new' properties have been initialized. */
3058 20 : bool finished = false;
3059 :
3060 : /* If not finished, number of properties that have been added. */
3061 20 : uint32_t numProperties = 0;
3062 :
3063 : /*
3064 : * If non-zero, we are scanning initializers in a call which has
3065 : * already finished.
3066 : */
3067 20 : size_t depth = 0;
3068 :
3069 70 : for (TypeNewScript::Initializer *init = newScript->initializerList;; init++) {
3070 70 : uint32_t offset = uint32_t(pc - fp->script()->code);
3071 70 : if (init->kind == TypeNewScript::Initializer::SETPROP) {
3072 50 : if (!depth && init->offset > offset) {
3073 : /* Advanced past all properties which have been initialized. */
3074 15 : break;
3075 : }
3076 35 : numProperties++;
3077 20 : } else if (init->kind == TypeNewScript::Initializer::FRAME_PUSH) {
3078 15 : if (depth) {
3079 0 : depth++;
3080 15 : } else if (init->offset > offset) {
3081 : /* Advanced past all properties which have been initialized. */
3082 5 : break;
3083 10 : } else if (init->offset == offset) {
3084 5 : StackSegment &seg = cx->stack.space().containingSegment(fp);
3085 5 : if (seg.maybefp() == fp)
3086 0 : break;
3087 5 : fp = seg.computeNextFrame(fp);
3088 5 : pc = fp->pcQuadratic(cx->stack);
3089 : } else {
3090 : /* This call has already finished. */
3091 5 : depth = 1;
3092 : }
3093 5 : } else if (init->kind == TypeNewScript::Initializer::FRAME_POP) {
3094 5 : if (depth) {
3095 5 : depth--;
3096 : } else {
3097 : /* This call has not finished yet. */
3098 0 : break;
3099 : }
3100 : } else {
3101 0 : JS_ASSERT(init->kind == TypeNewScript::Initializer::DONE);
3102 0 : finished = true;
3103 0 : break;
3104 : }
3105 : }
3106 :
3107 20 : if (!finished)
3108 20 : obj->rollbackProperties(cx, numProperties);
3109 : }
3110 : }
3111 :
3112 : /* We NULL out newScript *before* freeing it so the write barrier works. */
3113 40 : TypeNewScript *savedNewScript = newScript;
3114 40 : newScript = NULL;
3115 40 : cx->free_(savedNewScript);
3116 :
3117 40 : markStateChange(cx);
3118 : }
3119 :
3120 : void
3121 0 : TypeObject::print(JSContext *cx)
3122 : {
3123 : printf("%s : %s",
3124 : TypeObjectString(this),
3125 0 : proto ? TypeString(Type::ObjectType(proto)) : "(null)");
3126 :
3127 0 : if (unknownProperties()) {
3128 0 : printf(" unknown");
3129 : } else {
3130 0 : if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED_ARRAY))
3131 0 : printf(" packed");
3132 0 : if (!hasAnyFlags(OBJECT_FLAG_NON_DENSE_ARRAY))
3133 0 : printf(" dense");
3134 0 : if (!hasAnyFlags(OBJECT_FLAG_NON_TYPED_ARRAY))
3135 0 : printf(" typed");
3136 0 : if (hasAnyFlags(OBJECT_FLAG_UNINLINEABLE))
3137 0 : printf(" uninlineable");
3138 0 : if (hasAnyFlags(OBJECT_FLAG_SPECIAL_EQUALITY))
3139 0 : printf(" specialEquality");
3140 0 : if (hasAnyFlags(OBJECT_FLAG_ITERATED))
3141 0 : printf(" iterated");
3142 : }
3143 :
3144 0 : unsigned count = getPropertyCount();
3145 :
3146 0 : if (count == 0) {
3147 0 : printf(" {}\n");
3148 0 : return;
3149 : }
3150 :
3151 0 : printf(" {");
3152 :
3153 0 : for (unsigned i = 0; i < count; i++) {
3154 0 : Property *prop = getProperty(i);
3155 0 : if (prop) {
3156 0 : printf("\n %s:", TypeIdString(prop->id));
3157 0 : prop->types.print(cx);
3158 : }
3159 : }
3160 :
3161 0 : printf("\n}\n");
3162 : }
3163 :
3164 : /////////////////////////////////////////////////////////////////////
3165 : // Type Analysis
3166 : /////////////////////////////////////////////////////////////////////
3167 :
3168 : /*
3169 : * If the bytecode immediately following code/pc is a test of the value
3170 : * pushed by code, that value should be marked as possibly void.
3171 : */
3172 : static inline bool
3173 442871 : CheckNextTest(jsbytecode *pc)
3174 : {
3175 442871 : jsbytecode *next = pc + GetBytecodeLength(pc);
3176 442871 : switch ((JSOp)*next) {
3177 : case JSOP_IFEQ:
3178 : case JSOP_IFNE:
3179 : case JSOP_NOT:
3180 : case JSOP_OR:
3181 : case JSOP_AND:
3182 : case JSOP_TYPEOF:
3183 : case JSOP_TYPEOFEXPR:
3184 820 : return true;
3185 : default:
3186 : /* TRAP ok here */
3187 442051 : return false;
3188 : }
3189 : }
3190 :
3191 : static inline TypeObject *
3192 22374 : GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc)
3193 : {
3194 22374 : if (!script->hasGlobal())
3195 726 : return NULL;
3196 :
3197 21648 : if (UseNewTypeForInitializer(cx, script, pc))
3198 1168 : return NULL;
3199 :
3200 20480 : JSOp op = JSOp(*pc);
3201 20480 : JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT);
3202 :
3203 20480 : bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
3204 20480 : return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object);
3205 : }
3206 :
3207 : /*
3208 : * Detach nesting state for script from its parent, removing it entirely if it
3209 : * has no children of its own. This happens when walking type information while
3210 : * initially resolving NAME accesses, thus will not invalidate any compiler
3211 : * dependencies.
3212 : */
3213 : static void
3214 168 : DetachNestingParent(JSScript *script)
3215 : {
3216 168 : TypeScriptNesting *nesting = script->nesting();
3217 :
3218 168 : if (!nesting || !nesting->parent)
3219 0 : return;
3220 :
3221 : /* Remove from parent's list of children. */
3222 168 : JSScript **pscript = &nesting->parent->nesting()->children;
3223 340 : while ((*pscript)->nesting() != nesting)
3224 4 : pscript = &(*pscript)->nesting()->next;
3225 168 : *pscript = nesting->next;
3226 :
3227 168 : nesting->parent = NULL;
3228 :
3229 : /* If this nesting can have no children of its own, destroy it. */
3230 168 : if (!script->isOuterFunction)
3231 160 : script->clearNesting();
3232 : }
3233 :
3234 : ScriptAnalysis::NameAccess
3235 68681 : ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency)
3236 : {
3237 68681 : JS_ASSERT(cx->typeInferenceEnabled());
3238 :
3239 : NameAccess access;
3240 68681 : PodZero(&access);
3241 :
3242 68681 : if (!JSID_IS_ATOM(id))
3243 0 : return access;
3244 68681 : JSAtom *atom = JSID_TO_ATOM(id);
3245 :
3246 68681 : JSScript *script = this->script;
3247 175821 : while (script->function() && script->nesting()) {
3248 76484 : if (!script->ensureRanInference(cx))
3249 0 : return access;
3250 :
3251 : /*
3252 : * Don't resolve names in scripts which use 'let' or 'with'. New names
3253 : * bound here can mask variables of the script itself.
3254 : *
3255 : * Also, don't resolve names in scripts which are generators. Frame
3256 : * balancing works differently for generators and we do not maintain
3257 : * active frame counts for such scripts.
3258 : */
3259 76484 : if (script->analysis()->addsScopeObjects() ||
3260 : JSOp(*script->code) == JSOP_GENERATOR) {
3261 119 : return access;
3262 : }
3263 :
3264 : /* Check if the script definitely binds the identifier. */
3265 : unsigned index;
3266 76365 : BindingKind kind = script->bindings.lookup(cx, atom, &index);
3267 76365 : if (kind == ARGUMENT || kind == VARIABLE) {
3268 37050 : TypeObject *obj = script->function()->getType(cx);
3269 :
3270 37050 : if (addDependency) {
3271 : /*
3272 : * Record the dependency which compiled code has on the outer
3273 : * function being non-reentrant.
3274 : */
3275 30865 : if (TypeSet::HasObjectFlags(cx, obj, OBJECT_FLAG_REENTRANT_FUNCTION))
3276 1010 : return access;
3277 : }
3278 :
3279 36040 : if (!script->isOuterFunction)
3280 0 : return access;
3281 :
3282 36040 : access.script = script;
3283 36040 : access.nesting = script->nesting();
3284 36040 : access.slot = (kind == ARGUMENT) ? ArgSlot(index) : LocalSlot(script, index);
3285 36040 : access.arg = (kind == ARGUMENT);
3286 36040 : access.index = index;
3287 36040 : return access;
3288 39315 : } else if (kind != NONE) {
3289 0 : return access;
3290 : }
3291 :
3292 : /*
3293 : * The script's bindings do not contain a name for the function itself,
3294 : * don't resolve name accesses on lambdas in DeclEnv objects on the
3295 : * scope chain.
3296 : */
3297 39315 : if (atom == CallObjectLambdaName(script->function()))
3298 16 : return access;
3299 :
3300 39299 : if (!script->nesting()->parent)
3301 840 : return access;
3302 38459 : script = script->nesting()->parent;
3303 : }
3304 :
3305 30656 : return access;
3306 : }
3307 :
3308 : /* Analyze type information for a single bytecode. */
3309 : bool
3310 2120160 : ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
3311 : TypeInferenceState &state)
3312 : {
3313 2120160 : jsbytecode *pc = script->code + offset;
3314 2120160 : JSOp op = (JSOp)*pc;
3315 :
3316 2120160 : Bytecode &code = getCode(offset);
3317 2120160 : JS_ASSERT(!code.pushedTypes);
3318 :
3319 2120160 : InferSpew(ISpewOps, "analyze: #%u:%05u", script->id(), offset);
3320 :
3321 2120160 : unsigned defCount = GetDefCount(script, offset);
3322 2120160 : if (ExtendedDef(pc))
3323 103872 : defCount++;
3324 :
3325 2120160 : TypeSet *pushed = cx->typeLifoAlloc().newArrayUninitialized<TypeSet>(defCount);
3326 2120160 : if (!pushed)
3327 0 : return false;
3328 2120160 : PodZero(pushed, defCount);
3329 2120160 : code.pushedTypes = pushed;
3330 :
3331 : /*
3332 : * Add phi nodes introduced at this point to the list of all phi nodes in
3333 : * the script. Types for these are not generated until after the script has
3334 : * been processed, as types can flow backwards into phi nodes and the
3335 : * source sets may not exist if we try to process these eagerly.
3336 : */
3337 2120160 : if (code.newValues) {
3338 15688 : SlotValue *newv = code.newValues;
3339 94097 : while (newv->slot) {
3340 62721 : if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) {
3341 48450 : newv++;
3342 48450 : continue;
3343 : }
3344 :
3345 : /*
3346 : * The phi nodes at join points should all be unique, and every phi
3347 : * node created should be in the phiValues list on some bytecode.
3348 : */
3349 14271 : if (!state.phiNodes.append(newv->value.phiNode()))
3350 0 : return false;
3351 14271 : TypeSet &types = newv->value.phiNode()->types;
3352 : InferSpew(ISpewOps, "typeSet: %sT%p%s phi #%u:%05u:%u",
3353 : InferSpewColor(&types), &types, InferSpewColorReset(),
3354 14271 : script->id(), offset, newv->slot);
3355 14271 : newv++;
3356 : }
3357 : }
3358 :
3359 : /*
3360 : * Treat decomposed ops as no-ops, we will analyze the decomposed version
3361 : * instead. (We do, however, need to look at introduced phi nodes).
3362 : */
3363 2120160 : if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
3364 3656 : return true;
3365 :
3366 3983098 : for (unsigned i = 0; i < defCount; i++) {
3367 : InferSpew(ISpewOps, "typeSet: %sT%p%s pushed%u #%u:%05u",
3368 : InferSpewColor(&pushed[i]), &pushed[i], InferSpewColorReset(),
3369 1866594 : i, script->id(), offset);
3370 : }
3371 :
3372 : /* Add type constraints for the various opcodes. */
3373 2116504 : switch (op) {
3374 :
3375 : /* Nop bytecodes. */
3376 : case JSOP_POP:
3377 : case JSOP_NOP:
3378 : case JSOP_LOOPHEAD:
3379 : case JSOP_LOOPENTRY:
3380 : case JSOP_GOTO:
3381 : case JSOP_IFEQ:
3382 : case JSOP_IFNE:
3383 : case JSOP_LINENO:
3384 : case JSOP_DEFCONST:
3385 : case JSOP_LEAVEWITH:
3386 : case JSOP_LEAVEBLOCK:
3387 : case JSOP_RETRVAL:
3388 : case JSOP_ENDITER:
3389 : case JSOP_THROWING:
3390 : case JSOP_GOSUB:
3391 : case JSOP_RETSUB:
3392 : case JSOP_CONDSWITCH:
3393 : case JSOP_DEFAULT:
3394 : case JSOP_POPN:
3395 : case JSOP_STARTXML:
3396 : case JSOP_STARTXMLEXPR:
3397 : case JSOP_DEFXMLNS:
3398 : case JSOP_POPV:
3399 : case JSOP_DEBUGGER:
3400 : case JSOP_SETCALL:
3401 : case JSOP_TABLESWITCH:
3402 : case JSOP_LOOKUPSWITCH:
3403 : case JSOP_TRY:
3404 : case JSOP_LABEL:
3405 403426 : break;
3406 :
3407 : /* Bytecodes pushing values of known type. */
3408 : case JSOP_VOID:
3409 : case JSOP_UNDEFINED:
3410 74228 : pushed[0].addType(cx, Type::UndefinedType());
3411 74228 : break;
3412 : case JSOP_ZERO:
3413 : case JSOP_ONE:
3414 : case JSOP_INT8:
3415 : case JSOP_INT32:
3416 : case JSOP_UINT16:
3417 : case JSOP_UINT24:
3418 : case JSOP_BITAND:
3419 : case JSOP_BITOR:
3420 : case JSOP_BITXOR:
3421 : case JSOP_BITNOT:
3422 : case JSOP_RSH:
3423 : case JSOP_LSH:
3424 : case JSOP_URSH:
3425 112261 : pushed[0].addType(cx, Type::Int32Type());
3426 112261 : break;
3427 : case JSOP_FALSE:
3428 : case JSOP_TRUE:
3429 : case JSOP_EQ:
3430 : case JSOP_NE:
3431 : case JSOP_LT:
3432 : case JSOP_LE:
3433 : case JSOP_GT:
3434 : case JSOP_GE:
3435 : case JSOP_NOT:
3436 : case JSOP_STRICTEQ:
3437 : case JSOP_STRICTNE:
3438 : case JSOP_IN:
3439 : case JSOP_INSTANCEOF:
3440 : case JSOP_DELDESC:
3441 55202 : pushed[0].addType(cx, Type::BooleanType());
3442 55202 : break;
3443 : case JSOP_DOUBLE:
3444 2606 : pushed[0].addType(cx, Type::DoubleType());
3445 2606 : break;
3446 : case JSOP_STRING:
3447 : case JSOP_TYPEOF:
3448 : case JSOP_TYPEOFEXPR:
3449 : case JSOP_QNAMEPART:
3450 : case JSOP_XMLTAGEXPR:
3451 : case JSOP_TOATTRVAL:
3452 : case JSOP_ADDATTRNAME:
3453 : case JSOP_ADDATTRVAL:
3454 : case JSOP_XMLELTEXPR:
3455 63281 : pushed[0].addType(cx, Type::StringType());
3456 63281 : break;
3457 : case JSOP_NULL:
3458 3248 : pushed[0].addType(cx, Type::NullType());
3459 3248 : break;
3460 :
3461 : case JSOP_REGEXP:
3462 1385 : if (script->hasGlobal()) {
3463 1381 : TypeObject *object = TypeScript::StandardType(cx, script, JSProto_RegExp);
3464 1381 : if (!object)
3465 0 : return false;
3466 1381 : pushed[0].addType(cx, Type::ObjectType(object));
3467 : } else {
3468 4 : pushed[0].addType(cx, Type::UnknownType());
3469 : }
3470 1385 : break;
3471 :
3472 : case JSOP_OBJECT: {
3473 1132 : JSObject *obj = script->getObject(GET_UINT32_INDEX(pc));
3474 1132 : pushed[0].addType(cx, Type::ObjectType(obj));
3475 1132 : break;
3476 : }
3477 :
3478 : case JSOP_STOP:
3479 : /* If a stop is reachable then the return type may be void. */
3480 32259 : if (script->function())
3481 10055 : TypeScript::ReturnTypes(script)->addType(cx, Type::UndefinedType());
3482 32259 : break;
3483 :
3484 : case JSOP_OR:
3485 : case JSOP_AND:
3486 : /* OR/AND push whichever operand determined the result. */
3487 1339 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3488 1339 : break;
3489 :
3490 : case JSOP_DUP:
3491 21126 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3492 21126 : poppedTypes(pc, 0)->addSubset(cx, &pushed[1]);
3493 21126 : break;
3494 :
3495 : case JSOP_DUP2:
3496 286 : poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
3497 286 : poppedTypes(pc, 0)->addSubset(cx, &pushed[1]);
3498 286 : poppedTypes(pc, 1)->addSubset(cx, &pushed[2]);
3499 286 : poppedTypes(pc, 0)->addSubset(cx, &pushed[3]);
3500 286 : break;
3501 :
3502 : case JSOP_SWAP:
3503 : case JSOP_PICK: {
3504 22066 : unsigned pickedDepth = (op == JSOP_SWAP ? 1 : GET_UINT8(pc));
3505 : /* The last popped value is the last pushed. */
3506 22066 : poppedTypes(pc, pickedDepth)->addSubset(cx, &pushed[pickedDepth]);
3507 47229 : for (unsigned i = 0; i < pickedDepth; i++)
3508 25163 : poppedTypes(pc, i)->addSubset(cx, &pushed[pickedDepth - 1 - i]);
3509 22066 : break;
3510 : }
3511 :
3512 : case JSOP_GETGNAME:
3513 : case JSOP_CALLGNAME: {
3514 399268 : jsid id = GetAtomId(cx, script, pc, 0);
3515 :
3516 399268 : TypeSet *seen = bytecodeTypes(pc);
3517 399268 : seen->addSubset(cx, &pushed[0]);
3518 :
3519 : /*
3520 : * Normally we rely on lazy standard class initialization to fill in
3521 : * the types of global properties the script can access. In a few cases
3522 : * the method JIT will bypass this, and we need to add the types direclty.
3523 : */
3524 399268 : if (id == ATOM_TO_JSID(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]))
3525 1286 : seen->addType(cx, Type::UndefinedType());
3526 399268 : if (id == ATOM_TO_JSID(cx->runtime->atomState.NaNAtom))
3527 346 : seen->addType(cx, Type::DoubleType());
3528 399268 : if (id == ATOM_TO_JSID(cx->runtime->atomState.InfinityAtom))
3529 216 : seen->addType(cx, Type::DoubleType());
3530 :
3531 : /* Handle as a property access. */
3532 399268 : PropertyAccess(cx, script, pc, script->global()->getType(cx), false, seen, id);
3533 :
3534 399268 : if (op == JSOP_CALLGNAME)
3535 26760 : pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
3536 :
3537 399268 : if (CheckNextTest(pc))
3538 413 : pushed[0].addType(cx, Type::UndefinedType());
3539 399268 : break;
3540 : }
3541 :
3542 : case JSOP_NAME:
3543 : case JSOP_CALLNAME: {
3544 14298 : TypeSet *seen = bytecodeTypes(pc);
3545 14298 : seen->addSubset(cx, &pushed[0]);
3546 :
3547 : /*
3548 : * Try to resolve this name by walking the function's scope nesting.
3549 : * If we succeed but the accessed script has had its TypeScript purged
3550 : * in the past, we still must use a type barrier: the name access can
3551 : * be on a call object which predated the purge, and whose types might
3552 : * not be reflected in the reconstructed information.
3553 : */
3554 14298 : jsid id = GetAtomId(cx, script, pc, 0);
3555 14298 : NameAccess access = resolveNameAccess(cx, id);
3556 14298 : if (access.script && !access.script->typesPurged) {
3557 5838 : TypeSet *types = TypeScript::SlotTypes(access.script, access.slot);
3558 5838 : types->addSubsetBarrier(cx, script, pc, seen);
3559 : } else {
3560 8460 : addTypeBarrier(cx, pc, seen, Type::UnknownType());
3561 : }
3562 :
3563 14298 : if (op == JSOP_CALLNAME)
3564 2629 : pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
3565 14298 : break;
3566 : }
3567 :
3568 : case JSOP_BINDGNAME:
3569 : case JSOP_BINDNAME:
3570 39592 : break;
3571 :
3572 : case JSOP_SETGNAME: {
3573 38127 : jsid id = GetAtomId(cx, script, pc, 0);
3574 38127 : PropertyAccess(cx, script, pc, script->global()->getType(cx),
3575 76254 : true, poppedTypes(pc, 0), id);
3576 38127 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3577 38127 : break;
3578 : }
3579 :
3580 : case JSOP_SETNAME: {
3581 1411 : jsid id = GetAtomId(cx, script, pc, 0);
3582 1411 : NameAccess access = resolveNameAccess(cx, id);
3583 1411 : if (access.script) {
3584 347 : TypeSet *types = TypeScript::SlotTypes(access.script, access.slot);
3585 347 : poppedTypes(pc, 0)->addSubset(cx, types);
3586 : } else {
3587 1064 : cx->compartment->types.monitorBytecode(cx, script, offset);
3588 : }
3589 1411 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3590 1411 : break;
3591 : }
3592 :
3593 : case JSOP_SETCONST:
3594 12626 : cx->compartment->types.monitorBytecode(cx, script, offset);
3595 12626 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3596 12626 : break;
3597 :
3598 : case JSOP_GETXPROP: {
3599 119 : TypeSet *seen = bytecodeTypes(pc);
3600 119 : addTypeBarrier(cx, pc, seen, Type::UnknownType());
3601 119 : seen->addSubset(cx, &pushed[0]);
3602 119 : break;
3603 : }
3604 :
3605 : case JSOP_GETARG:
3606 : case JSOP_CALLARG:
3607 : case JSOP_GETLOCAL:
3608 : case JSOP_CALLLOCAL: {
3609 119038 : uint32_t slot = GetBytecodeSlot(script, pc);
3610 119038 : if (trackSlot(slot)) {
3611 : /*
3612 : * Normally these opcodes don't pop anything, but they are given
3613 : * an extended use holding the variable's SSA value before the
3614 : * access. Use the types from here.
3615 : */
3616 39277 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3617 79761 : } else if (slot < TotalSlots(script)) {
3618 59984 : TypeSet *types = TypeScript::SlotTypes(script, slot);
3619 59984 : types->addSubset(cx, &pushed[0]);
3620 : } else {
3621 : /* Local 'let' variable. Punt on types for these, for now. */
3622 19777 : pushed[0].addType(cx, Type::UnknownType());
3623 : }
3624 119038 : if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL)
3625 1688 : pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
3626 119038 : break;
3627 : }
3628 :
3629 : case JSOP_SETARG:
3630 : case JSOP_SETLOCAL: {
3631 97604 : uint32_t slot = GetBytecodeSlot(script, pc);
3632 97604 : if (!trackSlot(slot) && slot < TotalSlots(script)) {
3633 42423 : TypeSet *types = TypeScript::SlotTypes(script, slot);
3634 42423 : poppedTypes(pc, 0)->addSubset(cx, types);
3635 : }
3636 :
3637 : /*
3638 : * For assignments to non-escaping locals/args, we don't need to update
3639 : * the possible types of the var, as for each read of the var SSA gives
3640 : * us the writes that could have produced that read.
3641 : */
3642 97604 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3643 97604 : break;
3644 : }
3645 :
3646 : case JSOP_INCARG:
3647 : case JSOP_DECARG:
3648 : case JSOP_ARGINC:
3649 : case JSOP_ARGDEC:
3650 : case JSOP_INCLOCAL:
3651 : case JSOP_DECLOCAL:
3652 : case JSOP_LOCALINC:
3653 : case JSOP_LOCALDEC: {
3654 6268 : uint32_t slot = GetBytecodeSlot(script, pc);
3655 6268 : if (trackSlot(slot)) {
3656 2464 : poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
3657 3804 : } else if (slot < TotalSlots(script)) {
3658 2648 : TypeSet *types = TypeScript::SlotTypes(script, slot);
3659 2648 : types->addArith(cx, script, pc, types);
3660 2648 : types->addSubset(cx, &pushed[0]);
3661 : } else {
3662 1156 : pushed[0].addType(cx, Type::UnknownType());
3663 : }
3664 6268 : break;
3665 : }
3666 :
3667 : case JSOP_ARGUMENTS:
3668 : /* Compute a precise type only when we know the arguments won't escape. */
3669 800 : if (script->needsArgsObj())
3670 367 : pushed[0].addType(cx, Type::UnknownType());
3671 : else
3672 433 : pushed[0].addType(cx, Type::LazyArgsType());
3673 800 : break;
3674 :
3675 : case JSOP_SETPROP: {
3676 5566 : jsid id = GetAtomId(cx, script, pc, 0);
3677 5566 : poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id);
3678 5566 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3679 5566 : break;
3680 : }
3681 :
3682 : case JSOP_LENGTH:
3683 : case JSOP_GETPROP:
3684 : case JSOP_CALLPROP: {
3685 32757 : jsid id = GetAtomId(cx, script, pc, 0);
3686 32757 : TypeSet *seen = script->analysis()->bytecodeTypes(pc);
3687 :
3688 32757 : poppedTypes(pc, 0)->addGetProperty(cx, script, pc, seen, id);
3689 32757 : if (op == JSOP_CALLPROP)
3690 16450 : poppedTypes(pc, 0)->addCallProperty(cx, script, pc, id);
3691 :
3692 32757 : seen->addSubset(cx, &pushed[0]);
3693 32757 : if (CheckNextTest(pc))
3694 226 : pushed[0].addType(cx, Type::UndefinedType());
3695 32757 : break;
3696 : }
3697 :
3698 : /*
3699 : * We only consider ELEM accesses on integers below. Any element access
3700 : * which is accessing a non-integer property must be monitored.
3701 : */
3702 :
3703 : case JSOP_GETELEM:
3704 : case JSOP_CALLELEM: {
3705 10846 : TypeSet *seen = script->analysis()->bytecodeTypes(pc);
3706 :
3707 10846 : poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
3708 :
3709 10846 : seen->addSubset(cx, &pushed[0]);
3710 10846 : if (op == JSOP_CALLELEM)
3711 140 : pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), poppedTypes(pc, 1));
3712 10846 : if (CheckNextTest(pc))
3713 181 : pushed[0].addType(cx, Type::UndefinedType());
3714 10846 : break;
3715 : }
3716 :
3717 : case JSOP_SETELEM:
3718 4268 : poppedTypes(pc, 1)->addSetElement(cx, script, pc, poppedTypes(pc, 2), poppedTypes(pc, 0));
3719 4268 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3720 4268 : break;
3721 :
3722 : case JSOP_TOID:
3723 : /*
3724 : * This is only used for element inc/dec ops; any id produced which
3725 : * is not an integer must be monitored.
3726 : */
3727 148 : pushed[0].addType(cx, Type::Int32Type());
3728 148 : break;
3729 :
3730 : case JSOP_THIS:
3731 21585 : TypeScript::ThisTypes(script)->addTransformThis(cx, script, &pushed[0]);
3732 21585 : break;
3733 :
3734 : case JSOP_RETURN:
3735 : case JSOP_SETRVAL:
3736 7545 : if (script->function())
3737 7545 : poppedTypes(pc, 0)->addSubset(cx, TypeScript::ReturnTypes(script));
3738 7545 : break;
3739 :
3740 : case JSOP_ADD:
3741 340812 : poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 1));
3742 340812 : poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 0));
3743 340812 : break;
3744 :
3745 : case JSOP_SUB:
3746 : case JSOP_MUL:
3747 : case JSOP_MOD:
3748 : case JSOP_DIV:
3749 4113 : poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
3750 4113 : poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0]);
3751 4113 : break;
3752 :
3753 : case JSOP_NEG:
3754 : case JSOP_POS:
3755 5011 : poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
3756 5011 : break;
3757 :
3758 : case JSOP_LAMBDA:
3759 : case JSOP_DEFFUN: {
3760 30566 : JSObject *obj = script->getObject(GET_UINT32_INDEX(pc));
3761 :
3762 30566 : TypeSet *res = NULL;
3763 30566 : if (op == JSOP_LAMBDA)
3764 29838 : res = &pushed[0];
3765 :
3766 30566 : if (res) {
3767 29838 : if (script->hasGlobal())
3768 29784 : res->addType(cx, Type::ObjectType(obj));
3769 : else
3770 54 : res->addType(cx, Type::UnknownType());
3771 : } else {
3772 728 : cx->compartment->types.monitorBytecode(cx, script, offset);
3773 : }
3774 30566 : break;
3775 : }
3776 :
3777 : case JSOP_DEFVAR:
3778 852 : break;
3779 :
3780 : case JSOP_CALL:
3781 : case JSOP_EVAL:
3782 : case JSOP_FUNCALL:
3783 : case JSOP_FUNAPPLY:
3784 : case JSOP_NEW: {
3785 57521 : TypeSet *seen = script->analysis()->bytecodeTypes(pc);
3786 57521 : seen->addSubset(cx, &pushed[0]);
3787 :
3788 : /* Construct the base call information about this site. */
3789 57521 : unsigned argCount = GetUseCount(script, offset) - 2;
3790 57521 : TypeCallsite *callsite = cx->typeLifoAlloc().new_<TypeCallsite>(
3791 115042 : cx, script, pc, op == JSOP_NEW, argCount);
3792 57521 : if (!callsite || (argCount && !callsite->argumentTypes)) {
3793 0 : cx->compartment->types.setPendingNukeTypes(cx);
3794 0 : break;
3795 : }
3796 57521 : callsite->thisTypes = poppedTypes(pc, argCount);
3797 57521 : callsite->returnTypes = seen;
3798 :
3799 139039 : for (unsigned i = 0; i < argCount; i++)
3800 81518 : callsite->argumentTypes[i] = poppedTypes(pc, argCount - 1 - i);
3801 :
3802 : /*
3803 : * Mark FUNCALL and FUNAPPLY sites as monitored. The method JIT may
3804 : * lower these into normal calls, and we need to make sure the
3805 : * callee's argument types are checked on entry.
3806 : */
3807 57521 : if (op == JSOP_FUNCALL || op == JSOP_FUNAPPLY)
3808 828 : cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
3809 :
3810 57521 : poppedTypes(pc, argCount + 1)->addCall(cx, callsite);
3811 57521 : break;
3812 : }
3813 :
3814 : case JSOP_NEWINIT:
3815 : case JSOP_NEWARRAY:
3816 : case JSOP_NEWOBJECT: {
3817 6933 : TypeSet *types = script->analysis()->bytecodeTypes(pc);
3818 6933 : types->addSubset(cx, &pushed[0]);
3819 :
3820 6933 : if (UseNewTypeForInitializer(cx, script, pc)) {
3821 : /* Defer types pushed by this bytecode until runtime. */
3822 1206 : break;
3823 : }
3824 :
3825 5727 : TypeObject *initializer = GetInitializerType(cx, script, pc);
3826 5727 : if (script->hasGlobal()) {
3827 5443 : if (!initializer)
3828 0 : return false;
3829 5443 : types->addType(cx, Type::ObjectType(initializer));
3830 : } else {
3831 284 : JS_ASSERT(!initializer);
3832 284 : types->addType(cx, Type::UnknownType());
3833 : }
3834 5727 : break;
3835 : }
3836 :
3837 : case JSOP_ENDINIT:
3838 6933 : break;
3839 :
3840 : case JSOP_INITELEM: {
3841 11263 : const SSAValue &objv = poppedValue(pc, 2);
3842 11263 : jsbytecode *initpc = script->code + objv.pushedOffset();
3843 11263 : TypeObject *initializer = GetInitializerType(cx, script, initpc);
3844 :
3845 11263 : if (initializer) {
3846 10831 : pushed[0].addType(cx, Type::ObjectType(initializer));
3847 10831 : if (!initializer->unknownProperties()) {
3848 : /*
3849 : * Assume the initialized element is an integer. INITELEM can be used
3850 : * for doubles which don't map to the JSID_VOID property, which must
3851 : * be caught with dynamic monitoring.
3852 : */
3853 10819 : TypeSet *types = initializer->getProperty(cx, JSID_VOID, true);
3854 10819 : if (!types)
3855 0 : return false;
3856 10819 : if (state.hasGetSet) {
3857 0 : types->addType(cx, Type::UnknownType());
3858 10819 : } else if (state.hasHole) {
3859 1042 : if (!initializer->unknownProperties())
3860 1042 : initializer->setFlags(cx, OBJECT_FLAG_NON_PACKED_ARRAY);
3861 : } else {
3862 9777 : poppedTypes(pc, 0)->addSubset(cx, types);
3863 : }
3864 : }
3865 : } else {
3866 432 : pushed[0].addType(cx, Type::UnknownType());
3867 : }
3868 11263 : state.hasGetSet = false;
3869 11263 : state.hasHole = false;
3870 11263 : break;
3871 : }
3872 :
3873 : case JSOP_GETTER:
3874 : case JSOP_SETTER:
3875 66 : state.hasGetSet = true;
3876 66 : break;
3877 :
3878 : case JSOP_HOLE:
3879 1060 : state.hasHole = true;
3880 1060 : break;
3881 :
3882 : case JSOP_INITPROP: {
3883 5384 : const SSAValue &objv = poppedValue(pc, 1);
3884 5384 : jsbytecode *initpc = script->code + objv.pushedOffset();
3885 5384 : TypeObject *initializer = GetInitializerType(cx, script, initpc);
3886 :
3887 5384 : if (initializer) {
3888 4206 : pushed[0].addType(cx, Type::ObjectType(initializer));
3889 4206 : if (!initializer->unknownProperties()) {
3890 4206 : jsid id = GetAtomId(cx, script, pc, 0);
3891 4206 : TypeSet *types = initializer->getProperty(cx, id, true);
3892 4206 : if (!types)
3893 0 : return false;
3894 4206 : if (id == id___proto__(cx) || id == id_prototype(cx))
3895 6 : cx->compartment->types.monitorBytecode(cx, script, offset);
3896 4200 : else if (state.hasGetSet)
3897 8 : types->addType(cx, Type::UnknownType());
3898 : else
3899 4192 : poppedTypes(pc, 0)->addSubset(cx, types);
3900 : }
3901 : } else {
3902 1178 : pushed[0].addType(cx, Type::UnknownType());
3903 : }
3904 5384 : state.hasGetSet = false;
3905 5384 : JS_ASSERT(!state.hasHole);
3906 5384 : break;
3907 : }
3908 :
3909 : case JSOP_ENTERWITH:
3910 : case JSOP_ENTERBLOCK:
3911 : case JSOP_ENTERLET0:
3912 : /*
3913 : * Scope lookups can occur on the values being pushed here. We don't track
3914 : * the value or its properties, and just monitor all name opcodes in the
3915 : * script.
3916 : */
3917 17163 : break;
3918 :
3919 : case JSOP_ENTERLET1:
3920 : /*
3921 : * JSOP_ENTERLET1 enters a let block with an unrelated value on top of
3922 : * the stack (such as the condition to a switch) whose constraints must
3923 : * be propagated. The other values are ignored for the same reason as
3924 : * JSOP_ENTERLET0.
3925 : */
3926 212 : poppedTypes(pc, 0)->addSubset(cx, &pushed[defCount - 1]);
3927 212 : break;
3928 :
3929 : case JSOP_ITER: {
3930 : /*
3931 : * Use a per-script type set to unify the possible target types of all
3932 : * 'for in' or 'for each' loops in the script. We need to mark the
3933 : * value pushed by the ITERNEXT appropriately, but don't track the SSA
3934 : * information to connect that ITERNEXT with the appropriate ITER.
3935 : * This loses some precision when a script mixes 'for in' and
3936 : * 'for each' loops together, oh well.
3937 : */
3938 1558 : if (!state.forTypes) {
3939 1112 : state.forTypes = TypeSet::make(cx, "forTypes");
3940 1112 : if (!state.forTypes)
3941 0 : return false;
3942 : }
3943 :
3944 1558 : if (GET_UINT8(pc) & JSITER_FOREACH)
3945 288 : state.forTypes->addType(cx, Type::UnknownType());
3946 : else
3947 1270 : state.forTypes->addType(cx, Type::StringType());
3948 1558 : break;
3949 : }
3950 :
3951 : case JSOP_ITERNEXT:
3952 1558 : state.forTypes->addSubset(cx, &pushed[0]);
3953 1558 : break;
3954 :
3955 : case JSOP_MOREITER:
3956 1558 : pushed[1].addType(cx, Type::BooleanType());
3957 1558 : break;
3958 :
3959 : case JSOP_ENUMELEM:
3960 : case JSOP_ENUMCONSTELEM:
3961 : case JSOP_ARRAYPUSH:
3962 106 : cx->compartment->types.monitorBytecode(cx, script, offset);
3963 106 : break;
3964 :
3965 : case JSOP_THROW:
3966 : /* There will be a monitor on the bytecode catching the exception. */
3967 509 : break;
3968 :
3969 : case JSOP_FINALLY:
3970 : /* Pushes information about whether an exception was thrown. */
3971 35 : break;
3972 :
3973 : case JSOP_IMPLICITTHIS:
3974 : case JSOP_EXCEPTION:
3975 16209 : pushed[0].addType(cx, Type::UnknownType());
3976 16209 : break;
3977 :
3978 : case JSOP_DELPROP:
3979 : case JSOP_DELELEM:
3980 : case JSOP_DELNAME:
3981 343 : pushed[0].addType(cx, Type::BooleanType());
3982 343 : break;
3983 :
3984 : case JSOP_LEAVEBLOCKEXPR:
3985 192 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
3986 192 : break;
3987 :
3988 : case JSOP_LEAVEFORLETIN:
3989 230 : break;
3990 :
3991 : case JSOP_CASE:
3992 74 : poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
3993 74 : break;
3994 :
3995 : case JSOP_GENERATOR:
3996 142 : if (script->function()) {
3997 142 : if (script->hasGlobal()) {
3998 130 : JSObject *proto = script->global()->getOrCreateGeneratorPrototype(cx);
3999 130 : if (!proto)
4000 0 : return false;
4001 130 : TypeObject *object = proto->getNewType(cx);
4002 130 : if (!object)
4003 0 : return false;
4004 130 : TypeScript::ReturnTypes(script)->addType(cx, Type::ObjectType(object));
4005 : } else {
4006 12 : TypeScript::ReturnTypes(script)->addType(cx, Type::UnknownType());
4007 : }
4008 : }
4009 142 : break;
4010 :
4011 : case JSOP_YIELD:
4012 150 : pushed[0].addType(cx, Type::UnknownType());
4013 150 : break;
4014 :
4015 : case JSOP_CALLXMLNAME:
4016 0 : pushed[1].addType(cx, Type::UnknownType());
4017 : /* FALLTHROUGH */
4018 :
4019 : case JSOP_XMLNAME:
4020 11 : pushed[0].addType(cx, Type::UnknownType());
4021 11 : break;
4022 :
4023 : case JSOP_SETXMLNAME:
4024 4 : cx->compartment->types.monitorBytecode(cx, script, offset);
4025 4 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
4026 4 : break;
4027 :
4028 : case JSOP_BINDXMLNAME:
4029 4 : break;
4030 :
4031 : case JSOP_TOXML:
4032 : case JSOP_TOXMLLIST:
4033 : case JSOP_XMLPI:
4034 : case JSOP_XMLCDATA:
4035 : case JSOP_XMLCOMMENT:
4036 : case JSOP_DESCENDANTS:
4037 : case JSOP_TOATTRNAME:
4038 : case JSOP_QNAMECONST:
4039 : case JSOP_QNAME:
4040 : case JSOP_ANYNAME:
4041 : case JSOP_GETFUNNS:
4042 102 : pushed[0].addType(cx, Type::UnknownType());
4043 102 : break;
4044 :
4045 : case JSOP_FILTER:
4046 : /* Note: the second value pushed by filter is a hole, and not modelled. */
4047 16 : poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
4048 16 : break;
4049 :
4050 : case JSOP_ENDFILTER:
4051 16 : poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
4052 16 : break;
4053 :
4054 : case JSOP_CALLEE:
4055 87 : if (script->hasGlobal())
4056 71 : pushed[0].addType(cx, Type::ObjectType(script->function()));
4057 : else
4058 16 : pushed[0].addType(cx, Type::UnknownType());
4059 87 : break;
4060 :
4061 : default:
4062 : /* Display fine-grained debug information first */
4063 0 : fprintf(stderr, "Unknown bytecode %02x at #%u:%05u\n", op, script->id(), offset);
4064 0 : TypeFailure(cx, "Unknown bytecode %02x", op);
4065 : }
4066 :
4067 2116504 : return true;
4068 : }
4069 :
4070 : void
4071 38594 : ScriptAnalysis::analyzeTypes(JSContext *cx)
4072 : {
4073 38594 : JS_ASSERT(!ranInference());
4074 :
4075 38594 : if (OOM()) {
4076 0 : cx->compartment->types.setPendingNukeTypes(cx);
4077 0 : return;
4078 : }
4079 :
4080 : /*
4081 : * Refuse to analyze the types in a script which is compileAndGo but is
4082 : * running against a global with a cleared scope. Per GlobalObject::clear,
4083 : * we won't be running anymore compileAndGo code against the global
4084 : * (moreover, after clearing our analysis results will be wrong for the
4085 : * script and trying to reanalyze here can cause reentrance problems if we
4086 : * try to reinitialize standard classes that were cleared).
4087 : */
4088 38594 : if (script->hasClearedGlobal())
4089 0 : return;
4090 :
4091 38594 : if (!ranSSA()) {
4092 38063 : analyzeSSA(cx);
4093 38063 : if (failed())
4094 0 : return;
4095 : }
4096 :
4097 : /*
4098 : * Set this early to avoid reentrance. Any failures are OOMs, and will nuke
4099 : * all types in the compartment.
4100 : */
4101 38594 : ranInference_ = true;
4102 :
4103 : /* Make sure the initial type set of all local vars includes void. */
4104 70553 : for (unsigned i = 0; i < script->nfixed; i++)
4105 31959 : TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType());
4106 :
4107 38594 : TypeScriptNesting *nesting = script->function() ? script->nesting() : NULL;
4108 38594 : if (nesting && nesting->parent) {
4109 : /*
4110 : * Check whether NAME accesses can be resolved in parent scopes, and
4111 : * detach from the parent if so. Even if outdated activations of this
4112 : * function are live when the parent is called again, we do not need to
4113 : * consider this reentrance as no state in the parent will be used.
4114 : */
4115 1461 : if (!nesting->parent->ensureRanInference(cx))
4116 0 : return;
4117 :
4118 1461 : bool detached = false;
4119 :
4120 : /* Don't track for leaf scripts which have no free variables. */
4121 1461 : if (!usesScopeChain() && !script->isOuterFunction) {
4122 30 : DetachNestingParent(script);
4123 30 : detached = true;
4124 : }
4125 :
4126 : /*
4127 : * If the names bound by the script are extensible (DEFFUN, EVAL, ...),
4128 : * don't resolve NAME accesses into the parent.
4129 : */
4130 1461 : if (!detached && extendsScope()) {
4131 50 : DetachNestingParent(script);
4132 50 : detached = true;
4133 : }
4134 :
4135 :
4136 1461 : if (!detached) {
4137 : /*
4138 : * Don't track for parents which add call objects or are generators,
4139 : * don't resolve NAME accesses into the parent.
4140 : */
4141 1381 : if (nesting->parent->analysis()->addsScopeObjects() ||
4142 : JSOp(*nesting->parent->code) == JSOP_GENERATOR)
4143 : {
4144 88 : DetachNestingParent(script);
4145 : }
4146 : }
4147 : }
4148 :
4149 77188 : TypeInferenceState state(cx);
4150 :
4151 38594 : unsigned offset = 0;
4152 2219768 : while (offset < script->length) {
4153 2142580 : Bytecode *code = maybeCode(offset);
4154 :
4155 2142580 : jsbytecode *pc = script->code + offset;
4156 :
4157 2142580 : if (code && !analyzeTypesBytecode(cx, offset, state)) {
4158 0 : cx->compartment->types.setPendingNukeTypes(cx);
4159 : return;
4160 : }
4161 :
4162 2142580 : offset += GetBytecodeLength(pc);
4163 : }
4164 :
4165 52865 : for (unsigned i = 0; i < state.phiNodes.length(); i++) {
4166 14271 : SSAPhiNode *node = state.phiNodes[i];
4167 37873 : for (unsigned j = 0; j < node->length; j++) {
4168 23602 : const SSAValue &v = node->options[j];
4169 23602 : getValueTypes(v)->addSubset(cx, &node->types);
4170 : }
4171 : }
4172 :
4173 : /*
4174 : * Replay any dynamic type results which have been generated for the script
4175 : * either because we ran the interpreter some before analyzing or because
4176 : * we are reanalyzing after a GC.
4177 : */
4178 38594 : TypeResult *result = script->types->dynamicList;
4179 77963 : while (result) {
4180 775 : if (result->offset != UINT32_MAX) {
4181 762 : pushedTypes(result->offset)->addType(cx, result->type);
4182 : } else {
4183 : /* Custom for-in loop iteration has happened in this script. */
4184 13 : state.forTypes->addType(cx, Type::UnknownType());
4185 : }
4186 775 : result = result->next;
4187 : }
4188 : }
4189 :
4190 : bool
4191 15108 : ScriptAnalysis::integerOperation(JSContext *cx, jsbytecode *pc)
4192 : {
4193 15108 : JS_ASSERT(uint32_t(pc - script->code) < script->length);
4194 :
4195 15108 : switch (JSOp(*pc)) {
4196 :
4197 : case JSOP_INCARG:
4198 : case JSOP_DECARG:
4199 : case JSOP_ARGINC:
4200 : case JSOP_ARGDEC:
4201 : case JSOP_INCLOCAL:
4202 : case JSOP_DECLOCAL:
4203 : case JSOP_LOCALINC:
4204 : case JSOP_LOCALDEC: {
4205 11988 : if (pushedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
4206 76 : return false;
4207 11912 : uint32_t slot = GetBytecodeSlot(script, pc);
4208 11912 : if (trackSlot(slot)) {
4209 11912 : if (poppedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
4210 22 : return false;
4211 : }
4212 11890 : return true;
4213 : }
4214 :
4215 : case JSOP_ADD:
4216 : case JSOP_SUB:
4217 : case JSOP_MUL:
4218 : case JSOP_DIV:
4219 3096 : if (pushedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
4220 1236 : return false;
4221 1860 : if (poppedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
4222 63 : return false;
4223 1797 : if (poppedTypes(pc, 1)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
4224 158 : return false;
4225 1639 : return true;
4226 :
4227 : default:
4228 24 : return true;
4229 : }
4230 : }
4231 :
4232 : /*
4233 : * Persistent constraint clearing out newScript and definite properties from
4234 : * an object should a property on another object get a setter.
4235 : */
4236 : class TypeConstraintClearDefiniteSetter : public TypeConstraint
4237 : {
4238 : public:
4239 : TypeObject *object;
4240 :
4241 2211 : TypeConstraintClearDefiniteSetter(TypeObject *object)
4242 2211 : : TypeConstraint("clearDefiniteSetter"), object(object)
4243 2211 : {}
4244 :
4245 45 : void newPropertyState(JSContext *cx, TypeSet *source)
4246 : {
4247 45 : if (!object->newScript)
4248 0 : return;
4249 : /*
4250 : * Clear out the newScript shape and definite property information from
4251 : * an object if the source type set could be a setter or could be
4252 : * non-writable, both of which are indicated by the source type set
4253 : * being marked as configured.
4254 : */
4255 45 : if (!(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED) && source->isOwnProperty(true))
4256 25 : object->clearNewScript(cx);
4257 : }
4258 :
4259 65 : void newType(JSContext *cx, TypeSet *source, Type type) {}
4260 : };
4261 :
4262 : /*
4263 : * Constraint which clears definite properties on an object should a type set
4264 : * contain any types other than a single object.
4265 : */
4266 : class TypeConstraintClearDefiniteSingle : public TypeConstraint
4267 : {
4268 : public:
4269 : TypeObject *object;
4270 :
4271 78 : TypeConstraintClearDefiniteSingle(TypeObject *object)
4272 78 : : TypeConstraint("clearDefiniteSingle"), object(object)
4273 78 : {}
4274 :
4275 83 : void newType(JSContext *cx, TypeSet *source, Type type) {
4276 83 : if (object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)
4277 0 : return;
4278 :
4279 83 : if (source->baseFlags() || source->getObjectCount() > 1)
4280 5 : object->clearNewScript(cx);
4281 : }
4282 : };
4283 :
4284 : static bool
4285 1452 : AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSObject **pbaseobj,
4286 : Vector<TypeNewScript::Initializer> *initializerList)
4287 : {
4288 : /*
4289 : * When invoking 'new' on the specified script, try to find some properties
4290 : * which will definitely be added to the created object before it has a
4291 : * chance to escape and be accessed elsewhere.
4292 : *
4293 : * Returns true if the entire script was analyzed (pbaseobj has been
4294 : * preserved), false if we had to bail out part way through (pbaseobj may
4295 : * have been cleared).
4296 : */
4297 :
4298 1452 : if (initializerList->length() > 50) {
4299 : /*
4300 : * Bail out on really long initializer lists (far longer than maximum
4301 : * number of properties we can track), we may be recursing.
4302 : */
4303 0 : return false;
4304 : }
4305 :
4306 1452 : JSScript *script = fun->script();
4307 1452 : JS_ASSERT(!script->isInnerFunction);
4308 :
4309 1452 : if (!script->ensureRanAnalysis(cx, fun) || !script->ensureRanInference(cx)) {
4310 0 : *pbaseobj = NULL;
4311 0 : cx->compartment->types.setPendingNukeTypes(cx);
4312 0 : return false;
4313 : }
4314 :
4315 1452 : if (script->hasClearedGlobal())
4316 0 : return false;
4317 :
4318 1452 : ScriptAnalysis *analysis = script->analysis();
4319 :
4320 : /*
4321 : * Offset of the last bytecode which popped 'this' and which we have
4322 : * processed. For simplicity, we scan for places where 'this' is pushed
4323 : * and immediately analyze the place where that pushed value is popped.
4324 : * This runs the risk of doing things out of order, if the script looks
4325 : * something like 'this.f = (this.g = ...)', so we watch and bail out if
4326 : * a 'this' is pushed before the previous 'this' value was popped.
4327 : */
4328 1452 : uint32_t lastThisPopped = 0;
4329 :
4330 1452 : unsigned nextOffset = 0;
4331 12837 : while (nextOffset < script->length) {
4332 11355 : unsigned offset = nextOffset;
4333 11355 : jsbytecode *pc = script->code + offset;
4334 :
4335 11355 : JSOp op = JSOp(*pc);
4336 :
4337 11355 : nextOffset += GetBytecodeLength(pc);
4338 :
4339 11355 : Bytecode *code = analysis->maybeCode(pc);
4340 11355 : if (!code)
4341 40 : continue;
4342 :
4343 : /*
4344 : * End analysis after the first return statement from the script,
4345 : * returning success if the return is unconditional.
4346 : */
4347 11315 : if (op == JSOP_RETURN || op == JSOP_STOP || op == JSOP_RETRVAL) {
4348 1078 : if (offset < lastThisPopped) {
4349 0 : *pbaseobj = NULL;
4350 0 : return false;
4351 : }
4352 1078 : return code->unconditional;
4353 : }
4354 :
4355 : /* 'this' can escape through a call to eval. */
4356 10237 : if (op == JSOP_EVAL) {
4357 75 : if (offset < lastThisPopped)
4358 0 : *pbaseobj = NULL;
4359 75 : return false;
4360 : }
4361 :
4362 : /*
4363 : * We are only interested in places where 'this' is popped. The new
4364 : * 'this' value cannot escape and be accessed except through such uses.
4365 : */
4366 10162 : if (op != JSOP_THIS)
4367 8838 : continue;
4368 :
4369 : /* Maintain ordering property on how 'this' is used, as described above. */
4370 1324 : if (offset < lastThisPopped) {
4371 10 : *pbaseobj = NULL;
4372 10 : return false;
4373 : }
4374 :
4375 1314 : SSAValue thisv = SSAValue::PushedValue(offset, 0);
4376 1314 : SSAUseChain *uses = analysis->useChain(thisv);
4377 :
4378 1314 : JS_ASSERT(uses);
4379 1314 : if (uses->next || !uses->popped) {
4380 : /* 'this' value popped in more than one place. */
4381 24 : return false;
4382 : }
4383 :
4384 1290 : lastThisPopped = uses->offset;
4385 :
4386 : /* Only handle 'this' values popped in unconditional code. */
4387 1290 : Bytecode *poppedCode = analysis->maybeCode(uses->offset);
4388 1290 : if (!poppedCode || !poppedCode->unconditional)
4389 25 : return false;
4390 :
4391 1265 : pc = script->code + uses->offset;
4392 1265 : op = JSOp(*pc);
4393 :
4394 1265 : JSObject *obj = *pbaseobj;
4395 :
4396 1265 : if (op == JSOP_SETPROP && uses->u.which == 1) {
4397 : /*
4398 : * Don't use GetAtomId here, we need to watch for SETPROP on
4399 : * integer properties and bail out. We can't mark the aggregate
4400 : * JSID_VOID type property as being in a definite slot.
4401 : */
4402 1056 : jsid id = ATOM_TO_JSID(script->getAtom(GET_UINT32_INDEX(pc)));
4403 1056 : if (MakeTypeId(cx, id) != id)
4404 5 : return false;
4405 1051 : if (id == id_prototype(cx) || id == id___proto__(cx) || id == id_constructor(cx))
4406 0 : return false;
4407 :
4408 : /*
4409 : * Ensure that if the properties named here could have a setter or
4410 : * a permanent property in any transitive prototype, the definite
4411 : * properties get cleared from the shape.
4412 : */
4413 1051 : JSObject *parent = type->proto;
4414 4313 : while (parent) {
4415 2226 : TypeObject *parentObject = parent->getType(cx);
4416 2226 : if (parentObject->unknownProperties())
4417 0 : return false;
4418 2226 : TypeSet *parentTypes = parentObject->getProperty(cx, id, false);
4419 2226 : if (!parentTypes || parentTypes->isOwnProperty(true))
4420 15 : return false;
4421 2211 : parentTypes->add(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSetter>(type));
4422 2211 : parent = parent->getProto();
4423 : }
4424 :
4425 1036 : unsigned slotSpan = obj->slotSpan();
4426 1036 : if (!DefineNativeProperty(cx, obj, id, UndefinedValue(), NULL, NULL,
4427 1036 : JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) {
4428 0 : cx->compartment->types.setPendingNukeTypes(cx);
4429 0 : *pbaseobj = NULL;
4430 0 : return false;
4431 : }
4432 :
4433 1036 : if (obj->inDictionaryMode()) {
4434 0 : *pbaseobj = NULL;
4435 0 : return false;
4436 : }
4437 :
4438 1036 : if (obj->slotSpan() == slotSpan) {
4439 : /* Set a duplicate property. */
4440 8 : return false;
4441 : }
4442 :
4443 1028 : TypeNewScript::Initializer setprop(TypeNewScript::Initializer::SETPROP, uses->offset);
4444 1028 : if (!initializerList->append(setprop)) {
4445 0 : cx->compartment->types.setPendingNukeTypes(cx);
4446 0 : *pbaseobj = NULL;
4447 0 : return false;
4448 : }
4449 :
4450 1028 : if (obj->slotSpan() >= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
4451 : /* Maximum number of definite properties added. */
4452 0 : return false;
4453 1028 : }
4454 209 : } else if (op == JSOP_FUNCALL && uses->u.which == GET_ARGC(pc) - 1) {
4455 : /*
4456 : * Passed as the first parameter to Function.call. Follow control
4457 : * into the callee, and add any definite properties it assigns to
4458 : * the object as well. :TODO: This is narrow pattern matching on
4459 : * the inheritance patterns seen in the v8-deltablue benchmark, and
4460 : * needs robustness against other ways initialization can cross
4461 : * script boundaries.
4462 : *
4463 : * Add constraints ensuring we are calling Function.call on a
4464 : * particular script, removing definite properties from the result
4465 : */
4466 :
4467 : /* Callee/this must have been pushed by a CALLPROP. */
4468 47 : SSAValue calleev = analysis->poppedValue(pc, GET_ARGC(pc) + 1);
4469 47 : if (calleev.kind() != SSAValue::PUSHED)
4470 0 : return false;
4471 47 : jsbytecode *calleepc = script->code + calleev.pushedOffset();
4472 47 : if (JSOp(*calleepc) != JSOP_CALLPROP)
4473 0 : return false;
4474 :
4475 : /*
4476 : * This code may not have run yet, break any type barriers involved
4477 : * in performing the call (for the greater good!).
4478 : */
4479 47 : analysis->breakTypeBarriersSSA(cx, analysis->poppedValue(calleepc, 0));
4480 47 : analysis->breakTypeBarriers(cx, calleepc - script->code, true);
4481 :
4482 47 : TypeSet *funcallTypes = analysis->poppedTypes(pc, GET_ARGC(pc) + 1);
4483 47 : TypeSet *scriptTypes = analysis->poppedTypes(pc, GET_ARGC(pc));
4484 :
4485 : /* Need to definitely be calling Function.call on a specific script. */
4486 47 : JSObject *funcallObj = funcallTypes->getSingleton(cx, false);
4487 47 : JSObject *scriptObj = scriptTypes->getSingleton(cx, false);
4488 86 : if (!funcallObj || !scriptObj || !scriptObj->isFunction() ||
4489 39 : !scriptObj->toFunction()->isInterpreted()) {
4490 8 : return false;
4491 : }
4492 :
4493 39 : JSFunction *function = scriptObj->toFunction();
4494 39 : JS_ASSERT(!function->script()->isInnerFunction);
4495 :
4496 : /*
4497 : * Generate constraints to clear definite properties from the type
4498 : * should the Function.call or callee itself change in the future.
4499 : */
4500 : funcallTypes->add(cx,
4501 39 : cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
4502 : scriptTypes->add(cx,
4503 39 : cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
4504 :
4505 39 : TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset);
4506 39 : if (!initializerList->append(pushframe)) {
4507 0 : cx->compartment->types.setPendingNukeTypes(cx);
4508 0 : *pbaseobj = NULL;
4509 0 : return false;
4510 : }
4511 :
4512 39 : if (!AnalyzeNewScriptProperties(cx, type, function,
4513 39 : pbaseobj, initializerList)) {
4514 12 : return false;
4515 : }
4516 :
4517 27 : TypeNewScript::Initializer popframe(TypeNewScript::Initializer::FRAME_POP, 0);
4518 27 : if (!initializerList->append(popframe)) {
4519 0 : cx->compartment->types.setPendingNukeTypes(cx);
4520 0 : *pbaseobj = NULL;
4521 0 : return false;
4522 27 : }
4523 :
4524 : /*
4525 : * The callee never lets the 'this' value escape, continue looking
4526 : * for definite properties in the remainder of this script.
4527 : */
4528 : } else {
4529 : /* Unhandled use of 'this'. */
4530 162 : return false;
4531 : }
4532 : }
4533 :
4534 : /* Will have hit a STOP or similar, unless the script always throws. */
4535 30 : return true;
4536 : }
4537 :
4538 : /*
4539 : * Either make the newScript information for type when it is constructed
4540 : * by the specified script, or regenerate the constraints for an existing
4541 : * newScript on the type after they were cleared by a GC.
4542 : */
4543 : static void
4544 1473 : CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
4545 : {
4546 1473 : if (type->unknownProperties() || fun->script()->isInnerFunction)
4547 60 : return;
4548 :
4549 : /* Strawman object to add properties to and watch for duplicates. */
4550 1413 : JSObject *baseobj = NewBuiltinClassInstance(cx, &ObjectClass, gc::FINALIZE_OBJECT16);
4551 1413 : if (!baseobj) {
4552 0 : if (type->newScript)
4553 0 : type->clearNewScript(cx);
4554 0 : return;
4555 : }
4556 :
4557 2826 : Vector<TypeNewScript::Initializer> initializerList(cx);
4558 1413 : AnalyzeNewScriptProperties(cx, type, fun, &baseobj, &initializerList);
4559 1413 : if (!baseobj || baseobj->slotSpan() == 0 || !!(type->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)) {
4560 832 : if (type->newScript)
4561 0 : type->clearNewScript(cx);
4562 : return;
4563 : }
4564 :
4565 : /*
4566 : * If the type already has a new script, we are just regenerating the type
4567 : * constraints and don't need to make another TypeNewScript. Make sure that
4568 : * the properties added to baseobj match the type's definite properties.
4569 : */
4570 581 : if (type->newScript) {
4571 2 : if (!type->matchDefiniteProperties(baseobj))
4572 0 : type->clearNewScript(cx);
4573 : return;
4574 : }
4575 :
4576 579 : gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan());
4577 :
4578 : /* We should not have overflowed the maximum number of fixed slots for an object. */
4579 579 : JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan());
4580 :
4581 579 : TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0);
4582 :
4583 : /*
4584 : * The base object may have been created with a different finalize kind
4585 : * than we will use for subsequent new objects. Generate an object with the
4586 : * appropriate final shape.
4587 : */
4588 : baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind,
4589 579 : baseobj->lastProperty());
4590 1737 : if (!baseobj ||
4591 579 : !type->addDefiniteProperties(cx, baseobj) ||
4592 579 : !initializerList.append(done)) {
4593 0 : cx->compartment->types.setPendingNukeTypes(cx);
4594 : return;
4595 : }
4596 :
4597 : size_t numBytes = sizeof(TypeNewScript)
4598 579 : + (initializerList.length() * sizeof(TypeNewScript::Initializer));
4599 579 : type->newScript = (TypeNewScript *) cx->calloc_(numBytes);
4600 579 : if (!type->newScript) {
4601 0 : cx->compartment->types.setPendingNukeTypes(cx);
4602 : return;
4603 : }
4604 :
4605 579 : type->newScript->fun = fun;
4606 579 : type->newScript->allocKind = kind;
4607 579 : type->newScript->shape = baseobj->lastProperty();
4608 :
4609 579 : type->newScript->initializerList = (TypeNewScript::Initializer *)
4610 579 : ((char *) type->newScript.get() + sizeof(TypeNewScript));
4611 579 : PodCopy(type->newScript->initializerList, initializerList.begin(), initializerList.length());
4612 : }
4613 :
4614 : /////////////////////////////////////////////////////////////////////
4615 : // Printing
4616 : /////////////////////////////////////////////////////////////////////
4617 :
4618 : void
4619 0 : ScriptAnalysis::printTypes(JSContext *cx)
4620 : {
4621 0 : AutoEnterAnalysis enter(script->compartment());
4622 0 : TypeCompartment *compartment = &script->compartment()->types;
4623 :
4624 : /*
4625 : * Check if there are warnings for used values with unknown types, and build
4626 : * statistics about the size of type sets found for stack values.
4627 : */
4628 0 : for (unsigned offset = 0; offset < script->length; offset++) {
4629 0 : if (!maybeCode(offset))
4630 0 : continue;
4631 :
4632 0 : jsbytecode *pc = script->code + offset;
4633 :
4634 0 : if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
4635 0 : continue;
4636 :
4637 0 : unsigned defCount = GetDefCount(script, offset);
4638 0 : if (!defCount)
4639 0 : continue;
4640 :
4641 0 : for (unsigned i = 0; i < defCount; i++) {
4642 0 : TypeSet *types = pushedTypes(offset, i);
4643 :
4644 0 : if (types->unknown()) {
4645 0 : compartment->typeCountOver++;
4646 0 : continue;
4647 : }
4648 :
4649 0 : unsigned typeCount = 0;
4650 :
4651 0 : if (types->hasAnyFlag(TYPE_FLAG_ANYOBJECT) || types->getObjectCount() != 0)
4652 0 : typeCount++;
4653 0 : for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
4654 0 : if (types->hasAnyFlag(flag))
4655 0 : typeCount++;
4656 : }
4657 :
4658 : /*
4659 : * Adjust the type counts for floats: values marked as floats
4660 : * are also marked as ints by the inference, but for counting
4661 : * we don't consider these to be separate types.
4662 : */
4663 0 : if (types->hasAnyFlag(TYPE_FLAG_DOUBLE)) {
4664 0 : JS_ASSERT(types->hasAnyFlag(TYPE_FLAG_INT32));
4665 0 : typeCount--;
4666 : }
4667 :
4668 0 : if (typeCount > TypeCompartment::TYPE_COUNT_LIMIT) {
4669 0 : compartment->typeCountOver++;
4670 0 : } else if (typeCount == 0) {
4671 : /* Ignore values without types, this may be unreached code. */
4672 : } else {
4673 0 : compartment->typeCounts[typeCount-1]++;
4674 : }
4675 : }
4676 : }
4677 :
4678 : #ifdef DEBUG
4679 :
4680 0 : if (script->function())
4681 0 : printf("Function");
4682 0 : else if (script->isCachedEval)
4683 0 : printf("Eval");
4684 : else
4685 0 : printf("Main");
4686 0 : printf(" #%u %s (line %d):\n", script->id(), script->filename, script->lineno);
4687 :
4688 0 : printf("locals:");
4689 0 : printf("\n return:");
4690 0 : TypeScript::ReturnTypes(script)->print(cx);
4691 0 : printf("\n this:");
4692 0 : TypeScript::ThisTypes(script)->print(cx);
4693 :
4694 0 : for (unsigned i = 0; script->function() && i < script->function()->nargs; i++) {
4695 0 : printf("\n arg%u:", i);
4696 0 : TypeScript::ArgTypes(script, i)->print(cx);
4697 : }
4698 0 : for (unsigned i = 0; i < script->nfixed; i++) {
4699 0 : if (!trackSlot(LocalSlot(script, i))) {
4700 0 : printf("\n local%u:", i);
4701 0 : TypeScript::LocalTypes(script, i)->print(cx);
4702 : }
4703 : }
4704 0 : printf("\n");
4705 :
4706 0 : for (unsigned offset = 0; offset < script->length; offset++) {
4707 0 : if (!maybeCode(offset))
4708 0 : continue;
4709 :
4710 0 : jsbytecode *pc = script->code + offset;
4711 :
4712 0 : PrintBytecode(cx, script, pc);
4713 :
4714 0 : if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
4715 0 : continue;
4716 :
4717 0 : if (js_CodeSpec[*pc].format & JOF_TYPESET) {
4718 0 : TypeSet *types = script->analysis()->bytecodeTypes(pc);
4719 0 : printf(" typeset %d:", (int) (types - script->types->typeArray()));
4720 0 : types->print(cx);
4721 0 : printf("\n");
4722 : }
4723 :
4724 0 : unsigned defCount = GetDefCount(script, offset);
4725 0 : for (unsigned i = 0; i < defCount; i++) {
4726 0 : printf(" type %d:", i);
4727 0 : pushedTypes(offset, i)->print(cx);
4728 0 : printf("\n");
4729 : }
4730 :
4731 0 : if (getCode(offset).monitoredTypes)
4732 0 : printf(" monitored\n");
4733 :
4734 0 : TypeBarrier *barrier = getCode(offset).typeBarriers;
4735 0 : if (barrier != NULL) {
4736 0 : printf(" barrier:");
4737 0 : while (barrier) {
4738 0 : printf(" %s", TypeString(barrier->type));
4739 0 : barrier = barrier->next;
4740 : }
4741 0 : printf("\n");
4742 : }
4743 : }
4744 :
4745 0 : printf("\n");
4746 :
4747 : #endif /* DEBUG */
4748 :
4749 0 : }
4750 :
4751 : /////////////////////////////////////////////////////////////////////
4752 : // Interface functions
4753 : /////////////////////////////////////////////////////////////////////
4754 :
4755 : namespace js {
4756 : namespace types {
4757 :
4758 : void
4759 2396 : MarkIteratorUnknownSlow(JSContext *cx)
4760 : {
4761 : /* Check whether we are actually at an ITER opcode. */
4762 :
4763 : jsbytecode *pc;
4764 2396 : JSScript *script = cx->stack.currentScript(&pc);
4765 2396 : if (!script || !pc)
4766 5 : return;
4767 :
4768 2391 : if (JSOp(*pc) != JSOP_ITER)
4769 150 : return;
4770 :
4771 4482 : AutoEnterTypeInference enter(cx);
4772 :
4773 : /*
4774 : * This script is iterating over an actual Iterator or Generator object, or
4775 : * an object with a custom __iterator__ hook. In such cases 'for in' loops
4776 : * can produce values other than strings, and the types of the ITER opcodes
4777 : * in the script need to be updated. During analysis this is done with the
4778 : * forTypes in the analysis state, but we don't keep a pointer to this type
4779 : * set and need to scan the script to fix affected opcodes.
4780 : */
4781 :
4782 2241 : TypeResult *result = script->types->dynamicList;
4783 4482 : while (result) {
4784 1786 : if (result->offset == UINT32_MAX) {
4785 : /* Already know about custom iterators used in this script. */
4786 1786 : JS_ASSERT(result->type.isUnknown());
4787 : return;
4788 : }
4789 0 : result = result->next;
4790 : }
4791 :
4792 455 : InferSpew(ISpewOps, "externalType: customIterator #%u", script->id());
4793 :
4794 455 : result = cx->new_<TypeResult>(UINT32_MAX, Type::UnknownType());
4795 455 : if (!result) {
4796 0 : cx->compartment->types.setPendingNukeTypes(cx);
4797 : return;
4798 : }
4799 455 : result->next = script->types->dynamicList;
4800 455 : script->types->dynamicList = result;
4801 :
4802 455 : if (!script->hasAnalysis() || !script->analysis()->ranInference())
4803 : return;
4804 :
4805 182 : ScriptAnalysis *analysis = script->analysis();
4806 :
4807 25124 : for (unsigned i = 0; i < script->length; i++) {
4808 24942 : jsbytecode *pc = script->code + i;
4809 24942 : if (!analysis->maybeCode(pc))
4810 15908 : continue;
4811 9034 : if (JSOp(*pc) == JSOP_ITERNEXT)
4812 238 : analysis->pushedTypes(pc, 0)->addType(cx, Type::UnknownType());
4813 : }
4814 :
4815 : /* Trigger recompilation of any inline callers. */
4816 182 : if (script->function() && !script->function()->hasLazyType())
4817 36 : ObjectStateChange(cx, script->function()->type(), false, true);
4818 : }
4819 :
4820 : void
4821 12965925 : TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
4822 : const CallArgs &args, bool constructing)
4823 : {
4824 12965925 : unsigned nargs = callee->toFunction()->nargs;
4825 12965925 : JSScript *script = callee->toFunction()->script();
4826 :
4827 12965925 : if (!constructing)
4828 11653127 : TypeScript::SetThis(cx, script, args.thisv());
4829 :
4830 : /*
4831 : * Add constraints going up to the minimum of the actual and formal count.
4832 : * If there are more actuals than formals the later values can only be
4833 : * accessed through the arguments object, which is monitored.
4834 : */
4835 12965925 : unsigned arg = 0;
4836 33542584 : for (; arg < args.length() && arg < nargs; arg++)
4837 20576659 : TypeScript::SetArgument(cx, script, arg, args[arg]);
4838 :
4839 : /* Watch for fewer actuals than formals to the call. */
4840 13083893 : for (; arg < nargs; arg++)
4841 117968 : TypeScript::SetArgument(cx, script, arg, UndefinedValue());
4842 12965925 : }
4843 :
4844 : static inline bool
4845 673292 : IsAboutToBeFinalized(TypeObjectKey *key)
4846 : {
4847 : /* Mask out the low bit indicating whether this is a type or JS object. */
4848 673292 : return !reinterpret_cast<const gc::Cell *>(uintptr_t(key) & ~1)->isMarked();
4849 : }
4850 :
4851 : void
4852 815409 : TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
4853 : {
4854 815409 : JS_ASSERT(cx->typeInferenceEnabled());
4855 1630818 : AutoEnterTypeInference enter(cx);
4856 :
4857 : /* Directly update associated type sets for applicable bytecodes. */
4858 815409 : if (js_CodeSpec[*pc].format & JOF_TYPESET) {
4859 458 : if (!script->ensureRanAnalysis(cx, NULL)) {
4860 0 : cx->compartment->types.setPendingNukeTypes(cx);
4861 : return;
4862 : }
4863 458 : TypeSet *types = script->analysis()->bytecodeTypes(pc);
4864 458 : if (!types->hasType(type)) {
4865 : InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s",
4866 73 : script->id(), pc - script->code, TypeString(type));
4867 73 : types->addType(cx, type);
4868 : }
4869 : return;
4870 : }
4871 :
4872 : /*
4873 : * For inc/dec ops, we need to go back and reanalyze the affected opcode
4874 : * taking the overflow into account. We won't see an explicit adjustment
4875 : * of the type of the thing being inc/dec'ed, nor will adding TYPE_DOUBLE to
4876 : * the pushed value affect that type.
4877 : */
4878 814951 : JSOp op = JSOp(*pc);
4879 814951 : const JSCodeSpec *cs = &js_CodeSpec[op];
4880 814951 : if (cs->format & (JOF_INC | JOF_DEC)) {
4881 1247 : switch (op) {
4882 : case JSOP_INCLOCAL:
4883 : case JSOP_DECLOCAL:
4884 : case JSOP_LOCALINC:
4885 : case JSOP_LOCALDEC:
4886 : case JSOP_INCARG:
4887 : case JSOP_DECARG:
4888 : case JSOP_ARGINC:
4889 : case JSOP_ARGDEC: {
4890 : /*
4891 : * Just mark the slot's type as holding the new type. This captures
4892 : * the effect if the slot is not being tracked, and if the slot
4893 : * doesn't escape we will update the pushed types below to capture
4894 : * the slot's value after this write.
4895 : */
4896 1247 : uint32_t slot = GetBytecodeSlot(script, pc);
4897 1247 : if (slot < TotalSlots(script)) {
4898 1229 : TypeSet *types = TypeScript::SlotTypes(script, slot);
4899 1229 : types->addType(cx, type);
4900 : }
4901 1247 : break;
4902 : }
4903 :
4904 : default:;
4905 : }
4906 : }
4907 :
4908 814951 : if (script->hasAnalysis() && script->analysis()->ranInference()) {
4909 : /*
4910 : * If the pushed set already has this type, we don't need to ensure
4911 : * there is a TypeIntermediate. Either there already is one, or the
4912 : * type could be determined from the script's other input type sets.
4913 : */
4914 252131 : TypeSet *pushed = script->analysis()->pushedTypes(pc, 0);
4915 252131 : if (pushed->hasType(type))
4916 : return;
4917 : } else {
4918 : /* Scan all intermediate types on the script to check for a dupe. */
4919 562820 : TypeResult *result, **pstart = &script->types->dynamicList, **presult = pstart;
4920 2179152 : while (*presult) {
4921 1614822 : result = *presult;
4922 1614822 : if (result->offset == unsigned(pc - script->code) && result->type == type) {
4923 561310 : if (presult != pstart) {
4924 : /* Move to the head of the list, maintain LRU order. */
4925 319341 : *presult = result->next;
4926 319341 : result->next = *pstart;
4927 319341 : *pstart = result;
4928 : }
4929 : return;
4930 : }
4931 1053512 : presult = &result->next;
4932 : }
4933 : }
4934 :
4935 : InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s",
4936 2362 : script->id(), pc - script->code, TypeString(type));
4937 :
4938 2362 : TypeResult *result = cx->new_<TypeResult>(pc - script->code, type);
4939 2362 : if (!result) {
4940 0 : cx->compartment->types.setPendingNukeTypes(cx);
4941 : return;
4942 : }
4943 2362 : result->next = script->types->dynamicList;
4944 2362 : script->types->dynamicList = result;
4945 :
4946 2362 : if (script->hasAnalysis() && script->analysis()->ranInference()) {
4947 852 : TypeSet *pushed = script->analysis()->pushedTypes(pc, 0);
4948 852 : pushed->addType(cx, type);
4949 : }
4950 :
4951 : /* Trigger recompilation of any inline callers. */
4952 2362 : if (script->function() && !script->function()->hasLazyType())
4953 484 : ObjectStateChange(cx, script->function()->type(), false, true);
4954 : }
4955 :
4956 : void
4957 75873907 : TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
4958 : {
4959 : /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
4960 75873907 : if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
4961 0 : return;
4962 :
4963 151747814 : AutoEnterTypeInference enter(cx);
4964 :
4965 75873907 : if (!script->ensureRanAnalysis(cx, NULL)) {
4966 0 : cx->compartment->types.setPendingNukeTypes(cx);
4967 : return;
4968 : }
4969 :
4970 75873907 : Type type = GetValueType(cx, rval);
4971 75873907 : TypeSet *types = script->analysis()->bytecodeTypes(pc);
4972 75873907 : if (types->hasType(type))
4973 : return;
4974 :
4975 : InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s",
4976 365259 : script->id(), pc - script->code, TypeString(type));
4977 365259 : types->addType(cx, type);
4978 : }
4979 :
4980 : bool
4981 153945 : TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
4982 : {
4983 153945 : JS_ASSERT(script->types && !script->types->hasScope());
4984 :
4985 307890 : Root<JSScript*> scriptRoot(cx, &script);
4986 307890 : RootObject scopeRoot(cx, &scope);
4987 :
4988 153945 : JSFunction *fun = script->function();
4989 153945 : bool nullClosure = fun && fun->isNullClosure();
4990 :
4991 153945 : JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction);
4992 153945 : JS_ASSERT_IF(!scope, fun && !script->isInnerFunction);
4993 :
4994 : /*
4995 : * The scope object must be the initial one for the script, before any call
4996 : * object has been created in the heavyweight case.
4997 : */
4998 224864 : JS_ASSERT_IF(scope && scope->isCall() && !scope->asCall().isForEval(),
4999 378809 : scope->asCall().getCalleeFunction() != fun);
5000 :
5001 153945 : if (!script->compileAndGo) {
5002 7907 : script->types->global = NULL;
5003 7907 : return true;
5004 : }
5005 :
5006 146038 : JS_ASSERT_IF(fun && scope, fun->global() == scope->global());
5007 146038 : script->types->global = fun ? &fun->global() : &scope->global();
5008 :
5009 : /*
5010 : * Update the parent in the script's bindings. The bindings are created
5011 : * with a NULL parent, and fixing the parent now avoids the need to reshape
5012 : * every time a call object is created from the bindings.
5013 : */
5014 146038 : if (!script->bindings.setParent(cx, script->types->global))
5015 0 : return false;
5016 :
5017 146038 : if (!cx->typeInferenceEnabled())
5018 65155 : return true;
5019 :
5020 80883 : if (!script->isInnerFunction || nullClosure) {
5021 : /*
5022 : * Outermost functions need nesting information if there are inner
5023 : * functions directly nested in them.
5024 : */
5025 77908 : if (script->isOuterFunction) {
5026 1840 : script->types->nesting = cx->new_<TypeScriptNesting>();
5027 1840 : if (!script->types->nesting)
5028 0 : return false;
5029 : }
5030 77908 : return true;
5031 : }
5032 :
5033 : /*
5034 : * Walk the scope chain to the next call object, which will be the function
5035 : * the script is nested inside.
5036 : */
5037 6070 : while (!scope->isCall())
5038 120 : scope = &scope->asScope().enclosingScope();
5039 :
5040 2975 : CallObject &call = scope->asCall();
5041 :
5042 : /* The isInnerFunction test ensures there is no intervening strict eval call object. */
5043 2975 : JS_ASSERT(!call.isForEval());
5044 :
5045 : /* Don't track non-heavyweight parents, NAME ops won't reach into them. */
5046 2975 : JSFunction *parentFun = call.getCalleeFunction();
5047 2975 : if (!parentFun || !parentFun->isHeavyweight())
5048 0 : return true;
5049 2975 : JSScript *parent = parentFun->script();
5050 2975 : JS_ASSERT(parent->isOuterFunction);
5051 :
5052 : /*
5053 : * We only need the nesting in the child if it has NAME accesses going
5054 : * into the parent. We won't know for sure whether this is the case until
5055 : * analyzing the script's types, which we don't want to do yet. The nesting
5056 : * info we make here may get pruned if/when we eventually do such analysis.
5057 : */
5058 :
5059 : /*
5060 : * Scopes are set when scripts first execute, and the parent script must
5061 : * have executed first. It is still possible for the parent script to not
5062 : * have a scope, however, as we occasionally purge all TypeScripts from the
5063 : * compartment and there may be inner function objects parented to an
5064 : * activation of the outer function sticking around. In such cases, treat
5065 : * the parent's call object as the most recent one, so that it is not
5066 : * marked as reentrant.
5067 : */
5068 2975 : if (!parent->ensureHasTypes(cx))
5069 0 : return false;
5070 2975 : if (!parent->types->hasScope()) {
5071 0 : if (!SetScope(cx, parent, &call.enclosingScope()))
5072 0 : return false;
5073 0 : parent->nesting()->activeCall = &call;
5074 0 : parent->nesting()->argArray = Valueify(call.argArray());
5075 0 : parent->nesting()->varArray = Valueify(call.varArray());
5076 : }
5077 :
5078 2975 : JS_ASSERT(!script->types->nesting);
5079 :
5080 : /* Construct and link nesting information for the two functions. */
5081 :
5082 2975 : script->types->nesting = cx->new_<TypeScriptNesting>();
5083 2975 : if (!script->types->nesting)
5084 0 : return false;
5085 :
5086 2975 : script->nesting()->parent = parent;
5087 2975 : script->nesting()->next = parent->nesting()->children;
5088 2975 : parent->nesting()->children = script;
5089 :
5090 2975 : return true;
5091 : }
5092 :
5093 4815 : TypeScriptNesting::~TypeScriptNesting()
5094 : {
5095 : /*
5096 : * Unlink from any parent/child. Nesting info on a script does not keep
5097 : * either the parent or children live during GC.
5098 : */
5099 :
5100 4815 : if (parent) {
5101 2717 : JSScript **pscript = &parent->nesting()->children;
5102 6627 : while ((*pscript)->nesting() != this)
5103 1193 : pscript = &(*pscript)->nesting()->next;
5104 2717 : *pscript = next;
5105 : }
5106 :
5107 9720 : while (children) {
5108 90 : TypeScriptNesting *child = children->nesting();
5109 90 : children = child->next;
5110 90 : child->parent = NULL;
5111 90 : child->next = NULL;
5112 : }
5113 4815 : }
5114 :
5115 : bool
5116 403989 : ClearActiveNesting(JSScript *start)
5117 : {
5118 : /*
5119 : * Clear active call information for script and any outer functions
5120 : * inner to it. Return false if an inner function has frames on the stack.
5121 : */
5122 :
5123 : /* Traverse children, then parent, avoiding recursion. */
5124 403989 : JSScript *script = start;
5125 403989 : bool traverseChildren = true;
5126 379033 : while (true) {
5127 783022 : TypeScriptNesting *nesting = script->nesting();
5128 783022 : if (nesting->children && traverseChildren) {
5129 208049 : script = nesting->children;
5130 208049 : continue;
5131 : }
5132 574973 : if (nesting->activeFrames)
5133 210522 : return false;
5134 364451 : if (script->isOuterFunction) {
5135 304267 : nesting->activeCall = NULL;
5136 304267 : nesting->argArray = NULL;
5137 304267 : nesting->varArray = NULL;
5138 : }
5139 364451 : if (script == start)
5140 : break;
5141 170984 : if (nesting->next) {
5142 41063 : script = nesting->next;
5143 41063 : traverseChildren = true;
5144 : } else {
5145 129921 : script = nesting->parent;
5146 129921 : traverseChildren = false;
5147 : }
5148 : }
5149 :
5150 193467 : return true;
5151 : }
5152 :
5153 : /*
5154 : * For the specified scope and script with an outer function, check if the
5155 : * scope represents a reentrant activation on an inner function of the parent
5156 : * or any of its transitive parents.
5157 : */
5158 : static void
5159 477967 : CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
5160 : {
5161 : restart:
5162 477967 : JSScript *parent = script->nesting()->parent;
5163 477967 : JS_ASSERT(parent);
5164 :
5165 1003702 : while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent)
5166 47768 : scope = &scope->asScope().enclosingScope();
5167 :
5168 477967 : if (scope != parent->nesting()->activeCall) {
5169 231058 : parent->reentrantOuterFunction = true;
5170 231058 : MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION);
5171 :
5172 : /*
5173 : * Continue checking parents to see if this is reentrant for them too.
5174 : * We don't need to check this in for non-reentrant calls on the outer
5175 : * function: when we entered any outer function to the immediate parent
5176 : * we cleared the active call for its transitive children, so a
5177 : * non-reentrant call on a child is also a non-reentrant call on the
5178 : * parent.
5179 : */
5180 231058 : if (parent->nesting()->parent) {
5181 39945 : scope = &scope->asScope().enclosingScope();
5182 39945 : script = parent;
5183 39945 : goto restart;
5184 : }
5185 : }
5186 438022 : }
5187 :
5188 : void
5189 796707 : NestingPrologue(JSContext *cx, StackFrame *fp)
5190 : {
5191 796707 : JSScript *script = fp->fun()->script();
5192 796707 : TypeScriptNesting *nesting = script->nesting();
5193 :
5194 796707 : if (nesting->parent)
5195 438022 : CheckNestingParent(cx, &fp->scopeChain(), script);
5196 :
5197 796707 : if (script->isOuterFunction) {
5198 : /*
5199 : * Check the stack has no frames for this activation, any of its inner
5200 : * functions or any of their transitive inner functions.
5201 : */
5202 403989 : if (!ClearActiveNesting(script)) {
5203 210522 : script->reentrantOuterFunction = true;
5204 210522 : MarkTypeObjectFlags(cx, fp->fun(), OBJECT_FLAG_REENTRANT_FUNCTION);
5205 : }
5206 :
5207 403989 : nesting->activeCall = &fp->callObj();
5208 403989 : nesting->argArray = fp->formalArgs();
5209 403989 : nesting->varArray = fp->slots();
5210 : }
5211 :
5212 : /* Maintain stack frame count for the function. */
5213 796707 : nesting->activeFrames++;
5214 796707 : }
5215 :
5216 : void
5217 681430 : NestingEpilogue(StackFrame *fp)
5218 : {
5219 681430 : JSScript *script = fp->fun()->script();
5220 681430 : TypeScriptNesting *nesting = script->nesting();
5221 :
5222 681430 : JS_ASSERT(nesting->activeFrames != 0);
5223 681430 : nesting->activeFrames--;
5224 681430 : }
5225 :
5226 : } } /* namespace js::types */
5227 :
5228 : /////////////////////////////////////////////////////////////////////
5229 : // TypeScript
5230 : /////////////////////////////////////////////////////////////////////
5231 :
5232 : /*
5233 : * Returns true if we don't expect to compute the correct types for some value
5234 : * pushed by the specified bytecode.
5235 : */
5236 : static inline bool
5237 9300901 : IgnorePushed(const jsbytecode *pc, unsigned index)
5238 : {
5239 9300901 : switch (JSOp(*pc)) {
5240 : /* We keep track of the scopes pushed by BINDNAME separately. */
5241 : case JSOP_BINDNAME:
5242 : case JSOP_BINDGNAME:
5243 : case JSOP_BINDXMLNAME:
5244 202447 : return true;
5245 :
5246 : /* Stack not consistent in TRY_BRANCH_AFTER_COND. */
5247 : case JSOP_IN:
5248 : case JSOP_EQ:
5249 : case JSOP_NE:
5250 : case JSOP_LT:
5251 : case JSOP_LE:
5252 : case JSOP_GT:
5253 : case JSOP_GE:
5254 4954 : return (index == 0);
5255 :
5256 : /* Value not determining result is not pushed by OR/AND. */
5257 : case JSOP_OR:
5258 : case JSOP_AND:
5259 5288 : return (index == 0);
5260 :
5261 : /* Holes tracked separately. */
5262 : case JSOP_HOLE:
5263 888 : return (index == 0);
5264 : case JSOP_FILTER:
5265 0 : return (index == 1);
5266 :
5267 : /* Storage for 'with' and 'let' blocks not monitored. */
5268 : case JSOP_ENTERWITH:
5269 : case JSOP_ENTERBLOCK:
5270 : case JSOP_ENTERLET0:
5271 : case JSOP_ENTERLET1:
5272 37687 : return true;
5273 :
5274 : /* We don't keep track of the iteration state for 'for in' or 'for each in' loops. */
5275 : case JSOP_ITER:
5276 : case JSOP_ITERNEXT:
5277 : case JSOP_MOREITER:
5278 : case JSOP_ENDITER:
5279 39265 : return true;
5280 :
5281 : /* Ops which can manipulate values pushed by opcodes we don't model. */
5282 : case JSOP_DUP:
5283 : case JSOP_DUP2:
5284 : case JSOP_SWAP:
5285 : case JSOP_PICK:
5286 723438 : return true;
5287 :
5288 : /* We don't keep track of state indicating whether there is a pending exception. */
5289 : case JSOP_FINALLY:
5290 604 : return true;
5291 :
5292 : /*
5293 : * We don't treat GETLOCAL immediately followed by a pop as a use-before-def,
5294 : * and while the type will have been inferred correctly the method JIT
5295 : * may not have written the local's initial undefined value to the stack,
5296 : * leaving a stale value.
5297 : */
5298 : case JSOP_GETLOCAL:
5299 544307 : return JSOp(pc[JSOP_GETLOCAL_LENGTH]) == JSOP_POP;
5300 :
5301 : default:
5302 7742023 : return false;
5303 : }
5304 : }
5305 :
5306 : bool
5307 154005 : JSScript::makeTypes(JSContext *cx)
5308 : {
5309 154005 : JS_ASSERT(!types);
5310 :
5311 154005 : if (!cx->typeInferenceEnabled()) {
5312 68692 : types = (TypeScript *) cx->calloc_(sizeof(TypeScript));
5313 68692 : if (!types)
5314 0 : return false;
5315 68692 : new(types) TypeScript();
5316 68692 : return true;
5317 : }
5318 :
5319 170626 : AutoEnterTypeInference enter(cx);
5320 :
5321 85313 : unsigned count = TypeScript::NumTypeSets(this);
5322 :
5323 85313 : types = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(TypeSet) * count));
5324 85313 : if (!types) {
5325 0 : cx->compartment->types.setPendingNukeTypes(cx);
5326 0 : return false;
5327 : }
5328 :
5329 85313 : new(types) TypeScript();
5330 :
5331 : #ifdef DEBUG
5332 85313 : TypeSet *typeArray = types->typeArray();
5333 1288779 : for (unsigned i = 0; i < nTypeSets; i++)
5334 : InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
5335 : InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
5336 1203466 : i, id());
5337 85313 : TypeSet *returnTypes = TypeScript::ReturnTypes(this);
5338 : InferSpew(ISpewOps, "typeSet: %sT%p%s return #%u",
5339 : InferSpewColor(returnTypes), returnTypes, InferSpewColorReset(),
5340 85313 : id());
5341 85313 : TypeSet *thisTypes = TypeScript::ThisTypes(this);
5342 : InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
5343 : InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
5344 85313 : id());
5345 85313 : unsigned nargs = function() ? function()->nargs : 0;
5346 193887 : for (unsigned i = 0; i < nargs; i++) {
5347 108574 : TypeSet *types = TypeScript::ArgTypes(this, i);
5348 : InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u",
5349 : InferSpewColor(types), types, InferSpewColorReset(),
5350 108574 : i, id());
5351 : }
5352 151201 : for (unsigned i = 0; i < nfixed; i++) {
5353 65888 : TypeSet *types = TypeScript::LocalTypes(this, i);
5354 : InferSpew(ISpewOps, "typeSet: %sT%p%s local%u #%u",
5355 : InferSpewColor(types), types, InferSpewColorReset(),
5356 65888 : i, id());
5357 : }
5358 : #endif
5359 :
5360 85313 : return true;
5361 : }
5362 :
5363 : bool
5364 166090 : JSScript::makeAnalysis(JSContext *cx)
5365 : {
5366 166090 : JS_ASSERT(types && !types->analysis);
5367 :
5368 332180 : AutoEnterAnalysis enter(cx);
5369 :
5370 166090 : types->analysis = cx->typeLifoAlloc().new_<ScriptAnalysis>(this);
5371 :
5372 166090 : if (!types->analysis)
5373 0 : return false;
5374 :
5375 166090 : types->analysis->analyzeBytecode(cx);
5376 :
5377 166090 : if (types->analysis->OOM()) {
5378 0 : types->analysis = NULL;
5379 0 : return false;
5380 : }
5381 :
5382 166090 : return true;
5383 : }
5384 :
5385 : bool
5386 166934 : JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton)
5387 : {
5388 166934 : function_ = fun;
5389 :
5390 166934 : if (!cx->typeInferenceEnabled())
5391 74319 : return true;
5392 :
5393 92615 : if (singleton) {
5394 78590 : if (!fun->setSingletonType(cx))
5395 0 : return false;
5396 : } else {
5397 : TypeObject *type = cx->compartment->types.newTypeObject(cx, this,
5398 14025 : JSProto_Function, fun->getProto());
5399 14025 : if (!type)
5400 0 : return false;
5401 :
5402 14025 : fun->setType(type);
5403 14025 : type->interpretedFunction = fun;
5404 : }
5405 :
5406 92615 : return true;
5407 : }
5408 :
5409 : #ifdef DEBUG
5410 :
5411 : /* static */ void
5412 859862289 : TypeScript::CheckBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value *sp)
5413 : {
5414 1719724578 : AutoEnterTypeInference enter(cx);
5415 :
5416 859862289 : if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
5417 : return;
5418 :
5419 857836691 : if (!script->hasAnalysis() || !script->analysis()->ranInference())
5420 : return;
5421 11064760 : ScriptAnalysis *analysis = script->analysis();
5422 :
5423 11064760 : int defCount = GetDefCount(script, pc - script->code);
5424 :
5425 20365661 : for (int i = 0; i < defCount; i++) {
5426 9300901 : const js::Value &val = sp[-defCount + i];
5427 9300901 : TypeSet *types = analysis->pushedTypes(pc, i);
5428 9300901 : if (IgnorePushed(pc, i))
5429 1081134 : continue;
5430 :
5431 8219767 : Type type = GetValueType(cx, val);
5432 :
5433 8219767 : if (!types->hasType(type)) {
5434 : /* Display fine-grained debug information first */
5435 : fprintf(stderr, "Missing type at #%u:%05u pushed %u: %s\n",
5436 0 : script->id(), unsigned(pc - script->code), i, TypeString(type));
5437 0 : TypeFailure(cx, "Missing type pushed %u: %s", i, TypeString(type));
5438 : }
5439 : }
5440 : }
5441 :
5442 : #endif
5443 :
5444 : /////////////////////////////////////////////////////////////////////
5445 : // JSObject
5446 : /////////////////////////////////////////////////////////////////////
5447 :
5448 : bool
5449 23629 : JSObject::shouldSplicePrototype(JSContext *cx)
5450 : {
5451 : /*
5452 : * During bootstrapping, if inference is enabled we need to make sure not
5453 : * to splice a new prototype in for Function.prototype or the global
5454 : * object if their __proto__ had previously been set to null, as this
5455 : * will change the prototype for all other objects with the same type.
5456 : * If inference is disabled we cannot determine from the object whether it
5457 : * has had its __proto__ set after creation.
5458 : */
5459 23629 : if (getProto() != NULL)
5460 0 : return false;
5461 23629 : return !cx->typeInferenceEnabled() || hasSingletonType();
5462 : }
5463 :
5464 : bool
5465 36859 : JSObject::splicePrototype(JSContext *cx, JSObject *proto)
5466 : {
5467 : /*
5468 : * For singleton types representing only a single JSObject, the proto
5469 : * can be rearranged as needed without destroying type information for
5470 : * the old or new types. Note that type constraints propagating properties
5471 : * from the old prototype are not removed.
5472 : */
5473 36859 : JS_ASSERT_IF(cx->typeInferenceEnabled(), hasSingletonType());
5474 :
5475 : /* Inner objects may not appear on prototype chains. */
5476 36859 : JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject);
5477 :
5478 : /*
5479 : * Force type instantiation when splicing lazy types. This may fail,
5480 : * in which case inference will be disabled for the compartment.
5481 : */
5482 36859 : TypeObject *type = getType(cx);
5483 36859 : TypeObject *protoType = NULL;
5484 36859 : if (proto) {
5485 36754 : protoType = proto->getType(cx);
5486 36754 : if (!proto->getNewType(cx))
5487 0 : return false;
5488 : }
5489 :
5490 36859 : if (!cx->typeInferenceEnabled()) {
5491 10604 : TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx);
5492 10604 : if (!type)
5493 0 : return false;
5494 10604 : type_ = type;
5495 10604 : return true;
5496 : }
5497 :
5498 26255 : type->proto = proto;
5499 :
5500 52510 : AutoEnterTypeInference enter(cx);
5501 :
5502 26255 : if (protoType && protoType->unknownProperties() && !type->unknownProperties()) {
5503 12935 : type->markUnknown(cx);
5504 12935 : return true;
5505 : }
5506 :
5507 13320 : if (!type->unknownProperties()) {
5508 : /* Update properties on this type with any shared with the prototype. */
5509 13215 : unsigned count = type->getPropertyCount();
5510 13602 : for (unsigned i = 0; i < count; i++) {
5511 387 : Property *prop = type->getProperty(i);
5512 387 : if (prop && prop->types.hasPropagatedProperty())
5513 56 : type->getFromPrototypes(cx, prop->id, &prop->types, true);
5514 : }
5515 : }
5516 :
5517 13320 : return true;
5518 : }
5519 :
5520 : void
5521 51868 : JSObject::makeLazyType(JSContext *cx)
5522 : {
5523 51868 : JS_ASSERT(cx->typeInferenceEnabled() && hasLazyType());
5524 103736 : AutoEnterTypeInference enter(cx);
5525 :
5526 : TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
5527 51868 : JSProto_Object, getProto());
5528 51868 : if (!type) {
5529 0 : cx->compartment->types.setPendingNukeTypes(cx);
5530 : return;
5531 : }
5532 :
5533 : /* Fill in the type according to the state of this object. */
5534 :
5535 51868 : type->singleton = this;
5536 :
5537 51868 : if (isFunction() && toFunction()->isInterpreted()) {
5538 6982 : type->interpretedFunction = toFunction();
5539 6982 : JSScript *script = type->interpretedFunction->script();
5540 6982 : if (script->uninlineable)
5541 240 : type->flags |= OBJECT_FLAG_UNINLINEABLE;
5542 6982 : if (script->reentrantOuterFunction)
5543 8 : type->flags |= OBJECT_FLAG_REENTRANT_FUNCTION;
5544 : }
5545 :
5546 51868 : if (lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
5547 8 : type->flags |= OBJECT_FLAG_ITERATED;
5548 :
5549 : #if JS_HAS_XML_SUPPORT
5550 : /*
5551 : * XML objects do not have equality hooks but are treated special by EQ/NE
5552 : * ops. Just mark the type as totally unknown.
5553 : */
5554 51868 : if (isXML() && !type->unknownProperties())
5555 0 : type->markUnknown(cx);
5556 : #endif
5557 :
5558 51868 : if (getClass()->ext.equality)
5559 2 : type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
5560 :
5561 51868 : if (type->unknownProperties()) {
5562 0 : type_ = type;
5563 : return;
5564 : }
5565 :
5566 : /* Not yet generating singleton arrays. */
5567 : type->flags |= OBJECT_FLAG_NON_DENSE_ARRAY
5568 : | OBJECT_FLAG_NON_PACKED_ARRAY
5569 51868 : | OBJECT_FLAG_NON_TYPED_ARRAY;
5570 :
5571 51868 : type_ = type;
5572 : }
5573 :
5574 : /* static */ inline HashNumber
5575 20504073 : TypeObjectEntry::hash(JSObject *proto)
5576 : {
5577 20504073 : return PointerHasher<JSObject *, 3>::hash(proto);
5578 : }
5579 :
5580 : /* static */ inline bool
5581 20125922 : TypeObjectEntry::match(TypeObject *key, JSObject *lookup)
5582 : {
5583 20125922 : return key->proto == lookup;
5584 : }
5585 :
5586 : #ifdef DEBUG
5587 : bool
5588 2523996 : JSObject::hasNewType(TypeObject *type)
5589 : {
5590 2523996 : TypeObjectSet &table = compartment()->newTypeObjects;
5591 :
5592 2523996 : if (!table.initialized())
5593 0 : return false;
5594 :
5595 2523996 : TypeObjectSet::Ptr p = table.lookup(this);
5596 2523996 : return p && *p == type;
5597 : }
5598 : #endif /* DEBUG */
5599 :
5600 : bool
5601 1763256 : JSObject::setNewTypeUnknown(JSContext *cx)
5602 : {
5603 1763256 : if (!setFlag(cx, js::BaseShape::NEW_TYPE_UNKNOWN))
5604 0 : return false;
5605 :
5606 : /*
5607 : * If the object already has a new type, mark that type as unknown. It will
5608 : * not have the SETS_MARKED_UNKNOWN bit set, so may require a type set
5609 : * crawl if prototypes of the object change dynamically in the future.
5610 : */
5611 1763256 : TypeObjectSet &table = cx->compartment->newTypeObjects;
5612 1763256 : if (table.initialized()) {
5613 1740750 : if (TypeObjectSet::Ptr p = table.lookup(this))
5614 1606080 : MarkTypeObjectUnknownProperties(cx, *p);
5615 : }
5616 :
5617 1763256 : return true;
5618 : }
5619 :
5620 : TypeObject *
5621 12158239 : JSObject::getNewType(JSContext *cx, JSFunction *fun)
5622 : {
5623 12158239 : TypeObjectSet &table = cx->compartment->newTypeObjects;
5624 :
5625 12158239 : if (!table.initialized() && !table.init())
5626 0 : return NULL;
5627 :
5628 24316478 : TypeObjectSet::AddPtr p = table.lookupForAdd(this);
5629 12158239 : if (p) {
5630 11956043 : TypeObject *type = *p;
5631 :
5632 : /*
5633 : * If set, the type's newScript indicates the script used to create
5634 : * all objects in existence which have this type. If there are objects
5635 : * in existence which are not created by calling 'new' on newScript,
5636 : * we must clear the new script information from the type and will not
5637 : * be able to assume any definite properties for instances of the type.
5638 : * This case is rare, but can happen if, for example, two scripted
5639 : * functions have the same value for their 'prototype' property, or if
5640 : * Object.create is called with a prototype object that is also the
5641 : * 'prototype' property of some scripted function.
5642 : */
5643 11956043 : if (type->newScript && type->newScript->fun != fun)
5644 10 : type->clearNewScript(cx);
5645 :
5646 11956043 : return type;
5647 : }
5648 :
5649 404392 : RootedVarObject self(cx, this);
5650 :
5651 202196 : if (!setDelegate(cx))
5652 0 : return NULL;
5653 :
5654 202196 : bool markUnknown = self->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN);
5655 :
5656 : TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
5657 202196 : JSProto_Object, self, markUnknown);
5658 202196 : if (!type)
5659 0 : return NULL;
5660 :
5661 202196 : if (!table.relookupOrAdd(p, self, type))
5662 0 : return NULL;
5663 :
5664 202196 : if (!cx->typeInferenceEnabled())
5665 89183 : return type;
5666 :
5667 226026 : AutoEnterTypeInference enter(cx);
5668 :
5669 : /*
5670 : * Set the special equality flag for types whose prototype also has the
5671 : * flag set. This is a hack, :XXX: need a real correspondence between
5672 : * types and the possible js::Class of objects with that type.
5673 : */
5674 113013 : if (self->hasSpecialEquality())
5675 405 : type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
5676 :
5677 113013 : if (fun)
5678 1471 : CheckNewScriptProperties(cx, type, fun);
5679 :
5680 : #if JS_HAS_XML_SUPPORT
5681 : /* Special case for XML object equality, see makeLazyType(). */
5682 113013 : if (self->isXML() && !type->unknownProperties())
5683 215 : type->flags |= OBJECT_FLAG_UNKNOWN_MASK;
5684 : #endif
5685 :
5686 113013 : if (self->getClass()->ext.equality)
5687 405 : type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
5688 :
5689 : /*
5690 : * The new type is not present in any type sets, so mark the object as
5691 : * unknown in all type sets it appears in. This allows the prototype of
5692 : * such objects to mutate freely without triggering an expensive walk of
5693 : * the compartment's type sets. (While scripts normally don't mutate
5694 : * __proto__, the browser will for proxies and such, and we need to
5695 : * accommodate this behavior).
5696 : */
5697 113013 : if (type->unknownProperties())
5698 84559 : type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
5699 :
5700 113013 : return type;
5701 : }
5702 :
5703 : TypeObject *
5704 4081088 : JSCompartment::getLazyType(JSContext *cx, JSObject *proto)
5705 : {
5706 4081088 : gc::MaybeCheckStackRoots(cx);
5707 :
5708 4081088 : TypeObjectSet &table = cx->compartment->lazyTypeObjects;
5709 :
5710 4081088 : if (!table.initialized() && !table.init())
5711 0 : return NULL;
5712 :
5713 8162176 : TypeObjectSet::AddPtr p = table.lookupForAdd(proto);
5714 4081088 : if (p) {
5715 4039803 : TypeObject *type = *p;
5716 4039803 : JS_ASSERT(type->lazy());
5717 :
5718 4039803 : return type;
5719 : }
5720 :
5721 : TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
5722 41285 : JSProto_Object, proto, false);
5723 41285 : if (!type)
5724 0 : return NULL;
5725 :
5726 41285 : if (!table.relookupOrAdd(p, proto, type))
5727 0 : return NULL;
5728 :
5729 41285 : type->singleton = (JSObject *) TypeObject::LAZY_SINGLETON;
5730 :
5731 41285 : return type;
5732 : }
5733 :
5734 : /////////////////////////////////////////////////////////////////////
5735 : // Tracing
5736 : /////////////////////////////////////////////////////////////////////
5737 :
5738 : void
5739 1777230 : TypeSet::sweep(JSCompartment *compartment)
5740 : {
5741 : /*
5742 : * Purge references to type objects that are no longer live. Type sets hold
5743 : * only weak references. For type sets containing more than one object,
5744 : * live entries in the object hash need to be copied to the compartment's
5745 : * new arena.
5746 : */
5747 1777230 : unsigned objectCount = baseObjectCount();
5748 1777230 : if (objectCount >= 2) {
5749 10426 : unsigned oldCapacity = HashSetCapacity(objectCount);
5750 10426 : TypeObjectKey **oldArray = objectSet;
5751 :
5752 10426 : clearObjects();
5753 10426 : objectCount = 0;
5754 979810 : for (unsigned i = 0; i < oldCapacity; i++) {
5755 969384 : TypeObjectKey *object = oldArray[i];
5756 969384 : if (object && !IsAboutToBeFinalized(object)) {
5757 : TypeObjectKey **pentry =
5758 : HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
5759 305909 : (compartment, objectSet, objectCount, object);
5760 305909 : if (pentry)
5761 305909 : *pentry = object;
5762 : else
5763 0 : compartment->types.setPendingNukeTypesNoReport();
5764 : }
5765 : }
5766 10426 : setBaseObjectCount(objectCount);
5767 1766804 : } else if (objectCount == 1) {
5768 344521 : TypeObjectKey *object = (TypeObjectKey *) objectSet;
5769 344521 : if (IsAboutToBeFinalized(object)) {
5770 260791 : objectSet = NULL;
5771 260791 : setBaseObjectCount(0);
5772 : }
5773 : }
5774 :
5775 : /*
5776 : * All constraints are wiped out on each GC, including those propagating
5777 : * into this type set from prototype properties.
5778 : */
5779 1777230 : constraintList = NULL;
5780 1777230 : flags &= ~TYPE_FLAG_PROPAGATED_PROPERTY;
5781 1777230 : }
5782 :
5783 : inline void
5784 204444 : TypeObject::clearProperties()
5785 : {
5786 204444 : setBasePropertyCount(0);
5787 204444 : propertySet = NULL;
5788 204444 : }
5789 :
5790 : /*
5791 : * Before sweeping the arenas themselves, scan all type objects in a
5792 : * compartment to fixup weak references: property type sets referencing dead
5793 : * JS and type objects, and singleton JS objects whose type is not referenced
5794 : * elsewhere. This also releases memory associated with dead type objects,
5795 : * so that type objects do not need later finalization.
5796 : */
5797 : inline void
5798 767899 : TypeObject::sweep(FreeOp *fop)
5799 : {
5800 : /*
5801 : * We may be regenerating existing type sets containing this object,
5802 : * so reset contributions on each GC to avoid tripping the limit.
5803 : */
5804 767899 : contribution = 0;
5805 :
5806 767899 : if (singleton) {
5807 202668 : JS_ASSERT(!newScript);
5808 :
5809 : /*
5810 : * All properties can be discarded. We will regenerate them as needed
5811 : * as code gets reanalyzed.
5812 : */
5813 202668 : clearProperties();
5814 :
5815 202668 : return;
5816 : }
5817 :
5818 565231 : if (!isMarked()) {
5819 252985 : if (newScript)
5820 539 : fop->free_(newScript);
5821 252985 : return;
5822 : }
5823 :
5824 312246 : JSCompartment *compartment = this->compartment();
5825 :
5826 : /*
5827 : * Properties were allocated from the old arena, and need to be copied over
5828 : * to the new one. Don't hang onto properties without the OWN_PROPERTY
5829 : * flag; these were never directly assigned, and get any possible values
5830 : * from the object's prototype.
5831 : */
5832 312246 : unsigned propertyCount = basePropertyCount();
5833 312246 : if (propertyCount >= 2) {
5834 1776 : unsigned oldCapacity = HashSetCapacity(propertyCount);
5835 1776 : Property **oldArray = propertySet;
5836 :
5837 1776 : clearProperties();
5838 1776 : propertyCount = 0;
5839 15984 : for (unsigned i = 0; i < oldCapacity; i++) {
5840 14208 : Property *prop = oldArray[i];
5841 14208 : if (prop && prop->types.isOwnProperty(false)) {
5842 8176 : Property *newProp = compartment->typeLifoAlloc.new_<Property>(*prop);
5843 8176 : if (newProp) {
5844 : Property **pentry =
5845 : HashSetInsert<jsid,Property,Property>
5846 8176 : (compartment, propertySet, propertyCount, prop->id);
5847 8176 : if (pentry) {
5848 8176 : *pentry = newProp;
5849 8176 : newProp->types.sweep(compartment);
5850 : } else {
5851 0 : compartment->types.setPendingNukeTypesNoReport();
5852 : }
5853 : } else {
5854 0 : compartment->types.setPendingNukeTypesNoReport();
5855 : }
5856 : }
5857 : }
5858 1776 : setBasePropertyCount(propertyCount);
5859 310470 : } else if (propertyCount == 1) {
5860 2709 : Property *prop = (Property *) propertySet;
5861 2709 : if (prop->types.isOwnProperty(false)) {
5862 2591 : Property *newProp = compartment->typeLifoAlloc.new_<Property>(*prop);
5863 2591 : if (newProp) {
5864 2591 : propertySet = (Property **) newProp;
5865 2591 : newProp->types.sweep(compartment);
5866 : } else {
5867 0 : compartment->types.setPendingNukeTypesNoReport();
5868 : }
5869 : } else {
5870 118 : propertySet = NULL;
5871 118 : setBasePropertyCount(0);
5872 : }
5873 : }
5874 :
5875 312246 : if (basePropertyCount() <= SET_ARRAY_SIZE) {
5876 323013 : for (unsigned i = 0; i < basePropertyCount(); i++)
5877 10767 : JS_ASSERT(propertySet[i]);
5878 : }
5879 :
5880 : /*
5881 : * The GC will clear out the constraints ensuring the correctness of the
5882 : * newScript information, these constraints will need to be regenerated
5883 : * the next time we compile code which depends on this info.
5884 : */
5885 312246 : if (newScript)
5886 5 : flags |= OBJECT_FLAG_NEW_SCRIPT_REGENERATE;
5887 : }
5888 :
5889 : struct SweepTypeObjectOp
5890 : {
5891 : FreeOp *fop;
5892 83677 : SweepTypeObjectOp(FreeOp *fop) : fop(fop) {}
5893 767899 : void operator()(gc::Cell *cell) {
5894 767899 : TypeObject *object = static_cast<TypeObject *>(cell);
5895 767899 : object->sweep(fop);
5896 767899 : }
5897 : };
5898 :
5899 : void
5900 83677 : SweepTypeObjects(FreeOp *fop, JSCompartment *compartment)
5901 : {
5902 83677 : SweepTypeObjectOp op(fop);
5903 83677 : gc::ForEachArenaAndCell(compartment, gc::FINALIZE_TYPE_OBJECT, gc::EmptyArenaOp, op);
5904 83677 : }
5905 :
5906 : void
5907 83677 : TypeCompartment::sweep(FreeOp *fop)
5908 : {
5909 83677 : JSCompartment *compartment = this->compartment();
5910 :
5911 83677 : SweepTypeObjects(fop, compartment);
5912 :
5913 : /*
5914 : * Iterate through the array/object type tables and remove all entries
5915 : * referencing collected data. These tables only hold weak references.
5916 : */
5917 :
5918 83677 : if (arrayTypeTable) {
5919 3441 : for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
5920 1759 : const ArrayTableKey &key = e.front().key;
5921 1759 : TypeObject *obj = e.front().value;
5922 1759 : JS_ASSERT(obj->proto == key.proto);
5923 1759 : JS_ASSERT(!key.type.isSingleObject());
5924 :
5925 1759 : bool remove = false;
5926 1759 : if (key.type.isTypeObject() && !key.type.typeObject()->isMarked())
5927 50 : remove = true;
5928 1759 : if (!obj->isMarked())
5929 830 : remove = true;
5930 :
5931 1759 : if (remove)
5932 830 : e.removeFront();
5933 : }
5934 : }
5935 :
5936 83677 : if (objectTypeTable) {
5937 1469 : for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
5938 872 : const ObjectTableKey &key = e.front().key;
5939 872 : const ObjectTableEntry &entry = e.front().value;
5940 872 : JS_ASSERT(entry.object->proto == key.proto);
5941 :
5942 872 : bool remove = false;
5943 872 : if (!entry.object->isMarked())
5944 825 : remove = true;
5945 941 : for (unsigned i = 0; !remove && i < key.nslots; i++) {
5946 69 : if (JSID_IS_STRING(key.ids[i])) {
5947 69 : JSString *str = JSID_TO_STRING(key.ids[i]);
5948 69 : if (!str->isMarked())
5949 0 : remove = true;
5950 : }
5951 69 : JS_ASSERT(!entry.types[i].isSingleObject());
5952 69 : if (entry.types[i].isTypeObject() && !entry.types[i].typeObject()->isMarked())
5953 0 : remove = true;
5954 : }
5955 :
5956 872 : if (remove) {
5957 825 : Foreground::free_(key.ids);
5958 825 : Foreground::free_(entry.types);
5959 825 : e.removeFront();
5960 : }
5961 : }
5962 : }
5963 :
5964 83677 : if (allocationSiteTable) {
5965 21338 : for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
5966 15487 : const AllocationSiteKey &key = e.front().key;
5967 15487 : TypeObject *object = e.front().value;
5968 :
5969 15487 : if (IsAboutToBeFinalized(key.script) || !object->isMarked())
5970 12585 : e.removeFront();
5971 : }
5972 : }
5973 :
5974 : /*
5975 : * The pending array is reset on GC, it can grow large (75+ KB) and is easy
5976 : * to reallocate if the compartment becomes active again.
5977 : */
5978 83677 : if (pendingArray)
5979 7207 : fop->free_(pendingArray);
5980 :
5981 83677 : pendingArray = NULL;
5982 83677 : pendingCapacity = 0;
5983 83677 : }
5984 :
5985 : void
5986 167394 : JSCompartment::sweepNewTypeObjectTable(TypeObjectSet &table)
5987 : {
5988 167394 : if (table.initialized()) {
5989 640348 : for (TypeObjectSet::Enum e(table); !e.empty(); e.popFront()) {
5990 570290 : TypeObject *type = e.front();
5991 570290 : if (!type->isMarked())
5992 243481 : e.removeFront();
5993 : }
5994 : }
5995 167394 : }
5996 :
5997 41285 : TypeCompartment::~TypeCompartment()
5998 : {
5999 41285 : if (pendingArray)
6000 0 : Foreground::free_(pendingArray);
6001 :
6002 41285 : if (arrayTypeTable)
6003 755 : Foreground::delete_(arrayTypeTable);
6004 :
6005 41285 : if (objectTypeTable)
6006 575 : Foreground::delete_(objectTypeTable);
6007 :
6008 41285 : if (allocationSiteTable)
6009 4333 : Foreground::delete_(allocationSiteTable);
6010 41285 : }
6011 :
6012 : /* static */ void
6013 117460 : TypeScript::Sweep(FreeOp *fop, JSScript *script)
6014 : {
6015 117460 : JSCompartment *compartment = script->compartment();
6016 117460 : JS_ASSERT(compartment->types.inferenceEnabled);
6017 :
6018 117460 : unsigned num = NumTypeSets(script);
6019 117460 : TypeSet *typeArray = script->types->typeArray();
6020 :
6021 : /* Remove constraints and references to dead objects from the persistent type sets. */
6022 1883923 : for (unsigned i = 0; i < num; i++)
6023 1766463 : typeArray[i].sweep(compartment);
6024 :
6025 117460 : TypeResult **presult = &script->types->dynamicList;
6026 239195 : while (*presult) {
6027 4275 : TypeResult *result = *presult;
6028 4275 : Type type = result->type;
6029 :
6030 4275 : if (!type.isUnknown() && !type.isAnyObject() && type.isObject() &&
6031 0 : IsAboutToBeFinalized(type.objectKey())) {
6032 0 : *presult = result->next;
6033 0 : fop->delete_(result);
6034 : } else {
6035 4275 : presult = &result->next;
6036 : }
6037 : }
6038 :
6039 : /*
6040 : * If the script has nesting state with a most recent activation, we do not
6041 : * need either to mark the call object or clear it if not live. Even with
6042 : * a dead pointer in the nesting, we can't get a spurious match while
6043 : * testing for reentrancy: if previous activations are still live, they
6044 : * cannot alias the most recent one, and future activations will overwrite
6045 : * activeCall on creation.
6046 : */
6047 117460 : }
6048 :
6049 : void
6050 154005 : TypeScript::destroy()
6051 : {
6052 310827 : while (dynamicList) {
6053 2817 : TypeResult *next = dynamicList->next;
6054 2817 : Foreground::delete_(dynamicList);
6055 2817 : dynamicList = next;
6056 : }
6057 :
6058 154005 : if (nesting)
6059 4655 : Foreground::delete_(nesting);
6060 :
6061 154005 : Foreground::free_(this);
6062 154005 : }
6063 :
6064 : inline size_t
6065 0 : TypeSet::computedSizeOfExcludingThis()
6066 : {
6067 : /*
6068 : * This memory is allocated within the temp pool (but accounted for
6069 : * elsewhere) so we can't use a JSMallocSizeOfFun to measure it. We must
6070 : * compute its size analytically.
6071 : */
6072 0 : uint32_t count = baseObjectCount();
6073 0 : if (count >= 2)
6074 0 : return HashSetCapacity(count) * sizeof(TypeObject *);
6075 0 : return 0;
6076 : }
6077 :
6078 : inline size_t
6079 0 : TypeObject::computedSizeOfExcludingThis()
6080 : {
6081 : /*
6082 : * This memory is allocated within the temp pool (but accounted for
6083 : * elsewhere) so we can't use a JSMallocSizeOfFun to measure it. We must
6084 : * compute its size analytically.
6085 : */
6086 0 : size_t bytes = 0;
6087 :
6088 0 : uint32_t count = basePropertyCount();
6089 0 : if (count >= 2)
6090 0 : bytes += HashSetCapacity(count) * sizeof(TypeObject *);
6091 :
6092 0 : count = getPropertyCount();
6093 0 : for (unsigned i = 0; i < count; i++) {
6094 0 : Property *prop = getProperty(i);
6095 0 : if (prop)
6096 0 : bytes += sizeof(Property) + prop->types.computedSizeOfExcludingThis();
6097 : }
6098 :
6099 0 : return bytes;
6100 : }
6101 :
6102 : static void
6103 0 : SizeOfScriptTypeInferenceData(JSScript *script, TypeInferenceSizes *sizes,
6104 : JSMallocSizeOfFun mallocSizeOf)
6105 : {
6106 0 : TypeScript *typeScript = script->types;
6107 0 : if (!typeScript)
6108 0 : return;
6109 :
6110 : /* If TI is disabled, a single TypeScript is still present. */
6111 0 : if (!script->compartment()->types.inferenceEnabled) {
6112 0 : sizes->scripts += mallocSizeOf(typeScript);
6113 0 : return;
6114 : }
6115 :
6116 0 : sizes->scripts += mallocSizeOf(typeScript->nesting);
6117 :
6118 0 : unsigned count = TypeScript::NumTypeSets(script);
6119 0 : sizes->scripts += mallocSizeOf(typeScript);
6120 :
6121 0 : TypeResult *result = typeScript->dynamicList;
6122 0 : while (result) {
6123 0 : sizes->scripts += mallocSizeOf(result);
6124 0 : result = result->next;
6125 : }
6126 :
6127 : /*
6128 : * This counts memory that is in the temp pool but gets attributed
6129 : * elsewhere. See JS::SizeOfCompartmentTypeInferenceData for more details.
6130 : */
6131 0 : TypeSet *typeArray = typeScript->typeArray();
6132 0 : for (unsigned i = 0; i < count; i++) {
6133 0 : size_t bytes = typeArray[i].computedSizeOfExcludingThis();
6134 0 : sizes->scripts += bytes;
6135 0 : sizes->temporary -= bytes;
6136 : }
6137 : }
6138 :
6139 : void
6140 0 : JSCompartment::sizeOfTypeInferenceData(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf)
6141 : {
6142 : /*
6143 : * Note: not all data in the pool is temporary, and some will survive GCs
6144 : * by being copied to the replacement pool. This memory will be counted
6145 : * elsewhere and deducted from the amount of temporary data.
6146 : */
6147 0 : sizes->temporary += typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
6148 :
6149 : /* Pending arrays are cleared on GC along with the analysis pool. */
6150 0 : sizes->temporary += mallocSizeOf(types.pendingArray);
6151 :
6152 : /* TypeCompartment::pendingRecompiles is non-NULL only while inference code is running. */
6153 0 : JS_ASSERT(!types.pendingRecompiles);
6154 :
6155 0 : for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next())
6156 0 : SizeOfScriptTypeInferenceData(i.get<JSScript>(), sizes, mallocSizeOf);
6157 :
6158 0 : if (types.allocationSiteTable)
6159 0 : sizes->tables += types.allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
6160 :
6161 0 : if (types.arrayTypeTable)
6162 0 : sizes->tables += types.arrayTypeTable->sizeOfIncludingThis(mallocSizeOf);
6163 :
6164 0 : if (types.objectTypeTable) {
6165 0 : sizes->tables += types.objectTypeTable->sizeOfIncludingThis(mallocSizeOf);
6166 :
6167 0 : for (ObjectTypeTable::Enum e(*types.objectTypeTable);
6168 0 : !e.empty();
6169 0 : e.popFront())
6170 : {
6171 0 : const ObjectTableKey &key = e.front().key;
6172 0 : const ObjectTableEntry &value = e.front().value;
6173 :
6174 : /* key.ids and values.types have the same length. */
6175 0 : sizes->tables += mallocSizeOf(key.ids) + mallocSizeOf(value.types);
6176 : }
6177 : }
6178 0 : }
6179 :
6180 : void
6181 0 : TypeObject::sizeOfExcludingThis(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf)
6182 : {
6183 0 : if (singleton) {
6184 : /*
6185 : * Properties and associated type sets for singletons are cleared on
6186 : * every GC. The type object is normally destroyed too, but we don't
6187 : * charge this to 'temporary' as this is not for GC heap values.
6188 : */
6189 0 : JS_ASSERT(!newScript);
6190 0 : return;
6191 : }
6192 :
6193 0 : sizes->objects += mallocSizeOf(newScript);
6194 :
6195 : /*
6196 : * This counts memory that is in the temp pool but gets attributed
6197 : * elsewhere. See JSCompartment::sizeOfTypeInferenceData for more details.
6198 : */
6199 0 : size_t bytes = computedSizeOfExcludingThis();
6200 0 : sizes->objects += bytes;
6201 0 : sizes->temporary -= bytes;
6202 : }
|