|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* Inline members for javascript type inference. */ |
|
8 |
|
9 #ifndef jsinferinlines_h |
|
10 #define jsinferinlines_h |
|
11 |
|
12 #include "jsinfer.h" |
|
13 |
|
14 #include "mozilla/PodOperations.h" |
|
15 |
|
16 #include "jsanalyze.h" |
|
17 |
|
18 #include "vm/ArrayObject.h" |
|
19 #include "vm/BooleanObject.h" |
|
20 #include "vm/NumberObject.h" |
|
21 #include "vm/SharedArrayObject.h" |
|
22 #include "vm/StringObject.h" |
|
23 #include "vm/TypedArrayObject.h" |
|
24 |
|
25 #include "jscntxtinlines.h" |
|
26 |
|
27 #include "jit/ExecutionMode-inl.h" |
|
28 |
|
29 namespace js { |
|
30 namespace types { |
|
31 |
|
32 ///////////////////////////////////////////////////////////////////// |
|
33 // CompilerOutput & RecompileInfo |
|
34 ///////////////////////////////////////////////////////////////////// |
|
35 |
|
36 inline jit::IonScript * |
|
37 CompilerOutput::ion() const |
|
38 { |
|
39 #ifdef JS_ION |
|
40 // Note: If type constraints are generated before compilation has finished |
|
41 // (i.e. after IonBuilder but before CodeGenerator::link) then a valid |
|
42 // CompilerOutput may not yet have an associated IonScript. |
|
43 JS_ASSERT(isValid()); |
|
44 jit::IonScript *ion = jit::GetIonScript(script(), mode()); |
|
45 JS_ASSERT(ion != ION_COMPILING_SCRIPT); |
|
46 return ion; |
|
47 #endif |
|
48 MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput"); |
|
49 } |
|
50 |
|
51 inline CompilerOutput* |
|
52 RecompileInfo::compilerOutput(TypeZone &types) const |
|
53 { |
|
54 if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length()) |
|
55 return nullptr; |
|
56 return &(*types.compilerOutputs)[outputIndex]; |
|
57 } |
|
58 |
|
59 inline CompilerOutput* |
|
60 RecompileInfo::compilerOutput(JSContext *cx) const |
|
61 { |
|
62 return compilerOutput(cx->zone()->types); |
|
63 } |
|
64 |
|
65 inline bool |
|
66 RecompileInfo::shouldSweep(TypeZone &types) |
|
67 { |
|
68 CompilerOutput *output = compilerOutput(types); |
|
69 if (!output || !output->isValid()) |
|
70 return true; |
|
71 |
|
72 // Update this info for the output's new index in the zone's compiler outputs. |
|
73 outputIndex = output->sweepIndex(); |
|
74 return false; |
|
75 } |
|
76 |
|
77 ///////////////////////////////////////////////////////////////////// |
|
78 // Types |
|
79 ///////////////////////////////////////////////////////////////////// |
|
80 |
|
81 /* static */ inline Type |
|
82 Type::ObjectType(JSObject *obj) |
|
83 { |
|
84 if (obj->hasSingletonType()) |
|
85 return Type(uintptr_t(obj) | 1); |
|
86 return Type(uintptr_t(obj->type())); |
|
87 } |
|
88 |
|
89 /* static */ inline Type |
|
90 Type::ObjectType(TypeObject *obj) |
|
91 { |
|
92 if (obj->singleton()) |
|
93 return Type(uintptr_t(obj->singleton()) | 1); |
|
94 return Type(uintptr_t(obj)); |
|
95 } |
|
96 |
|
97 /* static */ inline Type |
|
98 Type::ObjectType(TypeObjectKey *obj) |
|
99 { |
|
100 return Type(uintptr_t(obj)); |
|
101 } |
|
102 |
|
103 inline Type |
|
104 GetValueType(const Value &val) |
|
105 { |
|
106 if (val.isDouble()) |
|
107 return Type::DoubleType(); |
|
108 if (val.isObject()) |
|
109 return Type::ObjectType(&val.toObject()); |
|
110 return Type::PrimitiveType(val.extractNonDoubleType()); |
|
111 } |
|
112 |
|
113 inline Type |
|
114 GetMaybeOptimizedOutValueType(const Value &val) |
|
115 { |
|
116 if (val.isMagic() && val.whyMagic() == JS_OPTIMIZED_OUT) |
|
117 return Type::UnknownType(); |
|
118 return GetValueType(val); |
|
119 } |
|
120 |
|
121 inline TypeFlags |
|
122 PrimitiveTypeFlag(JSValueType type) |
|
123 { |
|
124 switch (type) { |
|
125 case JSVAL_TYPE_UNDEFINED: |
|
126 return TYPE_FLAG_UNDEFINED; |
|
127 case JSVAL_TYPE_NULL: |
|
128 return TYPE_FLAG_NULL; |
|
129 case JSVAL_TYPE_BOOLEAN: |
|
130 return TYPE_FLAG_BOOLEAN; |
|
131 case JSVAL_TYPE_INT32: |
|
132 return TYPE_FLAG_INT32; |
|
133 case JSVAL_TYPE_DOUBLE: |
|
134 return TYPE_FLAG_DOUBLE; |
|
135 case JSVAL_TYPE_STRING: |
|
136 return TYPE_FLAG_STRING; |
|
137 case JSVAL_TYPE_MAGIC: |
|
138 return TYPE_FLAG_LAZYARGS; |
|
139 default: |
|
140 MOZ_ASSUME_UNREACHABLE("Bad type"); |
|
141 } |
|
142 } |
|
143 |
|
144 inline JSValueType |
|
145 TypeFlagPrimitive(TypeFlags flags) |
|
146 { |
|
147 switch (flags) { |
|
148 case TYPE_FLAG_UNDEFINED: |
|
149 return JSVAL_TYPE_UNDEFINED; |
|
150 case TYPE_FLAG_NULL: |
|
151 return JSVAL_TYPE_NULL; |
|
152 case TYPE_FLAG_BOOLEAN: |
|
153 return JSVAL_TYPE_BOOLEAN; |
|
154 case TYPE_FLAG_INT32: |
|
155 return JSVAL_TYPE_INT32; |
|
156 case TYPE_FLAG_DOUBLE: |
|
157 return JSVAL_TYPE_DOUBLE; |
|
158 case TYPE_FLAG_STRING: |
|
159 return JSVAL_TYPE_STRING; |
|
160 case TYPE_FLAG_LAZYARGS: |
|
161 return JSVAL_TYPE_MAGIC; |
|
162 default: |
|
163 MOZ_ASSUME_UNREACHABLE("Bad type"); |
|
164 } |
|
165 } |
|
166 |
|
167 /* |
|
168 * Get the canonical representation of an id to use when doing inference. This |
|
169 * maintains the constraint that if two different jsids map to the same property |
|
170 * in JS (e.g. 3 and "3"), they have the same type representation. |
|
171 */ |
|
172 inline jsid |
|
173 IdToTypeId(jsid id) |
|
174 { |
|
175 JS_ASSERT(!JSID_IS_EMPTY(id)); |
|
176 |
|
177 /* |
|
178 * All integers must map to the aggregate property for index types, including |
|
179 * negative integers. |
|
180 */ |
|
181 if (JSID_IS_INT(id)) |
|
182 return JSID_VOID; |
|
183 |
|
184 /* |
|
185 * Check for numeric strings, as in js_StringIsIndex, but allow negative |
|
186 * and overflowing integers. |
|
187 */ |
|
188 if (JSID_IS_STRING(id)) { |
|
189 JSAtom *atom = JSID_TO_ATOM(id); |
|
190 JS::TwoByteChars cp = atom->range(); |
|
191 if (cp.length() > 0 && (JS7_ISDEC(cp[0]) || cp[0] == '-')) { |
|
192 for (size_t i = 1; i < cp.length(); ++i) { |
|
193 if (!JS7_ISDEC(cp[i])) |
|
194 return id; |
|
195 } |
|
196 return JSID_VOID; |
|
197 } |
|
198 return id; |
|
199 } |
|
200 |
|
201 return JSID_VOID; |
|
202 } |
|
203 |
|
204 const char * TypeIdStringImpl(jsid id); |
|
205 |
|
206 /* Convert an id for printing during debug. */ |
|
207 static inline const char * |
|
208 TypeIdString(jsid id) |
|
209 { |
|
210 #ifdef DEBUG |
|
211 return TypeIdStringImpl(id); |
|
212 #else |
|
213 return "(missing)"; |
|
214 #endif |
|
215 } |
|
216 |
|
217 /* |
|
218 * Structure for type inference entry point functions. All functions which can |
|
219 * change type information must use this, and functions which depend on |
|
220 * intermediate types (i.e. JITs) can use this to ensure that intermediate |
|
221 * information is not collected and does not change. |
|
222 * |
|
223 * Pins inference results so that intermediate type information, TypeObjects |
|
224 * and JSScripts won't be collected during GC. Does additional sanity checking |
|
225 * that inference is not reentrant and that recompilations occur properly. |
|
226 */ |
|
227 struct AutoEnterAnalysis |
|
228 { |
|
229 /* Prevent GC activity in the middle of analysis. */ |
|
230 gc::AutoSuppressGC suppressGC; |
|
231 |
|
232 FreeOp *freeOp; |
|
233 JSCompartment *compartment; |
|
234 bool oldActiveAnalysis; |
|
235 |
|
236 AutoEnterAnalysis(ExclusiveContext *cx) |
|
237 : suppressGC(cx) |
|
238 { |
|
239 init(cx->defaultFreeOp(), cx->compartment()); |
|
240 } |
|
241 |
|
242 AutoEnterAnalysis(FreeOp *fop, JSCompartment *comp) |
|
243 : suppressGC(comp) |
|
244 { |
|
245 init(fop, comp); |
|
246 } |
|
247 |
|
248 ~AutoEnterAnalysis() |
|
249 { |
|
250 compartment->activeAnalysis = oldActiveAnalysis; |
|
251 |
|
252 /* |
|
253 * If there are no more type inference activations on the stack, |
|
254 * process any triggered recompilations. Note that we should not be |
|
255 * invoking any scripted code while type inference is running. |
|
256 */ |
|
257 if (!compartment->activeAnalysis) { |
|
258 TypeZone &types = compartment->zone()->types; |
|
259 if (types.pendingRecompiles) |
|
260 types.processPendingRecompiles(freeOp); |
|
261 } |
|
262 } |
|
263 |
|
264 private: |
|
265 void init(FreeOp *fop, JSCompartment *comp) { |
|
266 freeOp = fop; |
|
267 compartment = comp; |
|
268 oldActiveAnalysis = compartment->activeAnalysis; |
|
269 compartment->activeAnalysis = true; |
|
270 } |
|
271 }; |
|
272 |
|
273 ///////////////////////////////////////////////////////////////////// |
|
274 // Interface functions |
|
275 ///////////////////////////////////////////////////////////////////// |
|
276 |
|
277 inline const Class * |
|
278 GetClassForProtoKey(JSProtoKey key) |
|
279 { |
|
280 switch (key) { |
|
281 case JSProto_Object: |
|
282 return &JSObject::class_; |
|
283 case JSProto_Array: |
|
284 return &ArrayObject::class_; |
|
285 |
|
286 case JSProto_Number: |
|
287 return &NumberObject::class_; |
|
288 case JSProto_Boolean: |
|
289 return &BooleanObject::class_; |
|
290 case JSProto_String: |
|
291 return &StringObject::class_; |
|
292 case JSProto_RegExp: |
|
293 return &RegExpObject::class_; |
|
294 |
|
295 case JSProto_Int8Array: |
|
296 case JSProto_Uint8Array: |
|
297 case JSProto_Int16Array: |
|
298 case JSProto_Uint16Array: |
|
299 case JSProto_Int32Array: |
|
300 case JSProto_Uint32Array: |
|
301 case JSProto_Float32Array: |
|
302 case JSProto_Float64Array: |
|
303 case JSProto_Uint8ClampedArray: |
|
304 return &TypedArrayObject::classes[key - JSProto_Int8Array]; |
|
305 |
|
306 case JSProto_ArrayBuffer: |
|
307 return &ArrayBufferObject::class_; |
|
308 |
|
309 case JSProto_SharedArrayBuffer: |
|
310 return &SharedArrayBufferObject::class_; |
|
311 |
|
312 case JSProto_DataView: |
|
313 return &DataViewObject::class_; |
|
314 |
|
315 default: |
|
316 MOZ_ASSUME_UNREACHABLE("Bad proto key"); |
|
317 } |
|
318 } |
|
319 |
|
320 /* |
|
321 * Get the default 'new' object for a given standard class, per the currently |
|
322 * active global. |
|
323 */ |
|
324 inline TypeObject * |
|
325 GetTypeNewObject(JSContext *cx, JSProtoKey key) |
|
326 { |
|
327 RootedObject proto(cx); |
|
328 if (!GetBuiltinPrototype(cx, key, &proto)) |
|
329 return nullptr; |
|
330 return cx->getNewType(GetClassForProtoKey(key), proto.get()); |
|
331 } |
|
332 |
|
333 /* Get a type object for the immediate allocation site within a native. */ |
|
334 inline TypeObject * |
|
335 GetTypeCallerInitObject(JSContext *cx, JSProtoKey key) |
|
336 { |
|
337 jsbytecode *pc; |
|
338 RootedScript script(cx, cx->currentScript(&pc)); |
|
339 if (script) |
|
340 return TypeScript::InitObject(cx, script, pc, key); |
|
341 return GetTypeNewObject(cx, key); |
|
342 } |
|
343 |
|
344 void MarkIteratorUnknownSlow(JSContext *cx); |
|
345 |
|
346 void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args, |
|
347 bool constructing); |
|
348 |
|
349 /* |
|
350 * Monitor a javascript call, either on entry to the interpreter or made |
|
351 * from within the interpreter. |
|
352 */ |
|
353 inline void |
|
354 TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing) |
|
355 { |
|
356 if (args.callee().is<JSFunction>()) { |
|
357 JSFunction *fun = &args.callee().as<JSFunction>(); |
|
358 if (fun->isInterpreted() && fun->nonLazyScript()->types) |
|
359 TypeMonitorCallSlow(cx, &args.callee(), args, constructing); |
|
360 } |
|
361 } |
|
362 |
|
363 inline bool |
|
364 TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id) |
|
365 { |
|
366 if (obj->hasLazyType() || obj->type()->unknownProperties()) |
|
367 return false; |
|
368 |
|
369 if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id)) |
|
370 return false; |
|
371 |
|
372 return true; |
|
373 } |
|
374 |
|
375 inline void |
|
376 EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id) |
|
377 { |
|
378 id = IdToTypeId(id); |
|
379 |
|
380 if (obj->hasSingletonType()) { |
|
381 AutoEnterAnalysis enter(cx); |
|
382 if (obj->hasLazyType() && !obj->getType(cx)) { |
|
383 CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes"); |
|
384 return; |
|
385 } |
|
386 if (!obj->type()->unknownProperties() && !obj->type()->getProperty(cx, id)) { |
|
387 MOZ_ASSERT(obj->type()->unknownProperties()); |
|
388 return; |
|
389 } |
|
390 } |
|
391 |
|
392 JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id)); |
|
393 } |
|
394 |
|
395 inline bool |
|
396 CanHaveEmptyPropertyTypesForOwnProperty(JSObject *obj) |
|
397 { |
|
398 // Per the comment on TypeSet::propertySet, property type sets for global |
|
399 // objects may be empty for 'own' properties if the global property still |
|
400 // has its initial undefined value. |
|
401 return obj->is<GlobalObject>(); |
|
402 } |
|
403 |
|
404 inline bool |
|
405 HasTypePropertyId(JSObject *obj, jsid id, Type type) |
|
406 { |
|
407 if (obj->hasLazyType()) |
|
408 return true; |
|
409 |
|
410 if (obj->type()->unknownProperties()) |
|
411 return true; |
|
412 |
|
413 if (HeapTypeSet *types = obj->type()->maybeGetProperty(IdToTypeId(id))) |
|
414 return types->hasType(type); |
|
415 |
|
416 return false; |
|
417 } |
|
418 |
|
419 inline bool |
|
420 HasTypePropertyId(JSObject *obj, jsid id, const Value &value) |
|
421 { |
|
422 return HasTypePropertyId(obj, id, GetValueType(value)); |
|
423 } |
|
424 |
|
425 /* Add a possible type for a property of obj. */ |
|
426 inline void |
|
427 AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, Type type) |
|
428 { |
|
429 id = IdToTypeId(id); |
|
430 if (TrackPropertyTypes(cx, obj, id)) |
|
431 obj->type()->addPropertyType(cx, id, type); |
|
432 } |
|
433 |
|
434 inline void |
|
435 AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, const Value &value) |
|
436 { |
|
437 id = IdToTypeId(id); |
|
438 if (TrackPropertyTypes(cx, obj, id)) |
|
439 obj->type()->addPropertyType(cx, id, value); |
|
440 } |
|
441 |
|
442 inline void |
|
443 AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type) |
|
444 { |
|
445 if (!obj->unknownProperties()) |
|
446 obj->addPropertyType(cx, id, type); |
|
447 } |
|
448 |
|
449 inline void |
|
450 AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, const Value &value) |
|
451 { |
|
452 if (!obj->unknownProperties()) |
|
453 obj->addPropertyType(cx, id, value); |
|
454 } |
|
455 |
|
456 /* Set one or more dynamic flags on a type object. */ |
|
457 inline void |
|
458 MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags) |
|
459 { |
|
460 if (!obj->hasLazyType() && !obj->type()->hasAllFlags(flags)) |
|
461 obj->type()->setFlags(cx, flags); |
|
462 } |
|
463 |
|
464 /* |
|
465 * Mark all properties of a type object as unknown. If markSetsUnknown is set, |
|
466 * scan the entire compartment and mark all type sets containing it as having |
|
467 * an unknown object. This is needed for correctness in dealing with mutable |
|
468 * __proto__, which can change the type of an object dynamically. |
|
469 */ |
|
470 inline void |
|
471 MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj, |
|
472 bool markSetsUnknown = false) |
|
473 { |
|
474 if (!obj->unknownProperties()) |
|
475 obj->markUnknown(cx); |
|
476 if (markSetsUnknown && !(obj->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN)) |
|
477 cx->compartment()->types.markSetsUnknown(cx, obj); |
|
478 } |
|
479 |
|
480 inline void |
|
481 MarkTypePropertyNonData(ExclusiveContext *cx, JSObject *obj, jsid id) |
|
482 { |
|
483 id = IdToTypeId(id); |
|
484 if (TrackPropertyTypes(cx, obj, id)) |
|
485 obj->type()->markPropertyNonData(cx, id); |
|
486 } |
|
487 |
|
488 inline void |
|
489 MarkTypePropertyNonWritable(ExclusiveContext *cx, JSObject *obj, jsid id) |
|
490 { |
|
491 id = IdToTypeId(id); |
|
492 if (TrackPropertyTypes(cx, obj, id)) |
|
493 obj->type()->markPropertyNonWritable(cx, id); |
|
494 } |
|
495 |
|
496 inline bool |
|
497 IsTypePropertyIdMarkedNonData(JSObject *obj, jsid id) |
|
498 { |
|
499 return obj->type()->isPropertyNonData(id); |
|
500 } |
|
501 |
|
502 inline bool |
|
503 IsTypePropertyIdMarkedNonWritable(JSObject *obj, jsid id) |
|
504 { |
|
505 return obj->type()->isPropertyNonWritable(id); |
|
506 } |
|
507 |
|
508 /* Mark a state change on a particular object. */ |
|
509 inline void |
|
510 MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj) |
|
511 { |
|
512 if (!obj->hasLazyType() && !obj->type()->unknownProperties()) |
|
513 obj->type()->markStateChange(cx); |
|
514 } |
|
515 |
|
516 /* |
|
517 * For an array or object which has not yet escaped and been referenced elsewhere, |
|
518 * pick a new type based on the object's current contents. |
|
519 */ |
|
520 |
|
521 inline void |
|
522 FixArrayType(ExclusiveContext *cx, HandleObject obj) |
|
523 { |
|
524 cx->compartment()->types.fixArrayType(cx, obj); |
|
525 } |
|
526 |
|
527 inline void |
|
528 FixObjectType(ExclusiveContext *cx, HandleObject obj) |
|
529 { |
|
530 cx->compartment()->types.fixObjectType(cx, obj); |
|
531 } |
|
532 |
|
533 /* Interface helpers for JSScript*. */ |
|
534 extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, |
|
535 const js::Value &rval); |
|
536 extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, |
|
537 js::types::Type type); |
|
538 |
|
539 ///////////////////////////////////////////////////////////////////// |
|
540 // Script interface functions |
|
541 ///////////////////////////////////////////////////////////////////// |
|
542 |
|
543 /* static */ inline unsigned |
|
544 TypeScript::NumTypeSets(JSScript *script) |
|
545 { |
|
546 return script->nTypeSets() + analyze::LocalSlot(script, 0); |
|
547 } |
|
548 |
|
549 /* static */ inline StackTypeSet * |
|
550 TypeScript::ThisTypes(JSScript *script) |
|
551 { |
|
552 return script->types->typeArray() + script->nTypeSets() + analyze::ThisSlot(); |
|
553 } |
|
554 |
|
555 /* |
|
556 * Note: for non-escaping arguments and locals, argTypes/localTypes reflect |
|
557 * only the initial type of the variable (e.g. passed values for argTypes, |
|
558 * or undefined for localTypes) and not types from subsequent assignments. |
|
559 */ |
|
560 |
|
561 /* static */ inline StackTypeSet * |
|
562 TypeScript::ArgTypes(JSScript *script, unsigned i) |
|
563 { |
|
564 JS_ASSERT(i < script->functionNonDelazifying()->nargs()); |
|
565 return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i); |
|
566 } |
|
567 |
|
568 template <typename TYPESET> |
|
569 /* static */ inline TYPESET * |
|
570 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap, |
|
571 uint32_t *hint, TYPESET *typeArray) |
|
572 { |
|
573 JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET); |
|
574 uint32_t offset = script->pcToOffset(pc); |
|
575 |
|
576 // See if this pc is the next typeset opcode after the last one looked up. |
|
577 if ((*hint + 1) < script->nTypeSets() && bytecodeMap[*hint + 1] == offset) { |
|
578 (*hint)++; |
|
579 return typeArray + *hint; |
|
580 } |
|
581 |
|
582 // See if this pc is the same as the last one looked up. |
|
583 if (bytecodeMap[*hint] == offset) |
|
584 return typeArray + *hint; |
|
585 |
|
586 // Fall back to a binary search. |
|
587 size_t bottom = 0; |
|
588 size_t top = script->nTypeSets() - 1; |
|
589 size_t mid = bottom + (top - bottom) / 2; |
|
590 while (mid < top) { |
|
591 if (bytecodeMap[mid] < offset) |
|
592 bottom = mid + 1; |
|
593 else if (bytecodeMap[mid] > offset) |
|
594 top = mid; |
|
595 else |
|
596 break; |
|
597 mid = bottom + (top - bottom) / 2; |
|
598 } |
|
599 |
|
600 // We should have have zeroed in on either the exact offset, unless there |
|
601 // are more JOF_TYPESET opcodes than nTypeSets in the script (as can happen |
|
602 // if the script is very long). |
|
603 JS_ASSERT(bytecodeMap[mid] == offset || mid == top); |
|
604 |
|
605 *hint = mid; |
|
606 return typeArray + *hint; |
|
607 } |
|
608 |
|
609 /* static */ inline StackTypeSet * |
|
610 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc) |
|
611 { |
|
612 JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread())); |
|
613 #ifdef JS_ION |
|
614 uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets(); |
|
615 return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(), |
|
616 hint, script->types->typeArray()); |
|
617 #else |
|
618 MOZ_CRASH(); |
|
619 #endif |
|
620 } |
|
621 |
|
622 struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> { |
|
623 JSScript *script; |
|
624 |
|
625 uint32_t offset : 24; |
|
626 JSProtoKey kind : 8; |
|
627 |
|
628 static const uint32_t OFFSET_LIMIT = (1 << 23); |
|
629 |
|
630 AllocationSiteKey() { mozilla::PodZero(this); } |
|
631 |
|
632 static inline uint32_t hash(AllocationSiteKey key) { |
|
633 return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); |
|
634 } |
|
635 |
|
636 static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) { |
|
637 return a.script == b.script && a.offset == b.offset && a.kind == b.kind; |
|
638 } |
|
639 }; |
|
640 |
|
641 /* Whether to use a new type object for an initializer opcode at script/pc. */ |
|
642 js::NewObjectKind |
|
643 UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key); |
|
644 |
|
645 js::NewObjectKind |
|
646 UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp); |
|
647 |
|
648 /* static */ inline TypeObject * |
|
649 TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind) |
|
650 { |
|
651 JS_ASSERT(!UseNewTypeForInitializer(script, pc, kind)); |
|
652 |
|
653 /* :XXX: Limit script->length so we don't need to check the offset up front? */ |
|
654 uint32_t offset = script->pcToOffset(pc); |
|
655 |
|
656 if (!script->compileAndGo() || offset >= AllocationSiteKey::OFFSET_LIMIT) |
|
657 return GetTypeNewObject(cx, kind); |
|
658 |
|
659 AllocationSiteKey key; |
|
660 key.script = script; |
|
661 key.offset = offset; |
|
662 key.kind = kind; |
|
663 |
|
664 if (!cx->compartment()->types.allocationSiteTable) |
|
665 return cx->compartment()->types.addAllocationSiteTypeObject(cx, key); |
|
666 |
|
667 AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key); |
|
668 |
|
669 if (p) |
|
670 return p->value(); |
|
671 return cx->compartment()->types.addAllocationSiteTypeObject(cx, key); |
|
672 } |
|
673 |
|
674 /* Set the type to use for obj according to the site it was allocated at. */ |
|
675 static inline bool |
|
676 SetInitializerObjectType(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, NewObjectKind kind) |
|
677 { |
|
678 JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); |
|
679 JS_ASSERT(key != JSProto_Null); |
|
680 JS_ASSERT(kind == UseNewTypeForInitializer(script, pc, key)); |
|
681 |
|
682 if (kind == SingletonObject) { |
|
683 JS_ASSERT(obj->hasSingletonType()); |
|
684 |
|
685 /* |
|
686 * Inference does not account for types of run-once initializer |
|
687 * objects, as these may not be created until after the script |
|
688 * has been analyzed. |
|
689 */ |
|
690 TypeScript::Monitor(cx, script, pc, ObjectValue(*obj)); |
|
691 } else { |
|
692 types::TypeObject *type = TypeScript::InitObject(cx, script, pc, key); |
|
693 if (!type) |
|
694 return false; |
|
695 obj->uninlinedSetType(type); |
|
696 } |
|
697 |
|
698 return true; |
|
699 } |
|
700 |
|
701 /* static */ inline void |
|
702 TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) |
|
703 { |
|
704 TypeMonitorResult(cx, script, pc, rval); |
|
705 } |
|
706 |
|
707 /* static */ inline void |
|
708 TypeScript::Monitor(JSContext *cx, const js::Value &rval) |
|
709 { |
|
710 jsbytecode *pc; |
|
711 RootedScript script(cx, cx->currentScript(&pc)); |
|
712 Monitor(cx, script, pc, rval); |
|
713 } |
|
714 |
|
715 /* static */ inline void |
|
716 TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id) |
|
717 { |
|
718 if (!obj->hasSingletonType()) { |
|
719 /* |
|
720 * Mark as unknown any object which has had dynamic assignments to |
|
721 * non-integer properties at SETELEM opcodes. This avoids making large |
|
722 * numbers of type properties for hashmap-style objects. We don't need |
|
723 * to do this for objects with singleton type, because type properties |
|
724 * are only constructed for them when analyzed scripts depend on those |
|
725 * specific properties. |
|
726 */ |
|
727 uint32_t i; |
|
728 if (js_IdIsIndex(id, &i)) |
|
729 return; |
|
730 |
|
731 // But if we don't have too many properties yet, don't do anything. The |
|
732 // idea here is that normal object initialization should not trigger |
|
733 // deoptimization in most cases, while actual usage as a hashmap should. |
|
734 TypeObject* type = obj->type(); |
|
735 if (type->getPropertyCount() < 8) |
|
736 return; |
|
737 MarkTypeObjectUnknownProperties(cx, type); |
|
738 } |
|
739 } |
|
740 |
|
741 /* static */ inline void |
|
742 TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) |
|
743 { |
|
744 if (!script->types) |
|
745 return; |
|
746 |
|
747 if (!ThisTypes(script)->hasType(type)) { |
|
748 AutoEnterAnalysis enter(cx); |
|
749 |
|
750 InferSpew(ISpewOps, "externalType: setThis #%u: %s", |
|
751 script->id(), TypeString(type)); |
|
752 ThisTypes(script)->addType(cx, type); |
|
753 } |
|
754 } |
|
755 |
|
756 /* static */ inline void |
|
757 TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value) |
|
758 { |
|
759 SetThis(cx, script, GetValueType(value)); |
|
760 } |
|
761 |
|
762 /* static */ inline void |
|
763 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type) |
|
764 { |
|
765 if (!script->types) |
|
766 return; |
|
767 |
|
768 if (!ArgTypes(script, arg)->hasType(type)) { |
|
769 AutoEnterAnalysis enter(cx); |
|
770 |
|
771 InferSpew(ISpewOps, "externalType: setArg #%u %u: %s", |
|
772 script->id(), arg, TypeString(type)); |
|
773 ArgTypes(script, arg)->addType(cx, type); |
|
774 } |
|
775 } |
|
776 |
|
777 /* static */ inline void |
|
778 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value) |
|
779 { |
|
780 Type type = GetValueType(value); |
|
781 SetArgument(cx, script, arg, type); |
|
782 } |
|
783 |
|
784 ///////////////////////////////////////////////////////////////////// |
|
785 // TypeCompartment |
|
786 ///////////////////////////////////////////////////////////////////// |
|
787 |
|
788 inline JSCompartment * |
|
789 TypeCompartment::compartment() |
|
790 { |
|
791 return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); |
|
792 } |
|
793 |
|
794 ///////////////////////////////////////////////////////////////////// |
|
795 // TypeSet |
|
796 ///////////////////////////////////////////////////////////////////// |
|
797 |
|
798 /* |
|
799 * The sets of objects and scripts in a type set grow monotonically, are usually |
|
800 * empty, almost always small, and sometimes big. For empty or singleton sets, |
|
801 * the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE, |
|
802 * an array of this length is used to store the elements. For larger sets, a hash |
|
803 * table filled to 25%-50% of capacity is used, with collisions resolved by linear |
|
804 * probing. TODO: replace these with jshashtables. |
|
805 */ |
|
806 const unsigned SET_ARRAY_SIZE = 8; |
|
807 const unsigned SET_CAPACITY_OVERFLOW = 1u << 30; |
|
808 |
|
809 /* Get the capacity of a set with the given element count. */ |
|
810 static inline unsigned |
|
811 HashSetCapacity(unsigned count) |
|
812 { |
|
813 JS_ASSERT(count >= 2); |
|
814 JS_ASSERT(count < SET_CAPACITY_OVERFLOW); |
|
815 |
|
816 if (count <= SET_ARRAY_SIZE) |
|
817 return SET_ARRAY_SIZE; |
|
818 |
|
819 return 1u << (mozilla::FloorLog2(count) + 2); |
|
820 } |
|
821 |
|
822 /* Compute the FNV hash for the low 32 bits of v. */ |
|
823 template <class T, class KEY> |
|
824 static inline uint32_t |
|
825 HashKey(T v) |
|
826 { |
|
827 uint32_t nv = KEY::keyBits(v); |
|
828 |
|
829 uint32_t hash = 84696351 ^ (nv & 0xff); |
|
830 hash = (hash * 16777619) ^ ((nv >> 8) & 0xff); |
|
831 hash = (hash * 16777619) ^ ((nv >> 16) & 0xff); |
|
832 return (hash * 16777619) ^ ((nv >> 24) & 0xff); |
|
833 } |
|
834 |
|
835 /* |
|
836 * Insert space for an element into the specified set and grow its capacity if needed. |
|
837 * returned value is an existing or new entry (nullptr if new). |
|
838 */ |
|
839 template <class T, class U, class KEY> |
|
840 static U ** |
|
841 HashSetInsertTry(LifoAlloc &alloc, U **&values, unsigned &count, T key) |
|
842 { |
|
843 unsigned capacity = HashSetCapacity(count); |
|
844 unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1); |
|
845 |
|
846 /* Whether we are converting from a fixed array to hashtable. */ |
|
847 bool converting = (count == SET_ARRAY_SIZE); |
|
848 |
|
849 if (!converting) { |
|
850 while (values[insertpos] != nullptr) { |
|
851 if (KEY::getKey(values[insertpos]) == key) |
|
852 return &values[insertpos]; |
|
853 insertpos = (insertpos + 1) & (capacity - 1); |
|
854 } |
|
855 } |
|
856 |
|
857 if (count >= SET_CAPACITY_OVERFLOW) |
|
858 return nullptr; |
|
859 |
|
860 count++; |
|
861 unsigned newCapacity = HashSetCapacity(count); |
|
862 |
|
863 if (newCapacity == capacity) { |
|
864 JS_ASSERT(!converting); |
|
865 return &values[insertpos]; |
|
866 } |
|
867 |
|
868 U **newValues = alloc.newArray<U*>(newCapacity); |
|
869 if (!newValues) |
|
870 return nullptr; |
|
871 mozilla::PodZero(newValues, newCapacity); |
|
872 |
|
873 for (unsigned i = 0; i < capacity; i++) { |
|
874 if (values[i]) { |
|
875 unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1); |
|
876 while (newValues[pos] != nullptr) |
|
877 pos = (pos + 1) & (newCapacity - 1); |
|
878 newValues[pos] = values[i]; |
|
879 } |
|
880 } |
|
881 |
|
882 values = newValues; |
|
883 |
|
884 insertpos = HashKey<T,KEY>(key) & (newCapacity - 1); |
|
885 while (values[insertpos] != nullptr) |
|
886 insertpos = (insertpos + 1) & (newCapacity - 1); |
|
887 return &values[insertpos]; |
|
888 } |
|
889 |
|
890 /* |
|
891 * Insert an element into the specified set if it is not already there, returning |
|
892 * an entry which is nullptr if the element was not there. |
|
893 */ |
|
894 template <class T, class U, class KEY> |
|
895 static inline U ** |
|
896 HashSetInsert(LifoAlloc &alloc, U **&values, unsigned &count, T key) |
|
897 { |
|
898 if (count == 0) { |
|
899 JS_ASSERT(values == nullptr); |
|
900 count++; |
|
901 return (U **) &values; |
|
902 } |
|
903 |
|
904 if (count == 1) { |
|
905 U *oldData = (U*) values; |
|
906 if (KEY::getKey(oldData) == key) |
|
907 return (U **) &values; |
|
908 |
|
909 values = alloc.newArray<U*>(SET_ARRAY_SIZE); |
|
910 if (!values) { |
|
911 values = (U **) oldData; |
|
912 return nullptr; |
|
913 } |
|
914 mozilla::PodZero(values, SET_ARRAY_SIZE); |
|
915 count++; |
|
916 |
|
917 values[0] = oldData; |
|
918 return &values[1]; |
|
919 } |
|
920 |
|
921 if (count <= SET_ARRAY_SIZE) { |
|
922 for (unsigned i = 0; i < count; i++) { |
|
923 if (KEY::getKey(values[i]) == key) |
|
924 return &values[i]; |
|
925 } |
|
926 |
|
927 if (count < SET_ARRAY_SIZE) { |
|
928 count++; |
|
929 return &values[count - 1]; |
|
930 } |
|
931 } |
|
932 |
|
933 return HashSetInsertTry<T,U,KEY>(alloc, values, count, key); |
|
934 } |
|
935 |
|
936 /* Lookup an entry in a hash set, return nullptr if it does not exist. */ |
|
937 template <class T, class U, class KEY> |
|
938 static inline U * |
|
939 HashSetLookup(U **values, unsigned count, T key) |
|
940 { |
|
941 if (count == 0) |
|
942 return nullptr; |
|
943 |
|
944 if (count == 1) |
|
945 return (KEY::getKey((U *) values) == key) ? (U *) values : nullptr; |
|
946 |
|
947 if (count <= SET_ARRAY_SIZE) { |
|
948 for (unsigned i = 0; i < count; i++) { |
|
949 if (KEY::getKey(values[i]) == key) |
|
950 return values[i]; |
|
951 } |
|
952 return nullptr; |
|
953 } |
|
954 |
|
955 unsigned capacity = HashSetCapacity(count); |
|
956 unsigned pos = HashKey<T,KEY>(key) & (capacity - 1); |
|
957 |
|
958 while (values[pos] != nullptr) { |
|
959 if (KEY::getKey(values[pos]) == key) |
|
960 return values[pos]; |
|
961 pos = (pos + 1) & (capacity - 1); |
|
962 } |
|
963 |
|
964 return nullptr; |
|
965 } |
|
966 |
|
967 inline TypeObjectKey * |
|
968 Type::objectKey() const |
|
969 { |
|
970 JS_ASSERT(isObject()); |
|
971 if (isTypeObject()) |
|
972 TypeObject::readBarrier((TypeObject *) data); |
|
973 else |
|
974 JSObject::readBarrier((JSObject *) (data ^ 1)); |
|
975 return (TypeObjectKey *) data; |
|
976 } |
|
977 |
|
978 inline JSObject * |
|
979 Type::singleObject() const |
|
980 { |
|
981 JS_ASSERT(isSingleObject()); |
|
982 JSObject::readBarrier((JSObject *) (data ^ 1)); |
|
983 return (JSObject *) (data ^ 1); |
|
984 } |
|
985 |
|
986 inline TypeObject * |
|
987 Type::typeObject() const |
|
988 { |
|
989 JS_ASSERT(isTypeObject()); |
|
990 TypeObject::readBarrier((TypeObject *) data); |
|
991 return (TypeObject *) data; |
|
992 } |
|
993 |
|
994 inline bool |
|
995 TypeSet::hasType(Type type) const |
|
996 { |
|
997 if (unknown()) |
|
998 return true; |
|
999 |
|
1000 if (type.isUnknown()) { |
|
1001 return false; |
|
1002 } else if (type.isPrimitive()) { |
|
1003 return !!(flags & PrimitiveTypeFlag(type.primitive())); |
|
1004 } else if (type.isAnyObject()) { |
|
1005 return !!(flags & TYPE_FLAG_ANYOBJECT); |
|
1006 } else { |
|
1007 return !!(flags & TYPE_FLAG_ANYOBJECT) || |
|
1008 HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey> |
|
1009 (objectSet, baseObjectCount(), type.objectKey()) != nullptr; |
|
1010 } |
|
1011 } |
|
1012 |
|
1013 inline void |
|
1014 TypeSet::setBaseObjectCount(uint32_t count) |
|
1015 { |
|
1016 JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT); |
|
1017 flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK) |
|
1018 | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT); |
|
1019 } |
|
1020 |
|
1021 inline void |
|
1022 HeapTypeSet::newPropertyState(ExclusiveContext *cxArg) |
|
1023 { |
|
1024 /* Propagate the change to all constraints. */ |
|
1025 if (JSContext *cx = cxArg->maybeJSContext()) { |
|
1026 TypeConstraint *constraint = constraintList; |
|
1027 while (constraint) { |
|
1028 constraint->newPropertyState(cx, this); |
|
1029 constraint = constraint->next; |
|
1030 } |
|
1031 } else { |
|
1032 JS_ASSERT(!constraintList); |
|
1033 } |
|
1034 } |
|
1035 |
|
1036 inline void |
|
1037 HeapTypeSet::setNonDataPropertyIgnoringConstraints() |
|
1038 { |
|
1039 flags |= TYPE_FLAG_NON_DATA_PROPERTY; |
|
1040 } |
|
1041 |
|
1042 inline void |
|
1043 HeapTypeSet::setNonDataProperty(ExclusiveContext *cx) |
|
1044 { |
|
1045 if (flags & TYPE_FLAG_NON_DATA_PROPERTY) |
|
1046 return; |
|
1047 |
|
1048 setNonDataPropertyIgnoringConstraints(); |
|
1049 newPropertyState(cx); |
|
1050 } |
|
1051 |
|
1052 inline void |
|
1053 HeapTypeSet::setNonWritableProperty(ExclusiveContext *cx) |
|
1054 { |
|
1055 if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY) |
|
1056 return; |
|
1057 |
|
1058 flags |= TYPE_FLAG_NON_WRITABLE_PROPERTY; |
|
1059 newPropertyState(cx); |
|
1060 } |
|
1061 |
|
1062 inline unsigned |
|
1063 TypeSet::getObjectCount() const |
|
1064 { |
|
1065 JS_ASSERT(!unknownObject()); |
|
1066 uint32_t count = baseObjectCount(); |
|
1067 if (count > SET_ARRAY_SIZE) |
|
1068 return HashSetCapacity(count); |
|
1069 return count; |
|
1070 } |
|
1071 |
|
1072 inline TypeObjectKey * |
|
1073 TypeSet::getObject(unsigned i) const |
|
1074 { |
|
1075 JS_ASSERT(i < getObjectCount()); |
|
1076 if (baseObjectCount() == 1) { |
|
1077 JS_ASSERT(i == 0); |
|
1078 return (TypeObjectKey *) objectSet; |
|
1079 } |
|
1080 return objectSet[i]; |
|
1081 } |
|
1082 |
|
1083 inline JSObject * |
|
1084 TypeSet::getSingleObject(unsigned i) const |
|
1085 { |
|
1086 TypeObjectKey *key = getObject(i); |
|
1087 return (key && key->isSingleObject()) ? key->asSingleObject() : nullptr; |
|
1088 } |
|
1089 |
|
1090 inline TypeObject * |
|
1091 TypeSet::getTypeObject(unsigned i) const |
|
1092 { |
|
1093 TypeObjectKey *key = getObject(i); |
|
1094 return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr; |
|
1095 } |
|
1096 |
|
1097 inline const Class * |
|
1098 TypeSet::getObjectClass(unsigned i) const |
|
1099 { |
|
1100 if (JSObject *object = getSingleObject(i)) |
|
1101 return object->getClass(); |
|
1102 if (TypeObject *object = getTypeObject(i)) |
|
1103 return object->clasp(); |
|
1104 return nullptr; |
|
1105 } |
|
1106 |
|
1107 ///////////////////////////////////////////////////////////////////// |
|
1108 // TypeObject |
|
1109 ///////////////////////////////////////////////////////////////////// |
|
1110 |
|
1111 inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags) |
|
1112 { |
|
1113 mozilla::PodZero(this); |
|
1114 |
|
1115 /* Inner objects may not appear on prototype chains. */ |
|
1116 JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); |
|
1117 |
|
1118 this->clasp_ = clasp; |
|
1119 this->proto_ = proto.raw(); |
|
1120 this->flags_ = initialFlags; |
|
1121 |
|
1122 InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this)); |
|
1123 } |
|
1124 |
|
1125 inline uint32_t |
|
1126 TypeObject::basePropertyCount() const |
|
1127 { |
|
1128 return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; |
|
1129 } |
|
1130 |
|
1131 inline void |
|
1132 TypeObject::setBasePropertyCount(uint32_t count) |
|
1133 { |
|
1134 // Note: Callers must ensure they are performing threadsafe operations. |
|
1135 JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); |
|
1136 flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) |
|
1137 | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); |
|
1138 } |
|
1139 |
|
1140 inline HeapTypeSet * |
|
1141 TypeObject::getProperty(ExclusiveContext *cx, jsid id) |
|
1142 { |
|
1143 JS_ASSERT(cx->compartment()->activeAnalysis); |
|
1144 |
|
1145 JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); |
|
1146 JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); |
|
1147 JS_ASSERT(!unknownProperties()); |
|
1148 |
|
1149 if (HeapTypeSet *types = maybeGetProperty(id)) |
|
1150 return types; |
|
1151 |
|
1152 Property *base = cx->typeLifoAlloc().new_<Property>(id); |
|
1153 if (!base) { |
|
1154 markUnknown(cx); |
|
1155 return nullptr; |
|
1156 } |
|
1157 |
|
1158 uint32_t propertyCount = basePropertyCount(); |
|
1159 Property **pprop = HashSetInsert<jsid,Property,Property> |
|
1160 (cx->typeLifoAlloc(), propertySet, propertyCount, id); |
|
1161 if (!pprop) { |
|
1162 markUnknown(cx); |
|
1163 return nullptr; |
|
1164 } |
|
1165 |
|
1166 JS_ASSERT(!*pprop); |
|
1167 |
|
1168 setBasePropertyCount(propertyCount); |
|
1169 *pprop = base; |
|
1170 |
|
1171 updateNewPropertyTypes(cx, id, &base->types); |
|
1172 |
|
1173 if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) { |
|
1174 // We hit the maximum number of properties the object can have, mark |
|
1175 // the object unknown so that new properties will not be added in the |
|
1176 // future. |
|
1177 markUnknown(cx); |
|
1178 } |
|
1179 |
|
1180 return &base->types; |
|
1181 } |
|
1182 |
|
1183 inline HeapTypeSet * |
|
1184 TypeObject::maybeGetProperty(jsid id) |
|
1185 { |
|
1186 JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id)); |
|
1187 JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); |
|
1188 JS_ASSERT(!unknownProperties()); |
|
1189 |
|
1190 Property *prop = HashSetLookup<jsid,Property,Property> |
|
1191 (propertySet, basePropertyCount(), id); |
|
1192 |
|
1193 return prop ? &prop->types : nullptr; |
|
1194 } |
|
1195 |
|
1196 inline unsigned |
|
1197 TypeObject::getPropertyCount() |
|
1198 { |
|
1199 uint32_t count = basePropertyCount(); |
|
1200 if (count > SET_ARRAY_SIZE) |
|
1201 return HashSetCapacity(count); |
|
1202 return count; |
|
1203 } |
|
1204 |
|
1205 inline Property * |
|
1206 TypeObject::getProperty(unsigned i) |
|
1207 { |
|
1208 JS_ASSERT(i < getPropertyCount()); |
|
1209 if (basePropertyCount() == 1) { |
|
1210 JS_ASSERT(i == 0); |
|
1211 return (Property *) propertySet; |
|
1212 } |
|
1213 return propertySet[i]; |
|
1214 } |
|
1215 |
|
1216 inline void |
|
1217 TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type) |
|
1218 { |
|
1219 #ifdef JSGC_INCREMENTAL |
|
1220 if (!type) |
|
1221 return; |
|
1222 |
|
1223 switch (type->kind) { |
|
1224 case NewScript: |
|
1225 return TypeNewScript::writeBarrierPre(type->asNewScript()); |
|
1226 |
|
1227 case TypedObject: |
|
1228 return TypeTypedObject::writeBarrierPre(type->asTypedObject()); |
|
1229 } |
|
1230 #endif |
|
1231 } |
|
1232 |
|
1233 inline void |
|
1234 TypeNewScript::writeBarrierPre(TypeNewScript *newScript) |
|
1235 { |
|
1236 #ifdef JSGC_INCREMENTAL |
|
1237 if (!newScript || !newScript->fun->runtimeFromAnyThread()->needsBarrier()) |
|
1238 return; |
|
1239 |
|
1240 JS::Zone *zone = newScript->fun->zoneFromAnyThread(); |
|
1241 if (zone->needsBarrier()) { |
|
1242 MarkObject(zone->barrierTracer(), &newScript->fun, "write barrier"); |
|
1243 MarkObject(zone->barrierTracer(), &newScript->templateObject, "write barrier"); |
|
1244 } |
|
1245 #endif |
|
1246 } |
|
1247 |
|
1248 } } /* namespace js::types */ |
|
1249 |
|
1250 inline bool |
|
1251 JSScript::ensureHasTypes(JSContext *cx) |
|
1252 { |
|
1253 return types || makeTypes(cx); |
|
1254 } |
|
1255 |
|
1256 namespace js { |
|
1257 |
|
1258 template <> |
|
1259 struct GCMethods<const types::Type> |
|
1260 { |
|
1261 static types::Type initial() { return types::Type::UnknownType(); } |
|
1262 static ThingRootKind kind() { return THING_ROOT_TYPE; } |
|
1263 static bool poisoned(const types::Type &v) { |
|
1264 return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) |
|
1265 || (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); |
|
1266 } |
|
1267 }; |
|
1268 |
|
1269 template <> |
|
1270 struct GCMethods<types::Type> |
|
1271 { |
|
1272 static types::Type initial() { return types::Type::UnknownType(); } |
|
1273 static ThingRootKind kind() { return THING_ROOT_TYPE; } |
|
1274 static bool poisoned(const types::Type &v) { |
|
1275 return (v.isTypeObject() && IsPoisonedPtr(v.typeObject())) |
|
1276 || (v.isSingleObject() && IsPoisonedPtr(v.singleObject())); |
|
1277 } |
|
1278 }; |
|
1279 |
|
1280 } // namespace js |
|
1281 |
|
1282 namespace JS { |
|
1283 template<> class AnchorPermitted<js::types::TypeObject *> { }; |
|
1284 } // namespace JS |
|
1285 |
|
1286 #endif /* jsinferinlines_h */ |