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 : /* Inline members for javascript type inference. */
41 :
42 : #include "jsarray.h"
43 : #include "jsanalyze.h"
44 : #include "jscompartment.h"
45 : #include "jsgcmark.h"
46 : #include "jsinfer.h"
47 : #include "jsprf.h"
48 : #include "vm/GlobalObject.h"
49 :
50 : #include "vm/Stack-inl.h"
51 :
52 : #ifndef jsinferinlines_h___
53 : #define jsinferinlines_h___
54 :
55 : /////////////////////////////////////////////////////////////////////
56 : // Types
57 : /////////////////////////////////////////////////////////////////////
58 :
59 : namespace js {
60 : namespace types {
61 :
62 : /* static */ inline Type
63 41768749 : Type::ObjectType(JSObject *obj)
64 : {
65 41768749 : if (obj->hasSingletonType())
66 9269691 : return Type(uintptr_t(obj) | 1);
67 32499058 : return Type(uintptr_t(obj->type()));
68 : }
69 :
70 : /* static */ inline Type
71 1065273 : Type::ObjectType(TypeObject *obj)
72 : {
73 1065273 : if (obj->singleton)
74 123956 : return Type(uintptr_t(obj->singleton.get()) | 1);
75 941317 : return Type(uintptr_t(obj));
76 : }
77 :
78 : /* static */ inline Type
79 567319 : Type::ObjectType(TypeObjectKey *obj)
80 : {
81 567319 : return Type(uintptr_t(obj));
82 : }
83 :
84 : inline Type
85 167818288 : GetValueType(JSContext *cx, const Value &val)
86 : {
87 167818288 : JS_ASSERT(cx->typeInferenceEnabled());
88 167818288 : if (val.isDouble())
89 11840073 : return Type::DoubleType();
90 155978215 : if (val.isObject())
91 40956924 : return Type::ObjectType(&val.toObject());
92 115021291 : return Type::PrimitiveType(val.extractNonDoubleType());
93 : }
94 :
95 : inline TypeFlags
96 118022482 : PrimitiveTypeFlag(JSValueType type)
97 : {
98 118022482 : switch (type) {
99 : case JSVAL_TYPE_UNDEFINED:
100 11251361 : return TYPE_FLAG_UNDEFINED;
101 : case JSVAL_TYPE_NULL:
102 3500452 : return TYPE_FLAG_NULL;
103 : case JSVAL_TYPE_BOOLEAN:
104 2193979 : return TYPE_FLAG_BOOLEAN;
105 : case JSVAL_TYPE_INT32:
106 80208638 : return TYPE_FLAG_INT32;
107 : case JSVAL_TYPE_DOUBLE:
108 12246783 : return TYPE_FLAG_DOUBLE;
109 : case JSVAL_TYPE_STRING:
110 8577382 : return TYPE_FLAG_STRING;
111 : case JSVAL_TYPE_MAGIC:
112 43887 : return TYPE_FLAG_LAZYARGS;
113 : default:
114 0 : JS_NOT_REACHED("Bad type");
115 : return 0;
116 : }
117 : }
118 :
119 : inline JSValueType
120 774512 : TypeFlagPrimitive(TypeFlags flags)
121 : {
122 774512 : switch (flags) {
123 : case TYPE_FLAG_UNDEFINED:
124 128365 : return JSVAL_TYPE_UNDEFINED;
125 : case TYPE_FLAG_NULL:
126 7362 : return JSVAL_TYPE_NULL;
127 : case TYPE_FLAG_BOOLEAN:
128 8885 : return JSVAL_TYPE_BOOLEAN;
129 : case TYPE_FLAG_INT32:
130 548261 : return JSVAL_TYPE_INT32;
131 : case TYPE_FLAG_DOUBLE:
132 13002 : return JSVAL_TYPE_DOUBLE;
133 : case TYPE_FLAG_STRING:
134 66323 : return JSVAL_TYPE_STRING;
135 : case TYPE_FLAG_LAZYARGS:
136 2314 : return JSVAL_TYPE_MAGIC;
137 : default:
138 0 : JS_NOT_REACHED("Bad type");
139 : return (JSValueType) 0;
140 : }
141 : }
142 :
143 : /*
144 : * Get the canonical representation of an id to use when doing inference. This
145 : * maintains the constraint that if two different jsids map to the same property
146 : * in JS (e.g. 3 and "3"), they have the same type representation.
147 : */
148 : inline jsid
149 73691996 : MakeTypeId(JSContext *cx, jsid id)
150 : {
151 73691996 : JS_ASSERT(!JSID_IS_EMPTY(id));
152 :
153 : /*
154 : * All integers must map to the aggregate property for index types, including
155 : * negative integers.
156 : */
157 73691996 : if (JSID_IS_INT(id))
158 1471089 : return JSID_VOID;
159 :
160 : /*
161 : * Check for numeric strings, as in js_StringIsIndex, but allow negative
162 : * and overflowing integers.
163 : */
164 72220907 : if (JSID_IS_STRING(id)) {
165 41619851 : JSFlatString *str = JSID_TO_FLAT_STRING(id);
166 41619851 : const jschar *cp = str->getCharsZ(cx);
167 41619851 : if (JS7_ISDEC(*cp) || *cp == '-') {
168 25257 : cp++;
169 56166 : while (JS7_ISDEC(*cp))
170 5652 : cp++;
171 25257 : if (*cp == 0)
172 2905 : return JSID_VOID;
173 : }
174 41616946 : return id;
175 : }
176 :
177 30601056 : return JSID_VOID;
178 : }
179 :
180 : const char * TypeIdStringImpl(jsid id);
181 :
182 : /* Convert an id for printing during debug. */
183 : static inline const char *
184 189680 : TypeIdString(jsid id)
185 : {
186 : #ifdef DEBUG
187 189680 : return TypeIdStringImpl(id);
188 : #else
189 : return "(missing)";
190 : #endif
191 : }
192 :
193 : /*
194 : * Structure for type inference entry point functions. All functions which can
195 : * change type information must use this, and functions which depend on
196 : * intermediate types (i.e. JITs) can use this to ensure that intermediate
197 : * information is not collected and does not change.
198 : *
199 : * Pins inference results so that intermediate type information, TypeObjects
200 : * and JSScripts won't be collected during GC. Does additional sanity checking
201 : * that inference is not reentrant and that recompilations occur properly.
202 : */
203 : struct AutoEnterTypeInference
204 : {
205 : JSContext *cx;
206 : bool oldActiveAnalysis;
207 : bool oldActiveInference;
208 :
209 655808034 : AutoEnterTypeInference(JSContext *cx, bool compiling = false)
210 : : cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis),
211 655808034 : oldActiveInference(cx->compartment->activeInference)
212 : {
213 655808034 : JS_ASSERT_IF(!compiling, cx->compartment->types.inferenceEnabled);
214 655808034 : cx->compartment->activeAnalysis = true;
215 655808034 : cx->compartment->activeInference = true;
216 655808034 : }
217 :
218 655808034 : ~AutoEnterTypeInference()
219 : {
220 655808034 : cx->compartment->activeAnalysis = oldActiveAnalysis;
221 655808034 : cx->compartment->activeInference = oldActiveInference;
222 :
223 : /*
224 : * If there are no more type inference activations on the stack,
225 : * process any triggered recompilations. Note that we should not be
226 : * invoking any scripted code while type inference is running.
227 : * :TODO: assert this.
228 : */
229 655808034 : if (!cx->compartment->activeInference) {
230 655648702 : TypeCompartment *types = &cx->compartment->types;
231 655648702 : if (types->pendingNukeTypes)
232 0 : types->nukeTypes(cx);
233 655648702 : else if (types->pendingRecompiles)
234 32642 : types->processPendingRecompiles(cx);
235 : }
236 655808034 : }
237 : };
238 :
239 : /*
240 : * Structure marking the currently compiled script, for constraints which can
241 : * trigger recompilation.
242 : */
243 : struct AutoEnterCompilation
244 : {
245 : RecompileInfo &info;
246 :
247 134254 : AutoEnterCompilation(JSContext *cx, JSScript *script, bool constructing, unsigned chunkIndex)
248 134254 : : info(cx->compartment->types.compiledInfo)
249 : {
250 134254 : JS_ASSERT(!info.script);
251 134254 : info.script = script;
252 134254 : info.constructing = constructing;
253 134254 : info.chunkIndex = chunkIndex;
254 134254 : }
255 :
256 134254 : ~AutoEnterCompilation()
257 : {
258 134254 : JS_ASSERT(info.script);
259 134254 : info.script = NULL;
260 134254 : info.constructing = false;
261 134254 : info.chunkIndex = 0;
262 134254 : }
263 : };
264 :
265 : /////////////////////////////////////////////////////////////////////
266 : // Interface functions
267 : /////////////////////////////////////////////////////////////////////
268 :
269 : /*
270 : * These functions check whether inference is enabled before performing some
271 : * action on the type state. To avoid checking cx->typeInferenceEnabled()
272 : * everywhere, it is generally preferred to use one of these functions or
273 : * a type function on JSScript to perform inference operations.
274 : */
275 :
276 : /*
277 : * Get the default 'new' object for a given standard class, per the currently
278 : * active global.
279 : */
280 : inline TypeObject *
281 1625634 : GetTypeNewObject(JSContext *cx, JSProtoKey key)
282 : {
283 : JSObject *proto;
284 1625634 : if (!js_GetClassPrototype(cx, NULL, key, &proto, NULL))
285 0 : return NULL;
286 1625634 : return proto->getNewType(cx);
287 : }
288 :
289 : /* Get a type object for the immediate allocation site within a native. */
290 : inline TypeObject *
291 150030 : GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
292 : {
293 150030 : if (cx->typeInferenceEnabled()) {
294 : jsbytecode *pc;
295 53844 : JSScript *script = cx->stack.currentScript(&pc);
296 53844 : if (script)
297 53719 : return TypeScript::InitObject(cx, script, pc, key);
298 : }
299 96311 : return GetTypeNewObject(cx, key);
300 : }
301 :
302 : /*
303 : * When using a custom iterator within the initialization of a 'for in' loop,
304 : * mark the iterator values as unknown.
305 : */
306 : inline void
307 11976 : MarkIteratorUnknown(JSContext *cx)
308 : {
309 : extern void MarkIteratorUnknownSlow(JSContext *cx);
310 :
311 11976 : if (cx->typeInferenceEnabled())
312 2374 : MarkIteratorUnknownSlow(cx);
313 11976 : }
314 :
315 : /*
316 : * Monitor a javascript call, either on entry to the interpreter or made
317 : * from within the interpreter.
318 : */
319 : inline void
320 29081236 : TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
321 : {
322 : extern void TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
323 : const CallArgs &args, bool constructing);
324 :
325 29081236 : JSObject *callee = &args.callee();
326 29081236 : if (callee->isFunction()) {
327 29081236 : JSFunction *fun = callee->toFunction();
328 29081236 : if (fun->isInterpreted()) {
329 29081236 : JSScript *script = fun->script();
330 29081236 : if (!script->ensureRanAnalysis(cx, fun->environment()))
331 0 : return;
332 29081236 : if (cx->typeInferenceEnabled())
333 12954264 : TypeMonitorCallSlow(cx, callee, args, constructing);
334 : }
335 : }
336 : }
337 :
338 : inline bool
339 132685047 : TrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
340 : {
341 132685047 : if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties())
342 112061138 : return false;
343 :
344 20623909 : if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(cx, id))
345 6964581 : return false;
346 :
347 13659328 : return true;
348 : }
349 :
350 : /* Add a possible type for a property of obj. */
351 : inline void
352 2697930 : AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, Type type)
353 : {
354 2697930 : if (cx->typeInferenceEnabled())
355 679867 : id = MakeTypeId(cx, id);
356 2697930 : if (TrackPropertyTypes(cx, obj, id))
357 272835 : obj->type()->addPropertyType(cx, id, type);
358 2697930 : }
359 :
360 : inline void
361 114239973 : AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, const Value &value)
362 : {
363 114239973 : if (cx->typeInferenceEnabled())
364 27833051 : id = MakeTypeId(cx, id);
365 114239973 : if (TrackPropertyTypes(cx, obj, id))
366 13110850 : obj->type()->addPropertyType(cx, id, value);
367 114239973 : }
368 :
369 : inline void
370 98386 : AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type)
371 : {
372 98386 : if (cx->typeInferenceEnabled() && !obj->unknownProperties())
373 17709 : obj->addPropertyType(cx, name, type);
374 98386 : }
375 :
376 : inline void
377 0 : AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value)
378 : {
379 0 : if (cx->typeInferenceEnabled() && !obj->unknownProperties())
380 0 : obj->addPropertyType(cx, name, value);
381 0 : }
382 :
383 : /* Set one or more dynamic flags on a type object. */
384 : inline void
385 11254631 : MarkTypeObjectFlags(JSContext *cx, JSObject *obj, TypeObjectFlags flags)
386 : {
387 11254631 : if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->hasAllFlags(flags))
388 6773 : obj->type()->setFlags(cx, flags);
389 11254631 : }
390 :
391 : /*
392 : * Mark all properties of a type object as unknown. If markSetsUnknown is set,
393 : * scan the entire compartment and mark all type sets containing it as having
394 : * an unknown object. This is needed for correctness in dealing with mutable
395 : * __proto__, which can change the type of an object dynamically.
396 : */
397 : inline void
398 6601722 : MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
399 : bool markSetsUnknown = false)
400 : {
401 6601722 : if (cx->typeInferenceEnabled()) {
402 2242227 : if (!obj->unknownProperties())
403 13464 : obj->markUnknown(cx);
404 2242227 : if (markSetsUnknown && !(obj->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
405 165 : cx->compartment->types.markSetsUnknown(cx, obj);
406 : }
407 6601722 : }
408 :
409 : /*
410 : * Mark any property which has been deleted or configured to be non-writable or
411 : * have a getter/setter.
412 : */
413 : inline void
414 15747144 : MarkTypePropertyConfigured(JSContext *cx, JSObject *obj, jsid id)
415 : {
416 15747144 : if (cx->typeInferenceEnabled())
417 6793667 : id = MakeTypeId(cx, id);
418 15747144 : if (TrackPropertyTypes(cx, obj, id))
419 275643 : obj->type()->markPropertyConfigured(cx, id);
420 15747144 : }
421 :
422 : /* Mark a state change on a particular object. */
423 : inline void
424 38523 : MarkObjectStateChange(JSContext *cx, JSObject *obj)
425 : {
426 38523 : if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties())
427 12777 : obj->type()->markStateChange(cx);
428 38523 : }
429 :
430 : /*
431 : * For an array or object which has not yet escaped and been referenced elsewhere,
432 : * pick a new type based on the object's current contents.
433 : */
434 :
435 : inline void
436 5745 : FixArrayType(JSContext *cx, JSObject *obj)
437 : {
438 5745 : if (cx->typeInferenceEnabled())
439 1775 : cx->compartment->types.fixArrayType(cx, obj);
440 5745 : }
441 :
442 : inline void
443 2413 : FixObjectType(JSContext *cx, JSObject *obj)
444 : {
445 2413 : if (cx->typeInferenceEnabled())
446 1323 : cx->compartment->types.fixObjectType(cx, obj);
447 2413 : }
448 :
449 : /* Interface helpers for JSScript */
450 : extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval);
451 : extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::Type type);
452 :
453 : inline bool
454 1981460 : UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
455 : {
456 1987934 : return fp->isConstructing() && cx->typeInferenceEnabled() &&
457 346 : fp->prev() && fp->prev()->isScriptFrame() &&
458 1988280 : UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp));
459 : }
460 :
461 : /////////////////////////////////////////////////////////////////////
462 : // Script interface functions
463 : /////////////////////////////////////////////////////////////////////
464 :
465 : inline
466 469989 : TypeScript::TypeScript()
467 : {
468 469989 : this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE;
469 469989 : }
470 :
471 : /* static */ inline unsigned
472 199362 : TypeScript::NumTypeSets(JSScript *script)
473 : {
474 199362 : return script->nTypeSets + analyze::TotalSlots(script);
475 : }
476 :
477 : /* static */ inline TypeSet *
478 131463 : TypeScript::ReturnTypes(JSScript *script)
479 : {
480 131463 : return script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot();
481 : }
482 :
483 : /* static */ inline TypeSet *
484 29349997 : TypeScript::ThisTypes(JSScript *script)
485 : {
486 29349997 : return script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
487 : }
488 :
489 : /*
490 : * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
491 : * only the initial type of the variable (e.g. passed values for argTypes,
492 : * or undefined for localTypes) and not types from subsequent assignments.
493 : */
494 :
495 : /* static */ inline TypeSet *
496 45092259 : TypeScript::ArgTypes(JSScript *script, unsigned i)
497 : {
498 45092259 : JS_ASSERT(i < script->function()->nargs);
499 45092259 : return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
500 : }
501 :
502 : /* static */ inline TypeSet *
503 109515 : TypeScript::LocalTypes(JSScript *script, unsigned i)
504 : {
505 109515 : JS_ASSERT(i < script->nfixed);
506 109515 : return script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i);
507 : }
508 :
509 : /* static */ inline TypeSet *
510 336351 : TypeScript::SlotTypes(JSScript *script, unsigned slot)
511 : {
512 336351 : JS_ASSERT(slot < js::analyze::TotalSlots(script));
513 336351 : return script->types->typeArray() + script->nTypeSets + slot;
514 : }
515 :
516 : /* static */ inline TypeObject *
517 8019 : TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
518 : {
519 : JSObject *proto;
520 8019 : if (!js_GetClassPrototype(cx, script->global(), key, &proto, NULL))
521 0 : return NULL;
522 8019 : return proto->getNewType(cx);
523 : }
524 :
525 : struct AllocationSiteKey {
526 : JSScript *script;
527 :
528 : uint32_t offset : 24;
529 : JSProtoKey kind : 8;
530 :
531 : static const uint32_t OFFSET_LIMIT = (1 << 23);
532 :
533 1282626 : AllocationSiteKey() { PodZero(this); }
534 :
535 : typedef AllocationSiteKey Lookup;
536 :
537 1021850 : static inline uint32_t hash(AllocationSiteKey key) {
538 1021850 : return uint32_t(size_t(key.script->code + key.offset)) ^ key.kind;
539 : }
540 :
541 997860 : static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
542 997860 : return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
543 : }
544 : };
545 :
546 : /* static */ inline TypeObject *
547 2541677 : TypeScript::InitObject(JSContext *cx, JSScript *script, const jsbytecode *pc, JSProtoKey kind)
548 : {
549 : /* :XXX: Limit script->length so we don't need to check the offset up front? */
550 2541677 : uint32_t offset = pc - script->code;
551 :
552 2541677 : if (!cx->typeInferenceEnabled() || !script->hasGlobal() || offset >= AllocationSiteKey::OFFSET_LIMIT)
553 1529323 : return GetTypeNewObject(cx, kind);
554 :
555 1012354 : AllocationSiteKey key;
556 1012354 : key.script = script;
557 1012354 : key.offset = offset;
558 1012354 : key.kind = kind;
559 :
560 1012354 : if (!cx->compartment->types.allocationSiteTable)
561 5008 : return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
562 :
563 1007346 : AllocationSiteTable::Ptr p = cx->compartment->types.allocationSiteTable->lookup(key);
564 :
565 1007346 : if (p)
566 997850 : return p->value;
567 9496 : return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
568 : }
569 :
570 : /* static */ inline void
571 204270641 : TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
572 : {
573 204270641 : if (cx->typeInferenceEnabled())
574 75841479 : TypeMonitorResult(cx, script, pc, rval);
575 204270641 : }
576 :
577 : /* static */ inline void
578 1401220 : TypeScript::MonitorOverflow(JSContext *cx, JSScript *script, jsbytecode *pc)
579 : {
580 1401220 : if (cx->typeInferenceEnabled())
581 530616 : TypeDynamicResult(cx, script, pc, Type::DoubleType());
582 1401220 : }
583 :
584 : /* static */ inline void
585 544260 : TypeScript::MonitorString(JSContext *cx, JSScript *script, jsbytecode *pc)
586 : {
587 544260 : if (cx->typeInferenceEnabled())
588 282970 : TypeDynamicResult(cx, script, pc, Type::StringType());
589 544260 : }
590 :
591 : /* static */ inline void
592 3244 : TypeScript::MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc)
593 : {
594 3244 : if (cx->typeInferenceEnabled())
595 1577 : TypeDynamicResult(cx, script, pc, Type::UnknownType());
596 3244 : }
597 :
598 : /* static */ inline void
599 5119361 : TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc)
600 : {
601 5119361 : *script = cx->fp()->script();
602 5119361 : *pc = cx->regs().pc;
603 5119361 : }
604 :
605 : /* static */ inline void
606 1002079 : TypeScript::MonitorOverflow(JSContext *cx)
607 : {
608 : JSScript *script;
609 : jsbytecode *pc;
610 1002079 : GetPcScript(cx, &script, &pc);
611 1002079 : MonitorOverflow(cx, script, pc);
612 1002079 : }
613 :
614 : /* static */ inline void
615 149778 : TypeScript::MonitorString(JSContext *cx)
616 : {
617 : JSScript *script;
618 : jsbytecode *pc;
619 149778 : GetPcScript(cx, &script, &pc);
620 149778 : MonitorString(cx, script, pc);
621 149778 : }
622 :
623 : /* static */ inline void
624 884 : TypeScript::MonitorUnknown(JSContext *cx)
625 : {
626 : JSScript *script;
627 : jsbytecode *pc;
628 884 : GetPcScript(cx, &script, &pc);
629 884 : MonitorUnknown(cx, script, pc);
630 884 : }
631 :
632 : /* static */ inline void
633 : TypeScript::Monitor(JSContext *cx, const js::Value &rval)
634 : {
635 : JSScript *script;
636 : jsbytecode *pc;
637 : GetPcScript(cx, &script, &pc);
638 : Monitor(cx, script, pc, rval);
639 : }
640 :
641 : /* static */ inline void
642 28571882 : TypeScript::MonitorAssign(JSContext *cx, JSObject *obj, jsid id)
643 : {
644 28571882 : if (cx->typeInferenceEnabled() && !obj->hasSingletonType()) {
645 : /*
646 : * Mark as unknown any object which has had dynamic assignments to
647 : * non-integer properties at SETELEM opcodes. This avoids making large
648 : * numbers of type properties for hashmap-style objects. We don't need
649 : * to do this for objects with singleton type, because type properties
650 : * are only constructed for them when analyzed scripts depend on those
651 : * specific properties.
652 : */
653 : uint32_t i;
654 6091109 : if (js_IdIsIndex(id, &i))
655 5688219 : return;
656 402890 : MarkTypeObjectUnknownProperties(cx, obj->type());
657 : }
658 : }
659 :
660 : /* static */ inline void
661 12507487 : TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
662 : {
663 12507487 : if (!cx->typeInferenceEnabled())
664 0 : return;
665 12507487 : JS_ASSERT(script->types);
666 :
667 : /* Analyze the script regardless if -a was used. */
668 12507487 : bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS);
669 :
670 12507487 : if (!ThisTypes(script)->hasType(type) || analyze) {
671 8513154 : AutoEnterTypeInference enter(cx);
672 :
673 : InferSpew(ISpewOps, "externalType: setThis #%u: %s",
674 4256577 : script->id(), TypeString(type));
675 4256577 : ThisTypes(script)->addType(cx, type);
676 :
677 4256577 : if (analyze && script->types->hasScope())
678 4206534 : script->ensureRanInference(cx);
679 : }
680 : }
681 :
682 : /* static */ inline void
683 11821640 : TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
684 : {
685 11821640 : if (cx->typeInferenceEnabled())
686 11726857 : SetThis(cx, script, GetValueType(cx, value));
687 11821640 : }
688 :
689 : /* static */ inline void
690 10725 : TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type)
691 : {
692 10725 : if (!cx->typeInferenceEnabled())
693 0 : return;
694 10725 : JS_ASSERT(script->types);
695 :
696 10725 : if (!LocalTypes(script, local)->hasType(type)) {
697 900 : AutoEnterTypeInference enter(cx);
698 :
699 : InferSpew(ISpewOps, "externalType: setLocal #%u %u: %s",
700 450 : script->id(), local, TypeString(type));
701 450 : LocalTypes(script, local)->addType(cx, type);
702 : }
703 : }
704 :
705 : /* static */ inline void
706 37348 : TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value)
707 : {
708 37348 : if (cx->typeInferenceEnabled()) {
709 10725 : Type type = GetValueType(cx, value);
710 10725 : SetLocal(cx, script, local, type);
711 : }
712 37348 : }
713 :
714 : /* static */ inline void
715 20683053 : TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
716 : {
717 20683053 : if (!cx->typeInferenceEnabled())
718 0 : return;
719 20683053 : JS_ASSERT(script->types);
720 :
721 20683053 : if (!ArgTypes(script, arg)->hasType(type)) {
722 166014 : AutoEnterTypeInference enter(cx);
723 :
724 : InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
725 83007 : script->id(), arg, TypeString(type));
726 83007 : ArgTypes(script, arg)->addType(cx, type);
727 : }
728 : }
729 :
730 : /* static */ inline void
731 20686090 : TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
732 : {
733 20686090 : if (cx->typeInferenceEnabled()) {
734 20683053 : Type type = GetValueType(cx, value);
735 20683053 : SetArgument(cx, script, arg, type);
736 : }
737 20686090 : }
738 :
739 : void
740 1964743 : TypeScript::trace(JSTracer *trc)
741 : {
742 1964743 : if (hasScope() && global)
743 66676 : gc::MarkObject(trc, &global, "script_global");
744 :
745 : /* Note: nesting does not keep anything alive. */
746 1964743 : }
747 :
748 : /////////////////////////////////////////////////////////////////////
749 : // TypeCompartment
750 : /////////////////////////////////////////////////////////////////////
751 :
752 : inline JSCompartment *
753 163751 : TypeCompartment::compartment()
754 : {
755 163751 : return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
756 : }
757 :
758 : inline void
759 3120981 : TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, Type type)
760 : {
761 3120981 : JS_ASSERT(this == &cx->compartment->types);
762 3120981 : JS_ASSERT(!cx->runtime->gcRunning);
763 :
764 : InferSpew(ISpewOps, "pending: %sC%p%s %s",
765 : InferSpewColor(constraint), constraint, InferSpewColorReset(),
766 3120981 : TypeString(type));
767 :
768 3120981 : if ((pendingCount == pendingCapacity) && !growPendingArray(cx))
769 0 : return;
770 :
771 3120981 : PendingWork &pending = pendingArray[pendingCount++];
772 3120981 : pending.constraint = constraint;
773 3120981 : pending.source = source;
774 3120981 : pending.type = type;
775 : }
776 :
777 : inline void
778 4583273 : TypeCompartment::resolvePending(JSContext *cx)
779 : {
780 4583273 : JS_ASSERT(this == &cx->compartment->types);
781 :
782 4583273 : if (resolving) {
783 : /* There is an active call further up resolving the worklist. */
784 1192401 : return;
785 : }
786 :
787 3390872 : resolving = true;
788 :
789 : /* Handle all pending type registrations. */
790 9902725 : while (pendingCount) {
791 3120981 : const PendingWork &pending = pendingArray[--pendingCount];
792 : InferSpew(ISpewOps, "resolve: %sC%p%s %s",
793 : InferSpewColor(pending.constraint), pending.constraint,
794 3120981 : InferSpewColorReset(), TypeString(pending.type));
795 3120981 : pending.constraint->newType(cx, pending.source, pending.type);
796 : }
797 :
798 3390872 : resolving = false;
799 : }
800 :
801 : /////////////////////////////////////////////////////////////////////
802 : // TypeSet
803 : /////////////////////////////////////////////////////////////////////
804 :
805 : /*
806 : * The sets of objects and scripts in a type set grow monotonically, are usually
807 : * empty, almost always small, and sometimes big. For empty or singleton sets,
808 : * the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE,
809 : * an array of this length is used to store the elements. For larger sets, a hash
810 : * table filled to 25%-50% of capacity is used, with collisions resolved by linear
811 : * probing. TODO: replace these with jshashtables.
812 : */
813 : const unsigned SET_ARRAY_SIZE = 8;
814 :
815 : /* Get the capacity of a set with the given element count. */
816 : static inline unsigned
817 4389814 : HashSetCapacity(unsigned count)
818 : {
819 4389814 : JS_ASSERT(count >= 2);
820 :
821 4389814 : if (count <= SET_ARRAY_SIZE)
822 17231 : return SET_ARRAY_SIZE;
823 :
824 : unsigned log2;
825 4372583 : JS_FLOOR_LOG2(log2, count);
826 4372583 : return 1 << (log2 + 2);
827 : }
828 :
829 : /* Compute the FNV hash for the low 32 bits of v. */
830 : template <class T, class KEY>
831 : static inline uint32_t
832 2717673 : HashKey(T v)
833 : {
834 2717673 : uint32_t nv = KEY::keyBits(v);
835 :
836 2717673 : uint32_t hash = 84696351 ^ (nv & 0xff);
837 2717673 : hash = (hash * 16777619) ^ ((nv >> 8) & 0xff);
838 2717673 : hash = (hash * 16777619) ^ ((nv >> 16) & 0xff);
839 2717673 : return (hash * 16777619) ^ ((nv >> 24) & 0xff);
840 : }
841 :
842 : /*
843 : * Insert space for an element into the specified set and grow its capacity if needed.
844 : * returned value is an existing or new entry (NULL if new).
845 : */
846 : template <class T, class U, class KEY>
847 : static U **
848 860368 : HashSetInsertTry(JSCompartment *compartment, U **&values, unsigned &count, T key)
849 : {
850 860368 : unsigned capacity = HashSetCapacity(count);
851 860368 : unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
852 :
853 : /* Whether we are converting from a fixed array to hashtable. */
854 860368 : bool converting = (count == SET_ARRAY_SIZE);
855 :
856 860368 : if (!converting) {
857 2443567 : while (values[insertpos] != NULL) {
858 1087746 : if (KEY::getKey(values[insertpos]) == key)
859 345901 : return &values[insertpos];
860 741845 : insertpos = (insertpos + 1) & (capacity - 1);
861 : }
862 : }
863 :
864 514467 : count++;
865 514467 : unsigned newCapacity = HashSetCapacity(count);
866 :
867 514467 : if (newCapacity == capacity) {
868 488944 : JS_ASSERT(!converting);
869 488944 : return &values[insertpos];
870 : }
871 :
872 25523 : U **newValues = compartment->typeLifoAlloc.newArray<U*>(newCapacity);
873 25523 : if (!newValues)
874 0 : return NULL;
875 25523 : PodZero(newValues, newCapacity);
876 :
877 1525771 : for (unsigned i = 0; i < capacity; i++) {
878 1500248 : if (values[i]) {
879 772136 : unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
880 1674364 : while (newValues[pos] != NULL)
881 130092 : pos = (pos + 1) & (newCapacity - 1);
882 772136 : newValues[pos] = values[i];
883 : }
884 : }
885 :
886 25523 : values = newValues;
887 :
888 25523 : insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
889 64347 : while (values[insertpos] != NULL)
890 13301 : insertpos = (insertpos + 1) & (newCapacity - 1);
891 25523 : return &values[insertpos];
892 : }
893 :
894 : /*
895 : * Insert an element into the specified set if it is not already there, returning
896 : * an entry which is NULL if the element was not there.
897 : */
898 : template <class T, class U, class KEY>
899 : static inline U **
900 16986034 : HashSetInsert(JSCompartment *compartment, U **&values, unsigned &count, T key)
901 : {
902 16986034 : if (count == 0) {
903 650882 : JS_ASSERT(values == NULL);
904 650882 : count++;
905 650882 : return (U **) &values;
906 : }
907 :
908 16335152 : if (count == 1) {
909 10993426 : U *oldData = (U*) values;
910 10993426 : if (KEY::getKey(oldData) == key)
911 10938007 : return (U **) &values;
912 :
913 55419 : values = compartment->typeLifoAlloc.newArray<U*>(SET_ARRAY_SIZE);
914 55419 : if (!values) {
915 0 : values = (U **) oldData;
916 0 : return NULL;
917 : }
918 55419 : PodZero(values, SET_ARRAY_SIZE);
919 55419 : count++;
920 :
921 55419 : values[0] = oldData;
922 55419 : return &values[1];
923 : }
924 :
925 5341726 : if (count <= SET_ARRAY_SIZE) {
926 12541491 : for (unsigned i = 0; i < count; i++) {
927 12423901 : if (KEY::getKey(values[i]) == key)
928 4373275 : return &values[i];
929 : }
930 :
931 117590 : if (count < SET_ARRAY_SIZE) {
932 108083 : count++;
933 108083 : return &values[count - 1];
934 : }
935 : }
936 :
937 860368 : return HashSetInsertTry<T,U,KEY>(compartment, values, count, key);
938 : }
939 :
940 : /* Lookup an entry in a hash set, return NULL if it does not exist. */
941 : template <class T, class U, class KEY>
942 : static inline U *
943 49016756 : HashSetLookup(U **values, unsigned count, T key)
944 : {
945 49016756 : if (count == 0)
946 7539826 : return NULL;
947 :
948 41476930 : if (count == 1)
949 33372783 : return (KEY::getKey((U *) values) == key) ? (U *) values : NULL;
950 :
951 8104147 : if (count <= SET_ARRAY_SIZE) {
952 13176362 : for (unsigned i = 0; i < count; i++) {
953 12928166 : if (KEY::getKey(values[i]) == key)
954 6796305 : return values[i];
955 : }
956 248196 : return NULL;
957 : }
958 :
959 1059646 : unsigned capacity = HashSetCapacity(count);
960 1059646 : unsigned pos = HashKey<T,KEY>(key) & (capacity - 1);
961 :
962 3251403 : while (values[pos] != NULL) {
963 2156153 : if (KEY::getKey(values[pos]) == key)
964 1024042 : return values[pos];
965 1132111 : pos = (pos + 1) & (capacity - 1);
966 : }
967 :
968 35604 : return NULL;
969 : }
970 :
971 : inline bool
972 171720380 : TypeSet::hasType(Type type)
973 : {
974 171720380 : if (unknown())
975 13692725 : return true;
976 :
977 158027655 : if (type.isUnknown()) {
978 54533 : return false;
979 157973122 : } else if (type.isPrimitive()) {
980 116227001 : return !!(flags & PrimitiveTypeFlag(type.primitive()));
981 41746121 : } else if (type.isAnyObject()) {
982 346578 : return !!(flags & TYPE_FLAG_ANYOBJECT);
983 : } else {
984 41399543 : return !!(flags & TYPE_FLAG_ANYOBJECT) ||
985 : HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
986 41399543 : (objectSet, baseObjectCount(), type.objectKey()) != NULL;
987 : }
988 : }
989 :
990 : inline void
991 1274934 : TypeSet::setBaseObjectCount(uint32_t count)
992 : {
993 1274934 : JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT);
994 : flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK)
995 1274934 : | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
996 1274934 : }
997 :
998 : inline void
999 135609 : TypeSet::clearObjects()
1000 : {
1001 135609 : setBaseObjectCount(0);
1002 135609 : objectSet = NULL;
1003 135609 : }
1004 :
1005 : inline void
1006 6329505 : TypeSet::addType(JSContext *cx, Type type)
1007 : {
1008 6329505 : JS_ASSERT(cx->compartment->activeInference);
1009 :
1010 6329505 : if (unknown())
1011 2514113 : return;
1012 :
1013 3815392 : if (type.isUnknown()) {
1014 72429 : flags |= TYPE_FLAG_BASE_MASK;
1015 72429 : clearObjects();
1016 72429 : JS_ASSERT(unknown());
1017 3742963 : } else if (type.isPrimitive()) {
1018 1795481 : TypeFlags flag = PrimitiveTypeFlag(type.primitive());
1019 1795481 : if (flags & flag)
1020 764357 : return;
1021 :
1022 : /* If we add float to a type set it is also considered to contain int. */
1023 1031124 : if (flag == TYPE_FLAG_DOUBLE)
1024 49015 : flag |= TYPE_FLAG_INT32;
1025 :
1026 1031124 : flags |= flag;
1027 : } else {
1028 1947482 : if (flags & TYPE_FLAG_ANYOBJECT)
1029 5028 : return;
1030 1942454 : if (type.isAnyObject())
1031 26405 : goto unknownObject;
1032 1916049 : uint32_t objectCount = baseObjectCount();
1033 1916049 : TypeObjectKey *object = type.objectKey();
1034 : TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
1035 1916049 : (cx->compartment, objectSet, objectCount, object);
1036 1916049 : if (!pentry) {
1037 0 : cx->compartment->types.setPendingNukeTypes(cx);
1038 0 : return;
1039 : }
1040 1916049 : if (*pentry)
1041 1041881 : return;
1042 874168 : *pentry = object;
1043 :
1044 874168 : setBaseObjectCount(objectCount);
1045 :
1046 874168 : if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
1047 15 : goto unknownObject;
1048 :
1049 874153 : if (type.isTypeObject()) {
1050 287945 : TypeObject *nobject = type.typeObject();
1051 287945 : JS_ASSERT(!nobject->singleton);
1052 287945 : if (nobject->unknownProperties())
1053 26263 : goto unknownObject;
1054 261682 : if (objectCount > 1) {
1055 33702 : nobject->contribution += (objectCount - 1) * (objectCount - 1);
1056 33702 : if (nobject->contribution >= TypeObject::CONTRIBUTION_LIMIT) {
1057 : InferSpew(ISpewOps, "limitUnknown: %sT%p%s",
1058 360 : InferSpewColor(this), this, InferSpewColorReset());
1059 360 : goto unknownObject;
1060 : }
1061 : }
1062 : }
1063 : }
1064 :
1065 : if (false) {
1066 : unknownObject:
1067 53043 : type = Type::AnyObjectType();
1068 53043 : flags |= TYPE_FLAG_ANYOBJECT;
1069 53043 : clearObjects();
1070 : }
1071 :
1072 : InferSpew(ISpewOps, "addType: %sT%p%s %s",
1073 : InferSpewColor(this), this, InferSpewColorReset(),
1074 2004126 : TypeString(type));
1075 :
1076 : /* Propagate the type to all constraints. */
1077 2004126 : TypeConstraint *constraint = constraintList;
1078 5753228 : while (constraint) {
1079 1744976 : cx->compartment->types.addPending(cx, constraint, this, type);
1080 1744976 : constraint = constraint->next;
1081 : }
1082 :
1083 2004126 : cx->compartment->types.resolvePending(cx);
1084 : }
1085 :
1086 : inline void
1087 14075888 : TypeSet::setOwnProperty(JSContext *cx, bool configured)
1088 : {
1089 14075888 : TypeFlags nflags = TYPE_FLAG_OWN_PROPERTY | (configured ? TYPE_FLAG_CONFIGURED_PROPERTY : 0);
1090 :
1091 14075888 : if ((flags & nflags) == nflags)
1092 13992423 : return;
1093 :
1094 83465 : flags |= nflags;
1095 :
1096 : /* Propagate the change to all constraints. */
1097 83465 : TypeConstraint *constraint = constraintList;
1098 173823 : while (constraint) {
1099 6893 : constraint->newPropertyState(cx, this);
1100 6893 : constraint = constraint->next;
1101 : }
1102 : }
1103 :
1104 : inline unsigned
1105 6386361 : TypeSet::getObjectCount()
1106 : {
1107 6386361 : JS_ASSERT(!unknownObject());
1108 6386361 : uint32_t count = baseObjectCount();
1109 6386361 : if (count > SET_ARRAY_SIZE)
1110 1941954 : return HashSetCapacity(count);
1111 4444407 : return count;
1112 : }
1113 :
1114 : inline TypeObjectKey *
1115 2618538 : TypeSet::getObject(unsigned i)
1116 : {
1117 2618538 : JS_ASSERT(i < getObjectCount());
1118 2618538 : if (baseObjectCount() == 1) {
1119 839286 : JS_ASSERT(i == 0);
1120 839286 : return (TypeObjectKey *) objectSet;
1121 : }
1122 1779252 : return objectSet[i];
1123 : }
1124 :
1125 : inline JSObject *
1126 759577 : TypeSet::getSingleObject(unsigned i)
1127 : {
1128 759577 : TypeObjectKey *key = getObject(i);
1129 759577 : return (uintptr_t(key) & 1) ? (JSObject *)(uintptr_t(key) ^ 1) : NULL;
1130 : }
1131 :
1132 : inline TypeObject *
1133 838705 : TypeSet::getTypeObject(unsigned i)
1134 : {
1135 838705 : TypeObjectKey *key = getObject(i);
1136 838705 : return (key && !(uintptr_t(key) & 1)) ? (TypeObject *) key : NULL;
1137 : }
1138 :
1139 : /////////////////////////////////////////////////////////////////////
1140 : // TypeCallsite
1141 : /////////////////////////////////////////////////////////////////////
1142 :
1143 : inline
1144 56882 : TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
1145 : bool isNew, unsigned argumentCount)
1146 : : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
1147 56882 : thisTypes(NULL), returnTypes(NULL)
1148 : {
1149 : /* Caller must check for failure. */
1150 56882 : argumentTypes = cx->typeLifoAlloc().newArray<TypeSet*>(argumentCount);
1151 56882 : }
1152 :
1153 : /////////////////////////////////////////////////////////////////////
1154 : // TypeObject
1155 : /////////////////////////////////////////////////////////////////////
1156 :
1157 671162 : inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
1158 : {
1159 671162 : PodZero(this);
1160 :
1161 : /* Inner objects may not appear on prototype chains. */
1162 671162 : JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject);
1163 :
1164 671162 : this->proto = proto;
1165 :
1166 671162 : if (function)
1167 15181 : flags |= OBJECT_FLAG_FUNCTION;
1168 671162 : if (unknown)
1169 443314 : flags |= OBJECT_FLAG_UNKNOWN_MASK;
1170 :
1171 671162 : InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
1172 671162 : }
1173 :
1174 : inline uint32_t
1175 31789934 : TypeObject::basePropertyCount() const
1176 : {
1177 31789934 : return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
1178 : }
1179 :
1180 : inline void
1181 342978 : TypeObject::setBasePropertyCount(uint32_t count)
1182 : {
1183 342978 : JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
1184 : flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
1185 342978 : | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
1186 342978 : }
1187 :
1188 : inline TypeSet *
1189 14761608 : TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
1190 : {
1191 14761608 : JS_ASSERT(cx->compartment->activeInference);
1192 14761608 : JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
1193 14761608 : JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
1194 14761608 : JS_ASSERT(!unknownProperties());
1195 :
1196 14761608 : uint32_t propertyCount = basePropertyCount();
1197 : Property **pprop = HashSetInsert<jsid,Property,Property>
1198 14761608 : (cx->compartment, propertySet, propertyCount, id);
1199 14761608 : if (!pprop) {
1200 0 : cx->compartment->types.setPendingNukeTypes(cx);
1201 0 : return NULL;
1202 : }
1203 :
1204 14761608 : if (!*pprop) {
1205 146306 : setBasePropertyCount(propertyCount);
1206 146306 : if (!addProperty(cx, id, pprop))
1207 0 : return NULL;
1208 146306 : if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
1209 0 : markUnknown(cx);
1210 0 : TypeSet *types = TypeSet::make(cx, "propertyOverflow");
1211 0 : types->addType(cx, Type::UnknownType());
1212 0 : return types;
1213 : }
1214 : }
1215 :
1216 14761608 : TypeSet *types = &(*pprop)->types;
1217 :
1218 14761608 : if (assign)
1219 13752687 : types->setOwnProperty(cx, false);
1220 :
1221 14761608 : return types;
1222 : }
1223 :
1224 : inline TypeSet *
1225 8531909 : TypeObject::maybeGetProperty(JSContext *cx, jsid id)
1226 : {
1227 8531909 : JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
1228 8531909 : JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
1229 8531909 : JS_ASSERT(!unknownProperties());
1230 :
1231 : Property *prop = HashSetLookup<jsid,Property,Property>
1232 8531909 : (propertySet, basePropertyCount(), id);
1233 :
1234 8531909 : return prop ? &prop->types : NULL;
1235 : }
1236 :
1237 : inline unsigned
1238 2472232 : TypeObject::getPropertyCount()
1239 : {
1240 2472232 : uint32_t count = basePropertyCount();
1241 2472232 : if (count > SET_ARRAY_SIZE)
1242 2354 : return HashSetCapacity(count);
1243 2469878 : return count;
1244 : }
1245 :
1246 : inline Property *
1247 11174 : TypeObject::getProperty(unsigned i)
1248 : {
1249 11174 : JS_ASSERT(i < getPropertyCount());
1250 11174 : if (basePropertyCount() == 1) {
1251 3424 : JS_ASSERT(i == 0);
1252 3424 : return (Property *) propertySet;
1253 : }
1254 7750 : return propertySet[i];
1255 : }
1256 :
1257 : inline void
1258 249717 : TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
1259 : {
1260 249717 : TypeObjectFlags flags = 0;
1261 :
1262 249717 : switch (key) {
1263 : case JSProto_Function:
1264 15181 : JS_ASSERT(isFunction());
1265 : /* FALLTHROUGH */
1266 :
1267 : case JSProto_Object:
1268 : flags = OBJECT_FLAG_NON_DENSE_ARRAY
1269 : | OBJECT_FLAG_NON_PACKED_ARRAY
1270 240734 : | OBJECT_FLAG_NON_TYPED_ARRAY;
1271 240734 : break;
1272 :
1273 : case JSProto_Array:
1274 8026 : flags = OBJECT_FLAG_NON_TYPED_ARRAY;
1275 8026 : break;
1276 :
1277 : default:
1278 : /* :XXX: abstract */
1279 0 : JS_ASSERT(key == JSProto_Int8Array ||
1280 : key == JSProto_Uint8Array ||
1281 : key == JSProto_Int16Array ||
1282 : key == JSProto_Uint16Array ||
1283 : key == JSProto_Int32Array ||
1284 : key == JSProto_Uint32Array ||
1285 : key == JSProto_Float32Array ||
1286 : key == JSProto_Float64Array ||
1287 957 : key == JSProto_Uint8ClampedArray);
1288 : flags = OBJECT_FLAG_NON_DENSE_ARRAY
1289 957 : | OBJECT_FLAG_NON_PACKED_ARRAY;
1290 957 : break;
1291 : }
1292 :
1293 249717 : if (!hasAllFlags(flags))
1294 152869 : setFlags(cx, flags);
1295 249717 : }
1296 :
1297 : inline JSObject *
1298 0 : TypeObject::getGlobal()
1299 : {
1300 0 : if (singleton)
1301 0 : return &singleton->global();
1302 0 : if (interpretedFunction && interpretedFunction->script()->compileAndGo)
1303 0 : return &interpretedFunction->global();
1304 0 : return NULL;
1305 : }
1306 :
1307 : inline void
1308 13433107 : TypeObject::writeBarrierPre(TypeObject *type)
1309 : {
1310 : #ifdef JSGC_INCREMENTAL
1311 13433107 : if (!type)
1312 1863942 : return;
1313 :
1314 11569165 : JSCompartment *comp = type->compartment();
1315 11569165 : if (comp->needsBarrier()) {
1316 2537 : TypeObject *tmp = type;
1317 2537 : MarkTypeObjectUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
1318 2537 : JS_ASSERT(tmp == type);
1319 : }
1320 : #endif
1321 : }
1322 :
1323 : inline void
1324 23139643 : TypeObject::writeBarrierPost(TypeObject *type, void *addr)
1325 : {
1326 23139643 : }
1327 :
1328 : inline void
1329 57323769 : TypeObject::readBarrier(TypeObject *type)
1330 : {
1331 : #ifdef JSGC_INCREMENTAL
1332 57323769 : JSCompartment *comp = type->compartment();
1333 57323769 : if (comp->needsBarrier()) {
1334 14650 : TypeObject *tmp = type;
1335 14650 : MarkTypeObjectUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
1336 14650 : JS_ASSERT(tmp == type);
1337 : }
1338 : #endif
1339 57323769 : }
1340 :
1341 : inline void
1342 605 : TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
1343 : {
1344 : #ifdef JSGC_INCREMENTAL
1345 605 : if (!newScript)
1346 565 : return;
1347 :
1348 40 : JSCompartment *comp = newScript->fun->compartment();
1349 40 : if (comp->needsBarrier()) {
1350 0 : MarkObject(comp->barrierTracer(), &newScript->fun, "write barrier");
1351 0 : MarkShape(comp->barrierTracer(), &newScript->shape, "write barrier");
1352 : }
1353 : #endif
1354 : }
1355 :
1356 : inline void
1357 605 : TypeNewScript::writeBarrierPost(TypeNewScript *newScript, void *addr)
1358 : {
1359 605 : }
1360 :
1361 : inline
1362 146306 : Property::Property(jsid id)
1363 146306 : : id(id)
1364 : {
1365 146306 : }
1366 :
1367 : inline
1368 9670 : Property::Property(const Property &o)
1369 9670 : : id(o.id.get()), types(o.types)
1370 : {
1371 9670 : }
1372 :
1373 : } } /* namespace js::types */
1374 :
1375 : inline bool
1376 109967653 : JSScript::ensureHasTypes(JSContext *cx)
1377 : {
1378 109967653 : return types || makeTypes(cx);
1379 : }
1380 :
1381 : inline bool
1382 109873943 : JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope)
1383 : {
1384 109873943 : JSScript *self = this;
1385 :
1386 109873943 : if (!self->ensureHasTypes(cx))
1387 0 : return false;
1388 109873943 : if (!self->types->hasScope()) {
1389 939858 : js::CheckRoot root(cx, &self);
1390 939858 : js::RootObject objRoot(cx, &scope);
1391 469929 : if (!js::types::TypeScript::SetScope(cx, self, scope))
1392 0 : return false;
1393 : }
1394 109873943 : if (!self->hasAnalysis() && !self->makeAnalysis(cx))
1395 0 : return false;
1396 109873943 : JS_ASSERT(self->analysis()->ranBytecode());
1397 109873943 : return true;
1398 : }
1399 :
1400 : inline bool
1401 4464460 : JSScript::ensureRanInference(JSContext *cx)
1402 : {
1403 4464460 : if (!ensureRanAnalysis(cx, NULL))
1404 0 : return false;
1405 4464460 : if (!analysis()->ranInference()) {
1406 77582 : js::types::AutoEnterTypeInference enter(cx);
1407 38791 : analysis()->analyzeTypes(cx);
1408 : }
1409 4464460 : return !analysis()->OOM() &&
1410 4464460 : !cx->compartment->types.pendingNukeTypes;
1411 : }
1412 :
1413 : inline bool
1414 1457407688 : JSScript::hasAnalysis()
1415 : {
1416 1457407688 : return types && types->analysis;
1417 : }
1418 :
1419 : inline js::analyze::ScriptAnalysis *
1420 777909119 : JSScript::analysis()
1421 : {
1422 777909119 : JS_ASSERT(hasAnalysis());
1423 777909119 : return types->analysis;
1424 : }
1425 :
1426 : inline void
1427 8133683 : JSScript::clearAnalysis()
1428 : {
1429 8133683 : if (types)
1430 2409316 : types->analysis = NULL;
1431 8133683 : }
1432 :
1433 : inline void
1434 : js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32_t offset, uint32_t which,
1435 : js::types::Type type)
1436 : {
1437 : js::types::TypeSet *pushed = pushedTypes(offset, which);
1438 : pushed->addType(cx, type);
1439 : }
1440 :
1441 : inline js::types::TypeObject *
1442 3063489 : JSCompartment::getEmptyType(JSContext *cx)
1443 : {
1444 3063489 : if (!emptyTypeObject)
1445 25596 : emptyTypeObject = types.newTypeObject(cx, NULL, JSProto_Object, NULL, true);
1446 3063489 : return emptyTypeObject;
1447 : }
1448 :
1449 : #endif // jsinferinlines_h___
|