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 42524920 : Type::ObjectType(JSObject *obj)
64 : {
65 42524920 : if (obj->hasSingletonType())
66 9507877 : return Type(uintptr_t(obj) | 1);
67 33017043 : return Type(uintptr_t(obj->type()));
68 : }
69 :
70 : /* static */ inline Type
71 1062527 : Type::ObjectType(TypeObject *obj)
72 : {
73 1062527 : if (obj->singleton)
74 124092 : return Type(uintptr_t(obj->singleton.get()) | 1);
75 938435 : return Type(uintptr_t(obj));
76 : }
77 :
78 : /* static */ inline Type
79 567207 : Type::ObjectType(TypeObjectKey *obj)
80 : {
81 567207 : return Type(uintptr_t(obj));
82 : }
83 :
84 : inline Type
85 168541971 : GetValueType(JSContext *cx, const Value &val)
86 : {
87 168541971 : JS_ASSERT(cx->typeInferenceEnabled());
88 168541971 : if (val.isDouble())
89 11838191 : return Type::DoubleType();
90 156703780 : if (val.isObject())
91 41713240 : return Type::ObjectType(&val.toObject());
92 114990540 : return Type::PrimitiveType(val.extractNonDoubleType());
93 : }
94 :
95 : inline TypeFlags
96 117997633 : PrimitiveTypeFlag(JSValueType type)
97 : {
98 117997633 : switch (type) {
99 : case JSVAL_TYPE_UNDEFINED:
100 11250850 : return TYPE_FLAG_UNDEFINED;
101 : case JSVAL_TYPE_NULL:
102 3500577 : return TYPE_FLAG_NULL;
103 : case JSVAL_TYPE_BOOLEAN:
104 2194125 : return TYPE_FLAG_BOOLEAN;
105 : case JSVAL_TYPE_INT32:
106 80194798 : return TYPE_FLAG_INT32;
107 : case JSVAL_TYPE_DOUBLE:
108 12237227 : return TYPE_FLAG_DOUBLE;
109 : case JSVAL_TYPE_STRING:
110 8576169 : 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 774277 : TypeFlagPrimitive(TypeFlags flags)
121 : {
122 774277 : switch (flags) {
123 : case TYPE_FLAG_UNDEFINED:
124 128342 : return JSVAL_TYPE_UNDEFINED;
125 : case TYPE_FLAG_NULL:
126 7374 : return JSVAL_TYPE_NULL;
127 : case TYPE_FLAG_BOOLEAN:
128 8913 : return JSVAL_TYPE_BOOLEAN;
129 : case TYPE_FLAG_INT32:
130 548025 : return JSVAL_TYPE_INT32;
131 : case TYPE_FLAG_DOUBLE:
132 13081 : return JSVAL_TYPE_DOUBLE;
133 : case TYPE_FLAG_STRING:
134 66228 : 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 73460342 : MakeTypeId(JSContext *cx, jsid id)
150 : {
151 73460342 : 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 73460342 : if (JSID_IS_INT(id))
158 1471019 : return JSID_VOID;
159 :
160 : /*
161 : * Check for numeric strings, as in js_StringIsIndex, but allow negative
162 : * and overflowing integers.
163 : */
164 71989323 : if (JSID_IS_STRING(id)) {
165 41388432 : JSFlatString *str = JSID_TO_FLAT_STRING(id);
166 41388432 : const jschar *cp = str->getCharsZ(cx);
167 41388432 : 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 41385527 : return id;
175 : }
176 :
177 30600891 : 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 188589 : TypeIdString(jsid id)
185 : {
186 : #ifdef DEBUG
187 188589 : 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 956730921 : AutoEnterTypeInference(JSContext *cx, bool compiling = false)
210 : : cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis),
211 956730921 : oldActiveInference(cx->compartment->activeInference)
212 : {
213 956730921 : JS_ASSERT_IF(!compiling, cx->compartment->types.inferenceEnabled);
214 956730921 : cx->compartment->activeAnalysis = true;
215 956730921 : cx->compartment->activeInference = true;
216 956730921 : }
217 :
218 956730921 : ~AutoEnterTypeInference()
219 : {
220 956730921 : cx->compartment->activeAnalysis = oldActiveAnalysis;
221 956730921 : 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 956730921 : if (!cx->compartment->activeInference) {
230 956570377 : TypeCompartment *types = &cx->compartment->types;
231 956570377 : if (types->pendingNukeTypes)
232 0 : types->nukeTypes(cx);
233 956570377 : else if (types->pendingRecompiles)
234 32646 : types->processPendingRecompiles(cx);
235 : }
236 956730921 : }
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 134091 : AutoEnterCompilation(JSContext *cx, JSScript *script, bool constructing, unsigned chunkIndex)
248 134091 : : info(cx->compartment->types.compiledInfo)
249 : {
250 134091 : JS_ASSERT(!info.script);
251 134091 : info.script = script;
252 134091 : info.constructing = constructing;
253 134091 : info.chunkIndex = chunkIndex;
254 134091 : }
255 :
256 134091 : ~AutoEnterCompilation()
257 : {
258 134091 : JS_ASSERT(info.script);
259 134091 : info.script = NULL;
260 134091 : info.constructing = false;
261 134091 : info.chunkIndex = 0;
262 134091 : }
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 1632196 : GetTypeNewObject(JSContext *cx, JSProtoKey key)
282 : {
283 : JSObject *proto;
284 1632196 : if (!js_GetClassPrototype(cx, NULL, key, &proto, NULL))
285 0 : return NULL;
286 1632196 : return proto->getNewType(cx);
287 : }
288 :
289 : /* Get a type object for the immediate allocation site within a native. */
290 : inline TypeObject *
291 150081 : GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
292 : {
293 150081 : if (cx->typeInferenceEnabled()) {
294 : jsbytecode *pc;
295 53876 : JSScript *script = cx->stack.currentScript(&pc);
296 53876 : if (script)
297 53751 : return TypeScript::InitObject(cx, script, pc, key);
298 : }
299 96330 : 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 11978 : MarkIteratorUnknown(JSContext *cx)
308 : {
309 : extern void MarkIteratorUnknownSlow(JSContext *cx);
310 :
311 11978 : if (cx->typeInferenceEnabled())
312 2377 : MarkIteratorUnknownSlow(cx);
313 11978 : }
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 28978716 : 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 28978716 : JSObject *callee = &args.callee();
326 28978716 : if (callee->isFunction()) {
327 28978716 : JSFunction *fun = callee->toFunction();
328 28978716 : if (fun->isInterpreted()) {
329 28978716 : JSScript *script = fun->script();
330 28978716 : if (!script->ensureRanAnalysis(cx, fun->environment()))
331 0 : return;
332 28978716 : if (cx->typeInferenceEnabled())
333 12954220 : TypeMonitorCallSlow(cx, callee, args, constructing);
334 : }
335 : }
336 : }
337 :
338 : inline bool
339 133351572 : TrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
340 : {
341 133351572 : if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties())
342 112740486 : return false;
343 :
344 20611086 : if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(cx, id))
345 6964699 : return false;
346 :
347 13646387 : return true;
348 : }
349 :
350 : /* Add a possible type for a property of obj. */
351 : inline void
352 2702277 : AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, Type type)
353 : {
354 2702277 : if (cx->typeInferenceEnabled())
355 679899 : id = MakeTypeId(cx, id);
356 2702277 : if (TrackPropertyTypes(cx, obj, id))
357 272835 : obj->type()->addPropertyType(cx, id, type);
358 2702277 : }
359 :
360 : inline void
361 114876970 : AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, const Value &value)
362 : {
363 114876970 : if (cx->typeInferenceEnabled())
364 27821924 : id = MakeTypeId(cx, id);
365 114876970 : if (TrackPropertyTypes(cx, obj, id))
366 13097911 : obj->type()->addPropertyType(cx, id, value);
367 114876970 : }
368 :
369 : inline void
370 98623 : AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type)
371 : {
372 98623 : if (cx->typeInferenceEnabled() && !obj->unknownProperties())
373 17714 : obj->addPropertyType(cx, name, type);
374 98623 : }
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 11057790 : MarkTypeObjectFlags(JSContext *cx, JSObject *obj, TypeObjectFlags flags)
386 : {
387 11057790 : if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->hasAllFlags(flags))
388 6720 : obj->type()->setFlags(cx, flags);
389 11057790 : }
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 6631537 : MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
399 : bool markSetsUnknown = false)
400 : {
401 6631537 : if (cx->typeInferenceEnabled()) {
402 2242382 : if (!obj->unknownProperties())
403 13465 : obj->markUnknown(cx);
404 2242382 : if (markSetsUnknown && !(obj->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
405 165 : cx->compartment->types.markSetsUnknown(cx, obj);
406 : }
407 6631537 : }
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 15772325 : MarkTypePropertyConfigured(JSContext *cx, JSObject *obj, jsid id)
415 : {
416 15772325 : if (cx->typeInferenceEnabled())
417 6794159 : id = MakeTypeId(cx, id);
418 15772325 : if (TrackPropertyTypes(cx, obj, id))
419 275641 : obj->type()->markPropertyConfigured(cx, id);
420 15772325 : }
421 :
422 : /* Mark a state change on a particular object. */
423 : inline void
424 38570 : MarkObjectStateChange(JSContext *cx, JSObject *obj)
425 : {
426 38570 : if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties())
427 12778 : obj->type()->markStateChange(cx);
428 38570 : }
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 5767 : FixArrayType(JSContext *cx, JSObject *obj)
437 : {
438 5767 : if (cx->typeInferenceEnabled())
439 1775 : cx->compartment->types.fixArrayType(cx, obj);
440 5767 : }
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 1962019 : UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
455 : {
456 1968496 : return fp->isConstructing() && cx->typeInferenceEnabled() &&
457 346 : fp->prev() && fp->prev()->isScriptFrame() &&
458 1968842 : UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp));
459 : }
460 :
461 : /////////////////////////////////////////////////////////////////////
462 : // Script interface functions
463 : /////////////////////////////////////////////////////////////////////
464 :
465 : inline
466 470919 : TypeScript::TypeScript()
467 : {
468 470919 : this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE;
469 470919 : }
470 :
471 : /* static */ inline unsigned
472 199428 : TypeScript::NumTypeSets(JSScript *script)
473 : {
474 199428 : return script->nTypeSets + analyze::TotalSlots(script);
475 : }
476 :
477 : /* static */ inline TypeSet *
478 131434 : TypeScript::ReturnTypes(JSScript *script)
479 : {
480 131434 : return script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot();
481 : }
482 :
483 : /* static */ inline TypeSet *
484 29306782 : TypeScript::ThisTypes(JSScript *script)
485 : {
486 29306782 : 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 45054078 : TypeScript::ArgTypes(JSScript *script, unsigned i)
497 : {
498 45054078 : JS_ASSERT(i < script->function()->nargs);
499 45054078 : return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
500 : }
501 :
502 : /* static */ inline TypeSet *
503 109519 : TypeScript::LocalTypes(JSScript *script, unsigned i)
504 : {
505 109519 : JS_ASSERT(i < script->nfixed);
506 109519 : return script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i);
507 : }
508 :
509 : /* static */ inline TypeSet *
510 337175 : TypeScript::SlotTypes(JSScript *script, unsigned slot)
511 : {
512 337175 : JS_ASSERT(slot < js::analyze::TotalSlots(script));
513 337175 : return script->types->typeArray() + script->nTypeSets + slot;
514 : }
515 :
516 : /* static */ inline TypeObject *
517 8027 : TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
518 : {
519 : JSObject *proto;
520 8027 : if (!js_GetClassPrototype(cx, script->global(), key, &proto, NULL))
521 0 : return NULL;
522 8027 : 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 1282302 : AllocationSiteKey() { PodZero(this); }
534 :
535 : typedef AllocationSiteKey Lookup;
536 :
537 1021482 : static inline uint32_t hash(AllocationSiteKey key) {
538 1021482 : return uint32_t(size_t(key.script->code + key.offset)) ^ key.kind;
539 : }
540 :
541 997436 : static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
542 997436 : return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
543 : }
544 : };
545 :
546 : /* static */ inline TypeObject *
547 2547824 : 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 2547824 : uint32_t offset = pc - script->code;
551 :
552 2547824 : if (!cx->typeInferenceEnabled() || !script->hasGlobal() || offset >= AllocationSiteKey::OFFSET_LIMIT)
553 1535866 : return GetTypeNewObject(cx, kind);
554 :
555 1011958 : AllocationSiteKey key;
556 1011958 : key.script = script;
557 1011958 : key.offset = offset;
558 1011958 : key.kind = kind;
559 :
560 1011958 : if (!cx->compartment->types.allocationSiteTable)
561 5008 : return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
562 :
563 1006950 : AllocationSiteTable::Ptr p = cx->compartment->types.allocationSiteTable->lookup(key);
564 :
565 1006950 : if (p)
566 997426 : return p->value;
567 9524 : return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
568 : }
569 :
570 : /* static */ inline void
571 207366048 : TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
572 : {
573 207366048 : if (cx->typeInferenceEnabled())
574 76761967 : TypeMonitorResult(cx, script, pc, rval);
575 207366048 : }
576 :
577 : /* static */ inline void
578 1400830 : TypeScript::MonitorOverflow(JSContext *cx, JSScript *script, jsbytecode *pc)
579 : {
580 1400830 : if (cx->typeInferenceEnabled())
581 530616 : TypeDynamicResult(cx, script, pc, Type::DoubleType());
582 1400830 : }
583 :
584 : /* static */ inline void
585 544207 : TypeScript::MonitorString(JSContext *cx, JSScript *script, jsbytecode *pc)
586 : {
587 544207 : if (cx->typeInferenceEnabled())
588 282970 : TypeDynamicResult(cx, script, pc, Type::StringType());
589 544207 : }
590 :
591 : /* static */ inline void
592 3237 : TypeScript::MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc)
593 : {
594 3237 : if (cx->typeInferenceEnabled())
595 1570 : TypeDynamicResult(cx, script, pc, Type::UnknownType());
596 3237 : }
597 :
598 : /* static */ inline void
599 5122251 : TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc)
600 : {
601 5122251 : *script = cx->fp()->script();
602 5122251 : *pc = cx->regs().pc;
603 5122251 : }
604 :
605 : /* static */ inline void
606 1001716 : TypeScript::MonitorOverflow(JSContext *cx)
607 : {
608 : JSScript *script;
609 : jsbytecode *pc;
610 1001716 : GetPcScript(cx, &script, &pc);
611 1001716 : MonitorOverflow(cx, script, pc);
612 1001716 : }
613 :
614 : /* static */ inline void
615 149802 : TypeScript::MonitorString(JSContext *cx)
616 : {
617 : JSScript *script;
618 : jsbytecode *pc;
619 149802 : GetPcScript(cx, &script, &pc);
620 149802 : MonitorString(cx, script, pc);
621 149802 : }
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 28933522 : TypeScript::MonitorAssign(JSContext *cx, JSObject *obj, jsid id)
643 : {
644 28933522 : 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 6091035 : if (js_IdIsIndex(id, &i))
655 5688149 : return;
656 402886 : MarkTypeObjectUnknownProperties(cx, obj->type());
657 : }
658 : }
659 :
660 : /* static */ inline void
661 12507347 : TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
662 : {
663 12507347 : if (!cx->typeInferenceEnabled())
664 0 : return;
665 12507347 : JS_ASSERT(script->types);
666 :
667 : /* Analyze the script regardless if -a was used. */
668 12507347 : bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS);
669 :
670 12507347 : if (!ThisTypes(script)->hasType(type) || analyze) {
671 8513014 : AutoEnterTypeInference enter(cx);
672 :
673 : InferSpew(ISpewOps, "externalType: setThis #%u: %s",
674 4256507 : script->id(), TypeString(type));
675 4256507 : ThisTypes(script)->addType(cx, type);
676 :
677 4256507 : if (analyze && script->types->hasScope())
678 4206469 : script->ensureRanInference(cx);
679 : }
680 : }
681 :
682 : /* static */ inline void
683 11821918 : TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
684 : {
685 11821918 : if (cx->typeInferenceEnabled())
686 11726715 : SetThis(cx, script, GetValueType(cx, value));
687 11821918 : }
688 :
689 : /* static */ inline void
690 10729 : TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type)
691 : {
692 10729 : if (!cx->typeInferenceEnabled())
693 0 : return;
694 10729 : JS_ASSERT(script->types);
695 :
696 10729 : 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 37339 : TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value)
707 : {
708 37339 : if (cx->typeInferenceEnabled()) {
709 10729 : Type type = GetValueType(cx, value);
710 10729 : SetLocal(cx, script, local, type);
711 : }
712 37339 : }
713 :
714 : /* static */ inline void
715 20682835 : TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
716 : {
717 20682835 : if (!cx->typeInferenceEnabled())
718 0 : return;
719 20682835 : JS_ASSERT(script->types);
720 :
721 20682835 : 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 20685872 : TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
732 : {
733 20685872 : if (cx->typeInferenceEnabled()) {
734 20682835 : Type type = GetValueType(cx, value);
735 20682835 : SetArgument(cx, script, arg, type);
736 : }
737 20685872 : }
738 :
739 : void
740 1954054 : TypeScript::trace(JSTracer *trc)
741 : {
742 1954054 : if (hasScope() && global)
743 66908 : gc::MarkObject(trc, &global, "script_global");
744 :
745 : /* Note: nesting does not keep anything alive. */
746 1954054 : }
747 :
748 : /////////////////////////////////////////////////////////////////////
749 : // TypeCompartment
750 : /////////////////////////////////////////////////////////////////////
751 :
752 : inline JSCompartment *
753 164383 : TypeCompartment::compartment()
754 : {
755 164383 : return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
756 : }
757 :
758 : inline void
759 3123940 : TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, Type type)
760 : {
761 3123940 : JS_ASSERT(this == &cx->compartment->types);
762 3123940 : JS_ASSERT(!cx->runtime->gcRunning);
763 :
764 : InferSpew(ISpewOps, "pending: %sC%p%s %s",
765 : InferSpewColor(constraint), constraint, InferSpewColorReset(),
766 3123940 : TypeString(type));
767 :
768 3123940 : if ((pendingCount == pendingCapacity) && !growPendingArray(cx))
769 0 : return;
770 :
771 3123940 : PendingWork &pending = pendingArray[pendingCount++];
772 3123940 : pending.constraint = constraint;
773 3123940 : pending.source = source;
774 3123940 : pending.type = type;
775 : }
776 :
777 : inline void
778 4588579 : TypeCompartment::resolvePending(JSContext *cx)
779 : {
780 4588579 : JS_ASSERT(this == &cx->compartment->types);
781 :
782 4588579 : if (resolving) {
783 : /* There is an active call further up resolving the worklist. */
784 1193329 : return;
785 : }
786 :
787 3395250 : resolving = true;
788 :
789 : /* Handle all pending type registrations. */
790 9914440 : while (pendingCount) {
791 3123940 : const PendingWork &pending = pendingArray[--pendingCount];
792 : InferSpew(ISpewOps, "resolve: %sC%p%s %s",
793 : InferSpewColor(pending.constraint), pending.constraint,
794 3123940 : InferSpewColorReset(), TypeString(pending.type));
795 3123940 : pending.constraint->newType(cx, pending.source, pending.type);
796 : }
797 :
798 3395250 : 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 4294124 : HashSetCapacity(unsigned count)
818 : {
819 4294124 : JS_ASSERT(count >= 2);
820 :
821 4294124 : if (count <= SET_ARRAY_SIZE)
822 17220 : return SET_ARRAY_SIZE;
823 :
824 : unsigned log2;
825 4276904 : JS_FLOOR_LOG2(log2, count);
826 4276904 : 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 2623479 : HashKey(T v)
833 : {
834 2623479 : uint32_t nv = KEY::keyBits(v);
835 :
836 2623479 : uint32_t hash = 84696351 ^ (nv & 0xff);
837 2623479 : hash = (hash * 16777619) ^ ((nv >> 8) & 0xff);
838 2623479 : hash = (hash * 16777619) ^ ((nv >> 16) & 0xff);
839 2623479 : 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 853802 : HashSetInsertTry(JSCompartment *compartment, U **&values, unsigned &count, T key)
849 : {
850 853802 : unsigned capacity = HashSetCapacity(count);
851 853802 : unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
852 :
853 : /* Whether we are converting from a fixed array to hashtable. */
854 853802 : bool converting = (count == SET_ARRAY_SIZE);
855 :
856 853802 : if (!converting) {
857 2256352 : while (values[insertpos] != NULL) {
858 907782 : if (KEY::getKey(values[insertpos]) == key)
859 340016 : return &values[insertpos];
860 567766 : insertpos = (insertpos + 1) & (capacity - 1);
861 : }
862 : }
863 :
864 513786 : count++;
865 513786 : unsigned newCapacity = HashSetCapacity(count);
866 :
867 513786 : if (newCapacity == capacity) {
868 488274 : JS_ASSERT(!converting);
869 488274 : return &values[insertpos];
870 : }
871 :
872 25512 : U **newValues = compartment->typeLifoAlloc.newArray<U*>(newCapacity);
873 25512 : if (!newValues)
874 0 : return NULL;
875 25512 : PodZero(newValues, newCapacity);
876 :
877 1523600 : for (unsigned i = 0; i < capacity; i++) {
878 1498088 : if (values[i]) {
879 771077 : unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
880 1674095 : while (newValues[pos] != NULL)
881 131941 : pos = (pos + 1) & (newCapacity - 1);
882 771077 : newValues[pos] = values[i];
883 : }
884 : }
885 :
886 25512 : values = newValues;
887 :
888 25512 : insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
889 63597 : while (values[insertpos] != NULL)
890 12573 : insertpos = (insertpos + 1) & (newCapacity - 1);
891 25512 : 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 16983250 : HashSetInsert(JSCompartment *compartment, U **&values, unsigned &count, T key)
901 : {
902 16983250 : if (count == 0) {
903 656712 : JS_ASSERT(values == NULL);
904 656712 : count++;
905 656712 : return (U **) &values;
906 : }
907 :
908 16326538 : if (count == 1) {
909 10990045 : U *oldData = (U*) values;
910 10990045 : if (KEY::getKey(oldData) == key)
911 10934266 : return (U **) &values;
912 :
913 55779 : values = compartment->typeLifoAlloc.newArray<U*>(SET_ARRAY_SIZE);
914 55779 : if (!values) {
915 0 : values = (U **) oldData;
916 0 : return NULL;
917 : }
918 55779 : PodZero(values, SET_ARRAY_SIZE);
919 55779 : count++;
920 :
921 55779 : values[0] = oldData;
922 55779 : return &values[1];
923 : }
924 :
925 5336493 : if (count <= SET_ARRAY_SIZE) {
926 12591666 : for (unsigned i = 0; i < count; i++) {
927 12473378 : if (KEY::getKey(values[i]) == key)
928 4373912 : return &values[i];
929 : }
930 :
931 118288 : if (count < SET_ARRAY_SIZE) {
932 108779 : count++;
933 108779 : return &values[count - 1];
934 : }
935 : }
936 :
937 853802 : 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 49749557 : HashSetLookup(U **values, unsigned count, T key)
944 : {
945 49749557 : if (count == 0)
946 7543821 : return NULL;
947 :
948 42205736 : if (count == 1)
949 34202415 : return (KEY::getKey((U *) values) == key) ? (U *) values : NULL;
950 :
951 8003321 : if (count <= SET_ARRAY_SIZE) {
952 13154135 : for (unsigned i = 0; i < count; i++) {
953 12905984 : if (KEY::getKey(values[i]) == key)
954 6782082 : return values[i];
955 : }
956 248151 : return NULL;
957 : }
958 :
959 973088 : unsigned capacity = HashSetCapacity(count);
960 973088 : unsigned pos = HashKey<T,KEY>(key) & (capacity - 1);
961 :
962 2726787 : while (values[pos] != NULL) {
963 1718084 : if (KEY::getKey(values[pos]) == key)
964 937473 : return values[pos];
965 780611 : pos = (pos + 1) & (capacity - 1);
966 : }
967 :
968 35615 : return NULL;
969 : }
970 :
971 : inline bool
972 172540095 : TypeSet::hasType(Type type)
973 : {
974 172540095 : if (unknown())
975 13692890 : return true;
976 :
977 158847205 : if (type.isUnknown()) {
978 54924 : return false;
979 158792281 : } else if (type.isPrimitive()) {
980 116203354 : return !!(flags & PrimitiveTypeFlag(type.primitive()));
981 42588927 : } else if (type.isAnyObject()) {
982 346808 : return !!(flags & TYPE_FLAG_ANYOBJECT);
983 : } else {
984 42242119 : return !!(flags & TYPE_FLAG_ANYOBJECT) ||
985 : HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
986 42242119 : (objectSet, baseObjectCount(), type.objectKey()) != NULL;
987 : }
988 : }
989 :
990 : inline void
991 1287773 : TypeSet::setBaseObjectCount(uint32_t count)
992 : {
993 1287773 : JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT);
994 : flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK)
995 1287773 : | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
996 1287773 : }
997 :
998 : inline void
999 136081 : TypeSet::clearObjects()
1000 : {
1001 136081 : setBaseObjectCount(0);
1002 136081 : objectSet = NULL;
1003 136081 : }
1004 :
1005 : inline void
1006 6335990 : TypeSet::addType(JSContext *cx, Type type)
1007 : {
1008 6335990 : JS_ASSERT(cx->compartment->activeInference);
1009 :
1010 6335990 : if (unknown())
1011 2514138 : return;
1012 :
1013 3821852 : if (type.isUnknown()) {
1014 72463 : flags |= TYPE_FLAG_BASE_MASK;
1015 72463 : clearObjects();
1016 72463 : JS_ASSERT(unknown());
1017 3749389 : } else if (type.isPrimitive()) {
1018 1794279 : TypeFlags flag = PrimitiveTypeFlag(type.primitive());
1019 1794279 : if (flags & flag)
1020 764526 : return;
1021 :
1022 : /* If we add float to a type set it is also considered to contain int. */
1023 1029753 : if (flag == TYPE_FLAG_DOUBLE)
1024 49054 : flag |= TYPE_FLAG_INT32;
1025 :
1026 1029753 : flags |= flag;
1027 : } else {
1028 1955110 : if (flags & TYPE_FLAG_ANYOBJECT)
1029 5108 : return;
1030 1950002 : if (type.isAnyObject())
1031 26439 : goto unknownObject;
1032 1923563 : uint32_t objectCount = baseObjectCount();
1033 1923563 : TypeObjectKey *object = type.objectKey();
1034 : TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
1035 1923563 : (cx->compartment, objectSet, objectCount, object);
1036 1923563 : if (!pentry) {
1037 0 : cx->compartment->types.setPendingNukeTypes(cx);
1038 0 : return;
1039 : }
1040 1923563 : if (*pentry)
1041 1042561 : return;
1042 881002 : *pentry = object;
1043 :
1044 881002 : setBaseObjectCount(objectCount);
1045 :
1046 881002 : if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
1047 15 : goto unknownObject;
1048 :
1049 880987 : if (type.isTypeObject()) {
1050 291534 : TypeObject *nobject = type.typeObject();
1051 291534 : JS_ASSERT(!nobject->singleton);
1052 291534 : if (nobject->unknownProperties())
1053 26648 : goto unknownObject;
1054 264886 : if (objectCount > 1) {
1055 34299 : nobject->contribution += (objectCount - 1) * (objectCount - 1);
1056 34299 : if (nobject->contribution >= TypeObject::CONTRIBUTION_LIMIT) {
1057 : InferSpew(ISpewOps, "limitUnknown: %sT%p%s",
1058 392 : InferSpewColor(this), this, InferSpewColorReset());
1059 392 : goto unknownObject;
1060 : }
1061 : }
1062 : }
1063 : }
1064 :
1065 : if (false) {
1066 : unknownObject:
1067 53494 : type = Type::AnyObjectType();
1068 53494 : flags |= TYPE_FLAG_ANYOBJECT;
1069 53494 : clearObjects();
1070 : }
1071 :
1072 : InferSpew(ISpewOps, "addType: %sT%p%s %s",
1073 : InferSpewColor(this), this, InferSpewColorReset(),
1074 2009657 : TypeString(type));
1075 :
1076 : /* Propagate the type to all constraints. */
1077 2009657 : TypeConstraint *constraint = constraintList;
1078 5767538 : while (constraint) {
1079 1748224 : cx->compartment->types.addPending(cx, constraint, this, type);
1080 1748224 : constraint = constraint->next;
1081 : }
1082 :
1083 2009657 : cx->compartment->types.resolvePending(cx);
1084 : }
1085 :
1086 : inline void
1087 14064721 : TypeSet::setOwnProperty(JSContext *cx, bool configured)
1088 : {
1089 14064721 : TypeFlags nflags = TYPE_FLAG_OWN_PROPERTY | (configured ? TYPE_FLAG_CONFIGURED_PROPERTY : 0);
1090 :
1091 14064721 : if ((flags & nflags) == nflags)
1092 13981930 : return;
1093 :
1094 82791 : flags |= nflags;
1095 :
1096 : /* Propagate the change to all constraints. */
1097 82791 : TypeConstraint *constraint = constraintList;
1098 172484 : while (constraint) {
1099 6902 : constraint->newPropertyState(cx, this);
1100 6902 : constraint = constraint->next;
1101 : }
1102 : }
1103 :
1104 : inline unsigned
1105 6390705 : TypeSet::getObjectCount()
1106 : {
1107 6390705 : JS_ASSERT(!unknownObject());
1108 6390705 : uint32_t count = baseObjectCount();
1109 6390705 : if (count > SET_ARRAY_SIZE)
1110 1940082 : return HashSetCapacity(count);
1111 4450623 : return count;
1112 : }
1113 :
1114 : inline TypeObjectKey *
1115 2621547 : TypeSet::getObject(unsigned i)
1116 : {
1117 2621547 : JS_ASSERT(i < getObjectCount());
1118 2621547 : if (baseObjectCount() == 1) {
1119 843625 : JS_ASSERT(i == 0);
1120 843625 : return (TypeObjectKey *) objectSet;
1121 : }
1122 1777922 : return objectSet[i];
1123 : }
1124 :
1125 : inline JSObject *
1126 760618 : TypeSet::getSingleObject(unsigned i)
1127 : {
1128 760618 : TypeObjectKey *key = getObject(i);
1129 760618 : return (uintptr_t(key) & 1) ? (JSObject *)(uintptr_t(key) ^ 1) : NULL;
1130 : }
1131 :
1132 : inline TypeObject *
1133 840785 : TypeSet::getTypeObject(unsigned i)
1134 : {
1135 840785 : TypeObjectKey *key = getObject(i);
1136 840785 : return (key && !(uintptr_t(key) & 1)) ? (TypeObject *) key : NULL;
1137 : }
1138 :
1139 : /////////////////////////////////////////////////////////////////////
1140 : // TypeCallsite
1141 : /////////////////////////////////////////////////////////////////////
1142 :
1143 : inline
1144 56863 : TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
1145 : bool isNew, unsigned argumentCount)
1146 : : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
1147 56863 : thisTypes(NULL), returnTypes(NULL)
1148 : {
1149 : /* Caller must check for failure. */
1150 56863 : argumentTypes = cx->typeLifoAlloc().newArray<TypeSet*>(argumentCount);
1151 56863 : }
1152 :
1153 : /////////////////////////////////////////////////////////////////////
1154 : // TypeObject
1155 : /////////////////////////////////////////////////////////////////////
1156 :
1157 671141 : inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
1158 : {
1159 671141 : PodZero(this);
1160 :
1161 : /* Inner objects may not appear on prototype chains. */
1162 671141 : JS_ASSERT_IF(proto, !proto->getClass()->ext.outerObject);
1163 :
1164 671141 : this->proto = proto;
1165 :
1166 671141 : if (function)
1167 13763 : flags |= OBJECT_FLAG_FUNCTION;
1168 671141 : if (unknown)
1169 444465 : flags |= OBJECT_FLAG_UNKNOWN_MASK;
1170 :
1171 671141 : InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
1172 671141 : }
1173 :
1174 : inline uint32_t
1175 31643695 : TypeObject::basePropertyCount() const
1176 : {
1177 31643695 : return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
1178 : }
1179 :
1180 : inline void
1181 342694 : TypeObject::setBasePropertyCount(uint32_t count)
1182 : {
1183 342694 : JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
1184 : flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
1185 342694 : | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
1186 342694 : }
1187 :
1188 : inline TypeSet *
1189 14751310 : TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
1190 : {
1191 14751310 : JS_ASSERT(cx->compartment->activeInference);
1192 14751310 : JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
1193 14751310 : JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
1194 14751310 : JS_ASSERT(!unknownProperties());
1195 :
1196 14751310 : uint32_t propertyCount = basePropertyCount();
1197 : Property **pprop = HashSetInsert<jsid,Property,Property>
1198 14751310 : (cx->compartment, propertySet, propertyCount, id);
1199 14751310 : if (!pprop) {
1200 0 : cx->compartment->types.setPendingNukeTypes(cx);
1201 0 : return NULL;
1202 : }
1203 :
1204 14751310 : if (!*pprop) {
1205 145677 : setBasePropertyCount(propertyCount);
1206 145677 : if (!addProperty(cx, id, pprop))
1207 0 : return NULL;
1208 145677 : 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 14751310 : TypeSet *types = &(*pprop)->types;
1217 :
1218 14751310 : if (assign)
1219 13741533 : types->setOwnProperty(cx, false);
1220 :
1221 14751310 : return types;
1222 : }
1223 :
1224 : inline TypeSet *
1225 8428130 : TypeObject::maybeGetProperty(JSContext *cx, jsid id)
1226 : {
1227 8428130 : JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
1228 8428130 : JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
1229 8428130 : JS_ASSERT(!unknownProperties());
1230 :
1231 : Property *prop = HashSetLookup<jsid,Property,Property>
1232 8428130 : (propertySet, basePropertyCount(), id);
1233 :
1234 8428130 : return prop ? &prop->types : NULL;
1235 : }
1236 :
1237 : inline unsigned
1238 2452812 : TypeObject::getPropertyCount()
1239 : {
1240 2452812 : uint32_t count = basePropertyCount();
1241 2452812 : if (count > SET_ARRAY_SIZE)
1242 2354 : return HashSetCapacity(count);
1243 2450458 : return count;
1244 : }
1245 :
1246 : inline Property *
1247 11165 : TypeObject::getProperty(unsigned i)
1248 : {
1249 11165 : JS_ASSERT(i < getPropertyCount());
1250 11165 : if (basePropertyCount() == 1) {
1251 3371 : JS_ASSERT(i == 0);
1252 3371 : return (Property *) propertySet;
1253 : }
1254 7794 : return propertySet[i];
1255 : }
1256 :
1257 : inline void
1258 248419 : TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
1259 : {
1260 248419 : TypeObjectFlags flags = 0;
1261 :
1262 248419 : switch (key) {
1263 : case JSProto_Function:
1264 13763 : JS_ASSERT(isFunction());
1265 : /* FALLTHROUGH */
1266 :
1267 : case JSProto_Object:
1268 : flags = OBJECT_FLAG_NON_DENSE_ARRAY
1269 : | OBJECT_FLAG_NON_PACKED_ARRAY
1270 239431 : | OBJECT_FLAG_NON_TYPED_ARRAY;
1271 239431 : break;
1272 :
1273 : case JSProto_Array:
1274 8031 : flags = OBJECT_FLAG_NON_TYPED_ARRAY;
1275 8031 : 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 248419 : if (!hasAllFlags(flags))
1294 151612 : setFlags(cx, flags);
1295 248419 : }
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 13456089 : TypeObject::writeBarrierPre(TypeObject *type)
1309 : {
1310 : #ifdef JSGC_INCREMENTAL
1311 13456089 : if (!type)
1312 1868411 : return;
1313 :
1314 11587678 : JSCompartment *comp = type->compartment();
1315 11587678 : 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 23077179 : TypeObject::writeBarrierPost(TypeObject *type, void *addr)
1325 : {
1326 23077179 : }
1327 :
1328 : inline void
1329 57306806 : TypeObject::readBarrier(TypeObject *type)
1330 : {
1331 : #ifdef JSGC_INCREMENTAL
1332 57306806 : JSCompartment *comp = type->compartment();
1333 57306806 : if (comp->needsBarrier()) {
1334 14722 : TypeObject *tmp = type;
1335 14722 : MarkTypeObjectUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
1336 14722 : JS_ASSERT(tmp == type);
1337 : }
1338 : #endif
1339 57306806 : }
1340 :
1341 : inline void
1342 620 : TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
1343 : {
1344 : #ifdef JSGC_INCREMENTAL
1345 620 : if (!newScript)
1346 580 : 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 620 : TypeNewScript::writeBarrierPost(TypeNewScript *newScript, void *addr)
1358 : {
1359 620 : }
1360 :
1361 : inline
1362 145677 : Property::Property(jsid id)
1363 145677 : : id(id)
1364 : {
1365 145677 : }
1366 :
1367 : inline
1368 9560 : Property::Property(const Property &o)
1369 9560 : : id(o.id.get()), types(o.types)
1370 : {
1371 9560 : }
1372 :
1373 : } } /* namespace js::types */
1374 :
1375 : inline bool
1376 110786089 : JSScript::ensureHasTypes(JSContext *cx)
1377 : {
1378 110786089 : return types || makeTypes(cx);
1379 : }
1380 :
1381 : inline bool
1382 110692410 : JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope)
1383 : {
1384 110692410 : JSScript *self = this;
1385 :
1386 110692410 : if (!self->ensureHasTypes(cx))
1387 0 : return false;
1388 110692410 : if (!self->types->hasScope()) {
1389 941718 : js::CheckRoot root(cx, &self);
1390 941718 : js::RootObject objRoot(cx, &scope);
1391 470859 : if (!js::types::TypeScript::SetScope(cx, self, scope))
1392 0 : return false;
1393 : }
1394 110692410 : if (!self->hasAnalysis() && !self->makeAnalysis(cx))
1395 0 : return false;
1396 110692410 : JS_ASSERT(self->analysis()->ranBytecode());
1397 110692410 : return true;
1398 : }
1399 :
1400 : inline bool
1401 4464633 : JSScript::ensureRanInference(JSContext *cx)
1402 : {
1403 4464633 : if (!ensureRanAnalysis(cx, NULL))
1404 0 : return false;
1405 4464633 : if (!analysis()->ranInference()) {
1406 77564 : js::types::AutoEnterTypeInference enter(cx);
1407 38782 : analysis()->analyzeTypes(cx);
1408 : }
1409 4464633 : return !analysis()->OOM() &&
1410 4464633 : !cx->compartment->types.pendingNukeTypes;
1411 : }
1412 :
1413 : inline bool
1414 2060068643 : JSScript::hasAnalysis()
1415 : {
1416 2060068643 : return types && types->analysis;
1417 : }
1418 :
1419 : inline js::analyze::ScriptAnalysis *
1420 1079697474 : JSScript::analysis()
1421 : {
1422 1079697474 : JS_ASSERT(hasAnalysis());
1423 1079697474 : return types->analysis;
1424 : }
1425 :
1426 : inline void
1427 8105181 : JSScript::clearAnalysis()
1428 : {
1429 8105181 : if (types)
1430 2401904 : types->analysis = NULL;
1431 8105181 : }
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 3072009 : JSCompartment::getEmptyType(JSContext *cx)
1443 : {
1444 3072009 : if (!emptyTypeObject)
1445 25666 : emptyTypeObject = types.newTypeObject(cx, NULL, JSProto_Object, NULL, true);
1446 3072009 : return emptyTypeObject;
1447 : }
1448 :
1449 : #endif // jsinferinlines_h___
|