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