Line data Source code
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 : #include "jit/CacheIR.h"
8 :
9 : #include "mozilla/DebugOnly.h"
10 : #include "mozilla/FloatingPoint.h"
11 :
12 : #include "jit/BaselineCacheIRCompiler.h"
13 : #include "jit/BaselineIC.h"
14 : #include "jit/CacheIRSpewer.h"
15 : #include "vm/SelfHosting.h"
16 :
17 : #include "jit/MacroAssembler-inl.h"
18 : #include "vm/EnvironmentObject-inl.h"
19 : #include "vm/JSContext-inl.h"
20 : #include "vm/JSObject-inl.h"
21 : #include "vm/UnboxedObject-inl.h"
22 :
23 : using namespace js;
24 : using namespace js::jit;
25 :
26 : using mozilla::DebugOnly;
27 : using mozilla::Maybe;
28 :
29 : const char* js::jit::CacheKindNames[] = {
30 : #define DEFINE_KIND(kind) #kind,
31 : CACHE_IR_KINDS(DEFINE_KIND)
32 : #undef DEFINE_KIND
33 : };
34 :
35 : void
36 0 : CacheIRWriter::assertSameCompartment(JSObject* obj) {
37 0 : assertSameCompartmentDebugOnly(cx_, obj);
38 0 : }
39 :
40 0 : IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
41 0 : ICState::Mode mode)
42 : : writer(cx),
43 : cx_(cx),
44 : script_(script),
45 : pc_(pc),
46 : cacheKind_(cacheKind),
47 0 : mode_(mode)
48 0 : {}
49 :
50 0 : GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
51 : CacheKind cacheKind, ICState::Mode mode,
52 : bool* isTemporarilyUnoptimizable, HandleValue val,
53 : HandleValue idVal, HandleValue receiver,
54 0 : GetPropertyResultFlags resultFlags)
55 : : IRGenerator(cx, script, pc, cacheKind, mode),
56 : val_(val),
57 : idVal_(idVal),
58 : receiver_(receiver),
59 : isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
60 : resultFlags_(resultFlags),
61 0 : preliminaryObjectAction_(PreliminaryObjectAction::None)
62 0 : {}
63 :
64 : static void
65 0 : EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder,
66 : Shape* shape)
67 : {
68 0 : if (holder->isFixedSlot(shape->slot())) {
69 0 : writer.loadFixedSlotResult(holderOp, NativeObject::getFixedSlotOffset(shape->slot()));
70 : } else {
71 0 : size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
72 0 : writer.loadDynamicSlotResult(holderOp, dynamicSlotOffset);
73 : }
74 0 : }
75 :
76 : // DOM proxies
77 : // -----------
78 : //
79 : // DOM proxies are proxies that are used to implement various DOM objects like
80 : // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
81 : // object that stores extra properties added to the object. The following
82 : // CacheIR instructions are only used with DOM proxies:
83 : //
84 : // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
85 : // returns either an UndefinedValue (no expando), ObjectValue (the expando
86 : // object), or PrivateValue(ExpandoAndGeneration*).
87 : //
88 : // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
89 : // slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
90 : // generation, then returns expandoAndGeneration->expando. This Value is
91 : // either an UndefinedValue or ObjectValue.
92 : //
93 : // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
94 : // expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
95 : // returns the expandoAndGeneration->expando Value.
96 : //
97 : // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
98 : // guards it's either UndefinedValue or an object with the expected shape.
99 :
100 : enum class ProxyStubType {
101 : None,
102 : DOMExpando,
103 : DOMShadowed,
104 : DOMUnshadowed,
105 : Generic
106 : };
107 :
108 : static ProxyStubType
109 0 : GetProxyStubType(JSContext* cx, HandleObject obj, HandleId id)
110 : {
111 0 : if (!obj->is<ProxyObject>())
112 : return ProxyStubType::None;
113 :
114 0 : if (!IsCacheableDOMProxy(obj))
115 : return ProxyStubType::Generic;
116 :
117 0 : DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
118 0 : if (shadows == ShadowCheckFailed) {
119 0 : cx->clearPendingException();
120 0 : return ProxyStubType::None;
121 : }
122 :
123 0 : if (DOMProxyIsShadowing(shadows)) {
124 0 : if (shadows == ShadowsViaDirectExpando || shadows == ShadowsViaIndirectExpando)
125 : return ProxyStubType::DOMExpando;
126 0 : return ProxyStubType::DOMShadowed;
127 : }
128 :
129 0 : MOZ_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
130 : return ProxyStubType::DOMUnshadowed;
131 : }
132 :
133 : static bool
134 0 : ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
135 : bool* nameOrSymbol)
136 : {
137 0 : *nameOrSymbol = false;
138 :
139 0 : if (!idval.isString() && !idval.isSymbol())
140 : return true;
141 :
142 0 : if (!ValueToId<CanGC>(cx, idval, id))
143 : return false;
144 :
145 0 : if (!JSID_IS_STRING(id) && !JSID_IS_SYMBOL(id)) {
146 0 : id.set(JSID_VOID);
147 0 : return true;
148 : }
149 :
150 : uint32_t dummy;
151 0 : if (JSID_IS_STRING(id) && JSID_TO_ATOM(id)->isIndex(&dummy)) {
152 0 : id.set(JSID_VOID);
153 0 : return true;
154 : }
155 :
156 0 : *nameOrSymbol = true;
157 0 : return true;
158 : }
159 :
160 : bool
161 0 : GetPropIRGenerator::tryAttachStub()
162 : {
163 : // Idempotent ICs should call tryAttachIdempotentStub instead.
164 0 : MOZ_ASSERT(!idempotent());
165 :
166 0 : AutoAssertNoPendingException aanpe(cx_);
167 :
168 : // Non-object receivers are a degenerate case, so don't try to attach
169 : // stubs. The stubs we do emit will still perform runtime checks and
170 : // fallback as needed.
171 0 : if (isSuper() && !receiver_.isObject())
172 : return false;
173 :
174 0 : ValOperandId valId(writer.setInputOperandId(0));
175 0 : if (cacheKind_ != CacheKind::GetProp) {
176 0 : MOZ_ASSERT_IF(cacheKind_ == CacheKind::GetPropSuper, getSuperReceiverValueId().id() == 1);
177 0 : MOZ_ASSERT_IF(cacheKind_ != CacheKind::GetPropSuper, getElemKeyValueId().id() == 1);
178 0 : writer.setInputOperandId(1);
179 : }
180 0 : if (cacheKind_ == CacheKind::GetElemSuper) {
181 0 : MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
182 0 : writer.setInputOperandId(2);
183 : }
184 :
185 0 : RootedId id(cx_);
186 : bool nameOrSymbol;
187 0 : if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
188 0 : cx_->clearPendingException();
189 0 : return false;
190 : }
191 :
192 0 : if (val_.isObject()) {
193 0 : RootedObject obj(cx_, &val_.toObject());
194 0 : ObjOperandId objId = writer.guardIsObject(valId);
195 0 : if (nameOrSymbol) {
196 0 : if (tryAttachObjectLength(obj, objId, id))
197 : return true;
198 0 : if (tryAttachNative(obj, objId, id))
199 : return true;
200 0 : if (tryAttachUnboxed(obj, objId, id))
201 : return true;
202 0 : if (tryAttachUnboxedExpando(obj, objId, id))
203 : return true;
204 0 : if (tryAttachTypedObject(obj, objId, id))
205 : return true;
206 0 : if (tryAttachModuleNamespace(obj, objId, id))
207 : return true;
208 0 : if (tryAttachWindowProxy(obj, objId, id))
209 : return true;
210 0 : if (tryAttachCrossCompartmentWrapper(obj, objId, id))
211 : return true;
212 0 : if (tryAttachXrayCrossCompartmentWrapper(obj, objId, id))
213 : return true;
214 0 : if (tryAttachFunction(obj, objId, id))
215 : return true;
216 0 : if (tryAttachProxy(obj, objId, id))
217 : return true;
218 :
219 0 : trackAttached(IRGenerator::NotAttached);
220 0 : return false;
221 : }
222 :
223 0 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
224 :
225 0 : if (tryAttachProxyElement(obj, objId))
226 : return true;
227 :
228 : uint32_t index;
229 0 : Int32OperandId indexId;
230 0 : if (maybeGuardInt32Index(idVal_, getElemKeyValueId(), &index, &indexId)) {
231 0 : if (tryAttachTypedElement(obj, objId, index, indexId))
232 : return true;
233 0 : if (tryAttachDenseElement(obj, objId, index, indexId))
234 : return true;
235 0 : if (tryAttachDenseElementHole(obj, objId, index, indexId))
236 : return true;
237 0 : if (tryAttachUnboxedElementHole(obj, objId, index, indexId))
238 : return true;
239 0 : if (tryAttachArgumentsObjectArg(obj, objId, indexId))
240 : return true;
241 :
242 0 : trackAttached(IRGenerator::NotAttached);
243 0 : return false;
244 : }
245 :
246 0 : trackAttached(IRGenerator::NotAttached);
247 0 : return false;
248 : }
249 :
250 0 : if (nameOrSymbol) {
251 0 : if (tryAttachPrimitive(valId, id))
252 : return true;
253 0 : if (tryAttachStringLength(valId, id))
254 : return true;
255 0 : if (tryAttachMagicArgumentsName(valId, id))
256 : return true;
257 :
258 0 : trackAttached(IRGenerator::NotAttached);
259 0 : return false;
260 : }
261 :
262 0 : if (idVal_.isInt32()) {
263 0 : ValOperandId indexId = getElemKeyValueId();
264 0 : if (tryAttachStringChar(valId, indexId))
265 : return true;
266 0 : if (tryAttachMagicArgument(valId, indexId))
267 : return true;
268 :
269 0 : trackAttached(IRGenerator::NotAttached);
270 0 : return false;
271 : }
272 :
273 0 : trackAttached(IRGenerator::NotAttached);
274 0 : return false;
275 : }
276 :
277 : bool
278 0 : GetPropIRGenerator::tryAttachIdempotentStub()
279 : {
280 : // For idempotent ICs, only attach stubs which we can be sure have no side
281 : // effects and produce a result which the MIR in the calling code is able
282 : // to handle, since we do not have a pc to explicitly monitor the result.
283 :
284 0 : MOZ_ASSERT(idempotent());
285 :
286 0 : RootedObject obj(cx_, &val_.toObject());
287 0 : RootedId id(cx_, NameToId(idVal_.toString()->asAtom().asPropertyName()));
288 :
289 0 : ValOperandId valId(writer.setInputOperandId(0));
290 0 : ObjOperandId objId = writer.guardIsObject(valId);
291 0 : if (tryAttachNative(obj, objId, id))
292 : return true;
293 :
294 : // Object lengths are supported only if int32 results are allowed.
295 0 : if (tryAttachObjectLength(obj, objId, id))
296 : return true;
297 :
298 : // Also support native data properties on DOMProxy prototypes.
299 0 : if (GetProxyStubType(cx_, obj, id) == ProxyStubType::DOMUnshadowed)
300 0 : return tryAttachDOMProxyUnshadowed(obj, objId, id);
301 :
302 : return false;
303 : }
304 :
305 : static bool
306 0 : IsCacheableProtoChain(JSObject* obj, JSObject* holder)
307 : {
308 0 : while (obj != holder) {
309 : /*
310 : * We cannot assume that we find the holder object on the prototype
311 : * chain and must check for null proto. The prototype chain can be
312 : * altered during the lookupProperty call.
313 : */
314 0 : JSObject* proto = obj->staticPrototype();
315 0 : if (!proto || !proto->isNative())
316 : return false;
317 : obj = proto;
318 : }
319 : return true;
320 : }
321 :
322 : static bool
323 0 : IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, PropertyResult prop)
324 : {
325 0 : if (!prop || !IsCacheableProtoChain(obj, holder))
326 : return false;
327 :
328 0 : Shape* shape = prop.shape();
329 0 : if (!shape->isDataProperty())
330 : return false;
331 :
332 0 : return true;
333 : }
334 :
335 : static bool
336 0 : IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
337 : {
338 0 : if (!shape || !IsCacheableProtoChain(obj, holder))
339 : return false;
340 :
341 0 : if (!shape->hasGetterValue() || !shape->getterValue().isObject())
342 : return false;
343 :
344 0 : if (!shape->getterValue().toObject().is<JSFunction>())
345 : return false;
346 :
347 0 : JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
348 0 : if (!getter.isNativeWithCppEntry())
349 : return false;
350 :
351 0 : if (getter.isClassConstructor())
352 : return false;
353 :
354 : // Check for a getter that has jitinfo and whose jitinfo says it's
355 : // OK with both inner and outer objects.
356 0 : if (getter.hasJitInfo() && !getter.jitInfo()->needsOuterizedThisObject())
357 : return true;
358 :
359 : // For getters that need the WindowProxy (instead of the Window) as this
360 : // object, don't cache if obj is the Window, since our cache will pass that
361 : // instead of the WindowProxy.
362 0 : return !IsWindow(obj);
363 : }
364 :
365 : static bool
366 0 : IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
367 : bool* isTemporarilyUnoptimizable = nullptr)
368 : {
369 0 : if (!shape || !IsCacheableProtoChain(obj, holder))
370 : return false;
371 :
372 0 : if (!shape->hasGetterValue() || !shape->getterValue().isObject())
373 : return false;
374 :
375 0 : if (!shape->getterValue().toObject().is<JSFunction>())
376 : return false;
377 :
378 : // See IsCacheableGetPropCallNative.
379 0 : if (IsWindow(obj))
380 : return false;
381 :
382 0 : JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
383 0 : if (getter.isNativeWithCppEntry())
384 : return false;
385 :
386 : // Natives with jit entry can use the scripted path.
387 0 : if (getter.isNativeWithJitEntry())
388 : return true;
389 :
390 0 : if (!getter.hasScript()) {
391 0 : if (isTemporarilyUnoptimizable)
392 0 : *isTemporarilyUnoptimizable = true;
393 : return false;
394 : }
395 :
396 0 : if (getter.isClassConstructor())
397 : return false;
398 :
399 0 : return true;
400 : }
401 :
402 : static bool
403 0 : CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id)
404 : {
405 0 : if (obj->isNative()) {
406 : // Don't handle proto chains with resolve hooks.
407 0 : if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
408 : return false;
409 0 : if (obj->as<NativeObject>().contains(cx, id))
410 : return false;
411 0 : } else if (obj->is<UnboxedPlainObject>()) {
412 0 : if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id))
413 : return false;
414 0 : } else if (obj->is<TypedObject>()) {
415 0 : if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id))
416 : return false;
417 : } else {
418 : return false;
419 : }
420 :
421 : return true;
422 : }
423 :
424 : static bool
425 0 : CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id)
426 : {
427 0 : JSObject* curObj = obj;
428 : do {
429 0 : if (!CheckHasNoSuchOwnProperty(cx, curObj, id))
430 : return false;
431 :
432 0 : if (!curObj->isNative()) {
433 : // Non-native objects are only handled as the original receiver.
434 0 : if (curObj != obj)
435 : return false;
436 : }
437 :
438 0 : curObj = curObj->staticPrototype();
439 0 : } while (curObj);
440 :
441 : return true;
442 : }
443 :
444 : // Return whether obj is in some PreliminaryObjectArray and has a structure
445 : // that might change in the future.
446 : static bool
447 0 : IsPreliminaryObject(JSObject* obj)
448 : {
449 0 : if (obj->isSingleton())
450 : return false;
451 :
452 0 : AutoSweepObjectGroup sweep(obj->group());
453 0 : TypeNewScript* newScript = obj->group()->newScript(sweep);
454 0 : if (newScript && !newScript->analyzed())
455 : return true;
456 :
457 0 : if (obj->group()->maybePreliminaryObjects(sweep))
458 : return true;
459 :
460 0 : return false;
461 : }
462 :
463 : static bool
464 0 : IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
465 : jsbytecode* pc, GetPropertyResultFlags resultFlags)
466 : {
467 0 : if (shape)
468 : return false;
469 :
470 0 : MOZ_ASSERT(!holder);
471 :
472 : // Idempotent ICs may only attach missing-property stubs if undefined
473 : // results are explicitly allowed, since no monitoring is done of the
474 : // cache result.
475 0 : if (!pc && !(resultFlags & GetPropertyResultFlags::AllowUndefined))
476 : return false;
477 :
478 : // If we're doing a name lookup, we have to throw a ReferenceError. If
479 : // extra warnings are enabled, we may have to report a warning.
480 : // Note that Ion does not generate idempotent caches for JSOP_GETBOUNDNAME.
481 0 : if ((pc && *pc == JSOP_GETBOUNDNAME) || cx->realm()->behaviors().extraWarnings(cx))
482 : return false;
483 :
484 0 : return CheckHasNoSuchProperty(cx, obj, id);
485 : }
486 :
487 : enum NativeGetPropCacheability {
488 : CanAttachNone,
489 : CanAttachReadSlot,
490 : CanAttachCallGetter,
491 : };
492 :
493 : static NativeGetPropCacheability
494 0 : CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id,
495 : MutableHandleNativeObject holder, MutableHandleShape shape,
496 : jsbytecode* pc, GetPropertyResultFlags resultFlags,
497 : bool* isTemporarilyUnoptimizable)
498 : {
499 0 : MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
500 :
501 : // The lookup needs to be universally pure, otherwise we risk calling hooks out
502 : // of turn. We don't mind doing this even when purity isn't required, because we
503 : // only miss out on shape hashification, which is only a temporary perf cost.
504 : // The limits were arbitrarily set, anyways.
505 0 : JSObject* baseHolder = nullptr;
506 0 : PropertyResult prop;
507 0 : if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop))
508 : return CanAttachNone;
509 :
510 0 : MOZ_ASSERT(!holder);
511 0 : if (baseHolder) {
512 0 : if (!baseHolder->isNative())
513 : return CanAttachNone;
514 0 : holder.set(&baseHolder->as<NativeObject>());
515 : }
516 0 : shape.set(prop.maybeShape());
517 :
518 0 : if (IsCacheableGetPropReadSlot(obj, holder, prop))
519 : return CanAttachReadSlot;
520 :
521 0 : if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc, resultFlags))
522 : return CanAttachReadSlot;
523 :
524 : // Idempotent ICs cannot call getters, see tryAttachIdempotentStub.
525 0 : if (pc && (resultFlags & GetPropertyResultFlags::Monitored)) {
526 0 : if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable))
527 : return CanAttachCallGetter;
528 :
529 0 : if (IsCacheableGetPropCallNative(obj, holder, shape))
530 : return CanAttachCallGetter;
531 : }
532 :
533 : return CanAttachNone;
534 : }
535 :
536 : static void
537 0 : GuardGroupProto(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
538 : {
539 : // Uses the group to determine if the prototype is unchanged. If the
540 : // group's prototype is mutable, we must check the actual prototype,
541 : // otherwise checking the group is sufficient. This can be used if object
542 : // is not ShapedObject or if Shape has UNCACHEABLE_PROTO flag set.
543 :
544 0 : ObjectGroup* group = obj->groupRaw();
545 :
546 0 : if (group->hasUncacheableProto())
547 0 : writer.guardProto(objId, obj->staticPrototype());
548 : else
549 0 : writer.guardGroupForProto(objId, group);
550 0 : }
551 :
552 : // Guard that a given object has same class and same OwnProperties (excluding
553 : // dense elements and dynamic properties). Returns an OperandId for the unboxed
554 : // expando if it exists.
555 : static void
556 0 : TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId,
557 : Maybe<ObjOperandId>* expandoId)
558 : {
559 0 : if (obj->is<UnboxedPlainObject>()) {
560 0 : writer.guardGroupForLayout(objId, obj->group());
561 :
562 0 : if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
563 0 : expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId));
564 0 : writer.guardShapeForOwnProperties(expandoId->ref(), expando->lastProperty());
565 : } else {
566 0 : writer.guardNoUnboxedExpando(objId);
567 : }
568 0 : } else if (obj->is<TypedObject>()) {
569 0 : writer.guardGroupForLayout(objId, obj->group());
570 0 : } else if (obj->is<ProxyObject>()) {
571 0 : writer.guardShapeForClass(objId, obj->as<ProxyObject>().shape());
572 : } else {
573 0 : MOZ_ASSERT(obj->is<NativeObject>());
574 0 : writer.guardShapeForOwnProperties(objId, obj->as<NativeObject>().lastProperty());
575 : }
576 0 : }
577 :
578 : // Similar to |TestMatchingReceiver|, but specialized for NativeObject.
579 : static void
580 : TestMatchingNativeReceiver(CacheIRWriter& writer, NativeObject* obj, ObjOperandId objId)
581 : {
582 0 : writer.guardShapeForOwnProperties(objId, obj->lastProperty());
583 : }
584 :
585 : // Similar to |TestMatchingReceiver|, but specialized for ProxyObject.
586 : static void
587 : TestMatchingProxyReceiver(CacheIRWriter& writer, ProxyObject* obj, ObjOperandId objId)
588 : {
589 0 : writer.guardShapeForClass(objId, obj->shape());
590 : }
591 :
592 : // Adds additional guards if TestMatchingReceiver* does not also imply the
593 : // prototype.
594 : static void
595 0 : GeneratePrototypeGuardsForReceiver(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
596 : {
597 : // If receiver was marked UNCACHEABLE_PROTO, the previous shape guard
598 : // doesn't ensure the prototype is unchanged. In this case we must use the
599 : // group to check the prototype.
600 0 : if (obj->hasUncacheableProto()) {
601 0 : MOZ_ASSERT(obj->is<NativeObject>());
602 0 : GuardGroupProto(writer, obj, objId);
603 : }
604 :
605 : #ifdef DEBUG
606 : // The following cases already guaranteed the prototype is unchanged.
607 0 : if (obj->is<UnboxedPlainObject>())
608 0 : MOZ_ASSERT(!obj->group()->hasUncacheableProto());
609 0 : else if (obj->is<TypedObject>())
610 0 : MOZ_ASSERT(!obj->group()->hasUncacheableProto());
611 0 : else if (obj->is<ProxyObject>())
612 0 : MOZ_ASSERT(!obj->hasUncacheableProto());
613 : #endif // DEBUG
614 0 : }
615 :
616 : static void
617 0 : GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId objId)
618 : {
619 : // Assuming target property is on |holder|, generate appropriate guards to
620 : // ensure |holder| is still on the prototype chain of |obj| and we haven't
621 : // introduced any shadowing definitions.
622 : //
623 : // For each item in the proto chain before holder, we must ensure that
624 : // [[GetPrototypeOf]] still has the expected result, and that
625 : // [[GetOwnProperty]] has no definition of the target property.
626 : //
627 : //
628 : // Shape Teleporting Optimization
629 : // ------------------------------
630 : //
631 : // Starting with the assumption (and guideline to developers) that mutating
632 : // prototypes is an uncommon and fair-to-penalize operation we move cost
633 : // from the access side to the mutation side.
634 : //
635 : // Consider the following proto chain, with B defining a property 'x':
636 : //
637 : // D -> C -> B{x: 3} -> A -> null
638 : //
639 : // When accessing |D.x| we refer to D as the "receiver", and B as the
640 : // "holder". To optimize this access we need to ensure that neither D nor C
641 : // has since defined a shadowing property 'x'. Since C is a prototype that
642 : // we assume is rarely mutated we would like to avoid checking each time if
643 : // new properties are added. To do this we require that everytime C is
644 : // mutated that in addition to generating a new shape for itself, it will
645 : // walk the proto chain and generate new shapes for those objects on the
646 : // chain (B and A). As a result, checking the shape of D and B is
647 : // sufficient. Note that we do not care if the shape or properties of A
648 : // change since the lookup of 'x' will stop at B.
649 : //
650 : // The second condition we must verify is that the prototype chain was not
651 : // mutated. The same mechanism as above is used. When the prototype link is
652 : // changed, we generate a new shape for the object. If the object whose
653 : // link we are mutating is itself a prototype, we regenerate shapes down
654 : // the chain. This means the same two shape checks as above are sufficient.
655 : //
656 : // Unfortunately we don't stop there and add further caveats. We may set
657 : // the UNCACHEABLE_PROTO flag on the shape of an object to indicate that it
658 : // will not generate a new shape if its prototype link is modified. If the
659 : // object is itself a prototype we follow the shape chain and regenerate
660 : // shapes (if they aren't themselves uncacheable).
661 : //
662 : // Let's consider the effect of the UNCACHEABLE_PROTO flag on our example:
663 : // - D is uncacheable: Add check that D still links to C
664 : // - C is uncacheable: Modifying C.__proto__ will still reshape B (if B is
665 : // not uncacheable)
666 : // - B is uncacheable: Add shape check C since B will not reshape OR check
667 : // proto of D and C
668 : //
669 : // See:
670 : // - ReshapeForProtoMutation
671 : // - ReshapeForShadowedProp
672 :
673 0 : MOZ_ASSERT(holder);
674 0 : MOZ_ASSERT(obj != holder);
675 :
676 : // Only DELEGATE objects participate in teleporting so peel off the first
677 : // object in the chain if needed and handle it directly.
678 0 : JSObject* pobj = obj;
679 0 : if (!obj->isDelegate()) {
680 : // TestMatchingReceiver does not always ensure the prototype is
681 : // unchanged, so generate extra guards as needed.
682 0 : GeneratePrototypeGuardsForReceiver(writer, obj, objId);
683 :
684 0 : pobj = obj->staticPrototype();
685 : }
686 0 : MOZ_ASSERT(pobj->isDelegate());
687 :
688 : // In the common case, holder has a cacheable prototype and will regenerate
689 : // its shape if any (delegate) objects in the proto chain are updated.
690 0 : if (!holder->hasUncacheableProto())
691 0 : return;
692 :
693 : // If already at the holder, no further proto checks are needed.
694 0 : if (pobj == holder)
695 : return;
696 :
697 : // NOTE: We could be clever and look for a middle prototype to shape check
698 : // and elide some (but not all) of the group checks. Unless we have
699 : // real-world examples, let's avoid the complexity.
700 :
701 : // Synchronize pobj and protoId.
702 0 : MOZ_ASSERT(pobj == obj || pobj == obj->staticPrototype());
703 : ObjOperandId protoId = (pobj == obj) ? objId
704 0 : : writer.loadProto(objId);
705 :
706 : // Guard prototype links from |pobj| to |holder|.
707 0 : while (pobj != holder) {
708 0 : pobj = pobj->staticPrototype();
709 0 : protoId = writer.loadProto(protoId);
710 :
711 0 : writer.guardSpecificObject(protoId, pobj);
712 : }
713 : }
714 :
715 : static void
716 0 : GeneratePrototypeHoleGuards(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
717 : {
718 0 : if (obj->hasUncacheableProto())
719 0 : GuardGroupProto(writer, obj, objId);
720 :
721 0 : JSObject* pobj = obj->staticPrototype();
722 0 : while (pobj) {
723 0 : ObjOperandId protoId = writer.loadObject(pobj);
724 :
725 : // If shape doesn't imply proto, additional guards are needed.
726 0 : if (pobj->hasUncacheableProto())
727 0 : GuardGroupProto(writer, pobj, protoId);
728 :
729 : // Make sure the shape matches, to avoid non-dense elements or anything
730 : // else that is being checked by CanAttachDenseElementHole.
731 0 : writer.guardShape(protoId, pobj->as<NativeObject>().lastProperty());
732 :
733 : // Also make sure there are no dense elements.
734 0 : writer.guardNoDenseElements(protoId);
735 :
736 0 : pobj = pobj->staticPrototype();
737 : }
738 0 : }
739 :
740 : // Similar to |TestMatchingReceiver|, but for the holder object (when it
741 : // differs from the receiver). The holder may also be the expando of the
742 : // receiver if it exists.
743 : static void
744 0 : TestMatchingHolder(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
745 : {
746 : // The GeneratePrototypeGuards + TestMatchingHolder checks only support
747 : // prototype chains composed of NativeObject (excluding the receiver
748 : // itself).
749 0 : MOZ_ASSERT(obj->is<NativeObject>());
750 :
751 0 : writer.guardShapeForOwnProperties(objId, obj->as<NativeObject>().lastProperty());
752 0 : }
753 :
754 : static bool
755 0 : UncacheableProtoOnChain(JSObject* obj)
756 : {
757 : while (true) {
758 0 : if (obj->hasUncacheableProto())
759 : return true;
760 :
761 0 : obj = obj->staticPrototype();
762 0 : if (!obj)
763 : return false;
764 : }
765 : }
766 :
767 : static void
768 0 : ShapeGuardProtoChain(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
769 : {
770 : while (true) {
771 : // Guard on the proto if the shape does not imply the proto.
772 0 : bool guardProto = obj->hasUncacheableProto();
773 :
774 0 : obj = obj->staticPrototype();
775 0 : if (!obj && !guardProto)
776 : return;
777 :
778 0 : objId = writer.loadProto(objId);
779 :
780 0 : if (guardProto)
781 0 : writer.guardSpecificObject(objId, obj);
782 :
783 2770 : if (!obj)
784 : return;
785 :
786 5540 : writer.guardShape(objId, obj->as<NativeObject>().shape());
787 2770 : }
788 : }
789 :
790 : // For cross compartment guards we shape-guard the prototype chain to avoid
791 : // referencing the holder object.
792 : //
793 : // This peels off the first layer because it's guarded against obj == holder.
794 : static void
795 0 : ShapeGuardProtoChainForCrossCompartmentHolder(CacheIRWriter& writer, JSObject* obj,
796 : ObjOperandId objId, JSObject* holder,
797 : Maybe<ObjOperandId>* holderId)
798 : {
799 116 : MOZ_ASSERT(obj != holder);
800 0 : MOZ_ASSERT(holder);
801 : while (true) {
802 0 : obj = obj->staticPrototype();
803 0 : MOZ_ASSERT(obj);
804 :
805 162 : objId = writer.loadProto(objId);
806 0 : if (obj == holder) {
807 116 : TestMatchingHolder(writer, obj, objId);
808 116 : holderId->emplace(objId);
809 116 : return;
810 : } else {
811 92 : writer.guardShapeForOwnProperties(objId, obj->as<NativeObject>().shape());
812 : }
813 : }
814 : }
815 :
816 : enum class SlotReadType {
817 : Normal,
818 : CrossCompartment
819 : };
820 :
821 : template <SlotReadType MaybeCrossCompartment = SlotReadType::Normal>
822 : static void
823 6362 : EmitReadSlotGuard(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
824 : ObjOperandId objId, Maybe<ObjOperandId>* holderId)
825 : {
826 12724 : Maybe<ObjOperandId> expandoId;
827 6362 : TestMatchingReceiver(writer, obj, objId, &expandoId);
828 :
829 6362 : if (obj != holder) {
830 0 : if (holder) {
831 : if (MaybeCrossCompartment == SlotReadType::CrossCompartment) {
832 : // Guard proto chain integrity.
833 : // We use a variant of guards that avoid baking in any cross-compartment
834 : // object pointers.
835 116 : ShapeGuardProtoChainForCrossCompartmentHolder(writer, obj, objId, holder,
836 : holderId);
837 : } else {
838 : // Guard proto chain integrity.
839 1378 : GeneratePrototypeGuards(writer, obj, holder, objId);
840 :
841 : // Guard on the holder's shape.
842 1378 : holderId->emplace(writer.loadObject(holder));
843 1378 : TestMatchingHolder(writer, holder, holderId->ref());
844 : }
845 : } else {
846 : // The property does not exist. Guard on everything in the prototype
847 : // chain. This is guaranteed to see only Native objects because of
848 : // CanAttachNativeGetProp().
849 0 : ShapeGuardProtoChain(writer, obj, objId);
850 : }
851 0 : } else if (obj->is<UnboxedPlainObject>()) {
852 1 : holderId->emplace(*expandoId);
853 : } else {
854 4032 : holderId->emplace(objId);
855 : }
856 6362 : }
857 :
858 : template <SlotReadType MaybeCrossCompartment = SlotReadType::Normal>
859 : static void
860 5912 : EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
861 : Shape* shape, ObjOperandId objId)
862 : {
863 11824 : Maybe<ObjOperandId> holderId;
864 5912 : EmitReadSlotGuard<MaybeCrossCompartment>(writer, obj, holder, objId, &holderId);
865 :
866 0 : if (obj == holder && obj->is<UnboxedPlainObject>())
867 0 : holder = obj->as<UnboxedPlainObject>().maybeExpando();
868 :
869 : // Slot access.
870 5912 : if (holder) {
871 10698 : MOZ_ASSERT(holderId->valid());
872 0 : EmitLoadSlotResult(writer, *holderId, &holder->as<NativeObject>(), shape);
873 : } else {
874 563 : MOZ_ASSERT(holderId.isNothing());
875 : writer.loadUndefinedResult();
876 : }
877 5912 : }
878 :
879 : static void
880 0 : EmitReadSlotReturn(CacheIRWriter& writer, JSObject*, JSObject* holder, Shape* shape,
881 : bool wrapResult = false)
882 : {
883 : // Slot access.
884 5912 : if (holder) {
885 5349 : MOZ_ASSERT(shape);
886 5349 : if (wrapResult)
887 : writer.wrapResult();
888 : writer.typeMonitorResult();
889 : } else {
890 : // Normally for this op, the result would have to be monitored by TI.
891 : // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure
892 : // that undefined is already registered with the type-set, this can be avoided.
893 : writer.returnFromIC();
894 : }
895 5912 : }
896 :
897 : static void
898 0 : EmitCallGetterResultNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
899 : Shape* shape, ObjOperandId receiverId)
900 : {
901 511 : if (IsCacheableGetPropCallNative(obj, holder, shape)) {
902 276 : JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
903 276 : MOZ_ASSERT(target->isNativeWithCppEntry());
904 0 : writer.callNativeGetterResult(receiverId, target);
905 : writer.typeMonitorResult();
906 : return;
907 : }
908 :
909 235 : MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
910 :
911 235 : JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
912 235 : MOZ_ASSERT(target->hasJitEntry());
913 0 : writer.callScriptedGetterResult(receiverId, target);
914 : writer.typeMonitorResult();
915 : }
916 :
917 : static void
918 492 : EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape,
919 : ObjOperandId objId, ObjOperandId receiverId, ICState::Mode mode)
920 : {
921 : // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
922 : // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
923 : // require outerizing).
924 0 : if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
925 834 : Maybe<ObjOperandId> expandoId;
926 417 : TestMatchingReceiver(writer, obj, objId, &expandoId);
927 :
928 0 : if (obj != holder) {
929 307 : GeneratePrototypeGuards(writer, obj, holder, objId);
930 :
931 : // Guard on the holder's shape.
932 307 : ObjOperandId holderId = writer.loadObject(holder);
933 307 : TestMatchingHolder(writer, holder, holderId);
934 : }
935 : } else {
936 75 : writer.guardHasGetterSetter(objId, shape);
937 : }
938 :
939 492 : EmitCallGetterResultNoGuards(writer, obj, holder, shape, receiverId);
940 492 : }
941 :
942 : static void
943 : EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
944 : Shape* shape, ObjOperandId objId, ICState::Mode mode)
945 : {
946 0 : EmitCallGetterResult(writer, obj, holder, shape, objId, objId, mode);
947 : }
948 :
949 : void
950 280 : GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing)
951 : {
952 0 : MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
953 :
954 : // The stub handles the missing-properties case only if we're seeing one
955 : // now, to make sure Ion ICs correctly monitor the undefined type.
956 :
957 0 : if (cacheKind_ == CacheKind::GetProp || cacheKind_ == CacheKind::GetPropSuper) {
958 402 : writer.megamorphicLoadSlotResult(objId, JSID_TO_ATOM(id)->asPropertyName(),
959 0 : handleMissing);
960 : } else {
961 0 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
962 0 : writer.megamorphicLoadSlotByValueResult(objId, getElemKeyValueId(), handleMissing);
963 : }
964 560 : writer.typeMonitorResult();
965 :
966 280 : trackAttached(handleMissing ? "MegamorphicMissingNativeSlot" : "MegamorphicNativeSlot");
967 0 : }
968 :
969 : bool
970 0 : GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id)
971 : {
972 0 : RootedShape shape(cx_);
973 0 : RootedNativeObject holder(cx_);
974 :
975 28872 : NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_,
976 : resultFlags_,
977 0 : isTemporarilyUnoptimizable_);
978 0 : switch (type) {
979 : case CanAttachNone:
980 : return false;
981 : case CanAttachReadSlot:
982 0 : if (mode_ == ICState::Mode::Megamorphic) {
983 0 : attachMegamorphicNativeSlot(objId, id, holder == nullptr);
984 0 : return true;
985 : }
986 :
987 0 : maybeEmitIdGuard(id);
988 5589 : if (holder) {
989 0 : EnsureTrackPropertyTypes(cx_, holder, id);
990 : // See the comment in StripPreliminaryObjectStubs.
991 0 : if (IsPreliminaryObject(obj))
992 0 : preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
993 : else
994 0 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
995 : }
996 16767 : EmitReadSlotResult(writer, obj, holder, shape, objId);
997 16767 : EmitReadSlotReturn(writer, obj, holder, shape);
998 :
999 0 : trackAttached("NativeSlot");
1000 0 : return true;
1001 : case CanAttachCallGetter: {
1002 : // |super.prop| accesses use a |this| value that differs from lookup object
1003 984 : MOZ_ASSERT(!idempotent());
1004 0 : ObjOperandId receiverId = isSuper() ? writer.guardIsObject(getSuperReceiverValueId())
1005 501 : : objId;
1006 492 : maybeEmitIdGuard(id);
1007 1968 : EmitCallGetterResult(writer, obj, holder, shape, objId, receiverId, mode_);
1008 :
1009 1 : trackAttached("NativeGetter");
1010 : return true;
1011 : }
1012 : }
1013 :
1014 0 : MOZ_CRASH("Bad NativeGetPropCacheability");
1015 : }
1016 :
1017 : bool
1018 0 : GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id)
1019 : {
1020 : // Attach a stub when the receiver is a WindowProxy and we can do the lookup
1021 : // on the Window (the global object).
1022 :
1023 0 : if (!IsWindowProxy(obj))
1024 : return false;
1025 :
1026 : // If we're megamorphic prefer a generic proxy stub that handles a lot more
1027 : // cases.
1028 0 : if (mode_ == ICState::Mode::Megamorphic)
1029 : return false;
1030 :
1031 : // This must be a WindowProxy for the current Window/global. Else it would
1032 : // be a cross-compartment wrapper and IsWindowProxy returns false for
1033 : // those.
1034 0 : MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
1035 0 : MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
1036 :
1037 : // Now try to do the lookup on the Window (the current global).
1038 0 : HandleObject windowObj = cx_->global();
1039 0 : RootedShape shape(cx_);
1040 0 : RootedNativeObject holder(cx_);
1041 0 : NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_,
1042 : resultFlags_,
1043 0 : isTemporarilyUnoptimizable_);
1044 0 : switch (type) {
1045 : case CanAttachNone:
1046 : return false;
1047 :
1048 : case CanAttachReadSlot: {
1049 0 : maybeEmitIdGuard(id);
1050 0 : writer.guardClass(objId, GuardClassKind::WindowProxy);
1051 :
1052 0 : ObjOperandId windowObjId = writer.loadObject(windowObj);
1053 0 : EmitReadSlotResult(writer, windowObj, holder, shape, windowObjId);
1054 0 : EmitReadSlotReturn(writer, windowObj, holder, shape);
1055 :
1056 0 : trackAttached("WindowProxySlot");
1057 : return true;
1058 : }
1059 :
1060 : case CanAttachCallGetter: {
1061 0 : if (!IsCacheableGetPropCallNative(windowObj, holder, shape))
1062 : return false;
1063 :
1064 : // Make sure the native getter is okay with the IC passing the Window
1065 : // instead of the WindowProxy as |this| value.
1066 0 : JSFunction* callee = &shape->getterObject()->as<JSFunction>();
1067 0 : MOZ_ASSERT(callee->isNative());
1068 0 : if (!callee->hasJitInfo() || callee->jitInfo()->needsOuterizedThisObject())
1069 : return false;
1070 :
1071 : // If a |super| access, it is not worth the complexity to attach an IC.
1072 0 : if (isSuper())
1073 : return false;
1074 :
1075 : // Guard the incoming object is a WindowProxy and inline a getter call based
1076 : // on the Window object.
1077 0 : maybeEmitIdGuard(id);
1078 0 : writer.guardClass(objId, GuardClassKind::WindowProxy);
1079 0 : ObjOperandId windowObjId = writer.loadObject(windowObj);
1080 0 : EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId, mode_);
1081 :
1082 0 : trackAttached("WindowProxyGetter");
1083 0 : return true;
1084 : }
1085 : }
1086 :
1087 0 : MOZ_CRASH("Unreachable");
1088 : }
1089 :
1090 : bool
1091 0 : GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId,
1092 : HandleId id)
1093 : {
1094 : // We can only optimize this very wrapper-handler, because others might
1095 : // have a security policy.
1096 0 : if (!IsWrapper(obj) || Wrapper::wrapperHandler(obj) != &CrossCompartmentWrapper::singleton)
1097 : return false;
1098 :
1099 : // If we're megamorphic prefer a generic proxy stub that handles a lot more
1100 : // cases.
1101 438 : if (mode_ == ICState::Mode::Megamorphic)
1102 : return false;
1103 :
1104 856 : RootedObject unwrapped(cx_, Wrapper::wrappedObject(obj));
1105 856 : MOZ_ASSERT(unwrapped == UnwrapOneChecked(obj));
1106 :
1107 : // If we allowed different zones we would have to wrap strings.
1108 1284 : if (unwrapped->compartment()->zone() != cx_->compartment()->zone())
1109 : return false;
1110 :
1111 : // Take the unwrapped object's global, and wrap in a
1112 : // this-compartment wrapper. This is what will be stored in the IC
1113 : // keep the compartment alive.
1114 0 : RootedObject wrappedTargetGlobal(cx_, &unwrapped->deprecatedGlobal());
1115 0 : if (!cx_->compartment()->wrap(cx_, &wrappedTargetGlobal))
1116 : return false;
1117 :
1118 365 : bool isWindowProxy = false;
1119 1095 : RootedShape shape(cx_);
1120 0 : RootedNativeObject holder(cx_);
1121 :
1122 : // Enter realm of target since some checks have side-effects
1123 : // such as de-lazifying type info.
1124 : {
1125 0 : AutoRealm ar(cx_, unwrapped);
1126 :
1127 : // The first CCW for iframes is almost always wrapping another WindowProxy
1128 : // so we optimize for that case as well.
1129 365 : isWindowProxy = IsWindowProxy(unwrapped);
1130 365 : if (isWindowProxy) {
1131 0 : MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == unwrapped->realm()->maybeGlobal());
1132 0 : unwrapped = cx_->global();
1133 0 : MOZ_ASSERT(unwrapped);
1134 : }
1135 :
1136 : NativeGetPropCacheability canCache =
1137 0 : CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_,
1138 365 : resultFlags_, isTemporarilyUnoptimizable_);
1139 365 : if (canCache != CanAttachReadSlot)
1140 0 : return false;
1141 :
1142 242 : if (holder) {
1143 : // Need to be in the compartment of the holder to
1144 : // call EnsureTrackPropertyTypes
1145 462 : EnsureTrackPropertyTypes(cx_, holder, id);
1146 0 : if (unwrapped == holder) {
1147 : // See the comment in StripPreliminaryObjectStubs.
1148 115 : if (IsPreliminaryObject(unwrapped))
1149 42 : preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
1150 : else
1151 0 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
1152 : }
1153 : } else {
1154 : // UNCACHEABLE_PROTO may result in guards against specific (cross-compartment)
1155 : // prototype objects, so don't try to attach IC if we see the flag at all.
1156 11 : if (UncacheableProtoOnChain(unwrapped)) {
1157 : return false;
1158 : }
1159 : }
1160 : }
1161 :
1162 0 : maybeEmitIdGuard(id);
1163 242 : writer.guardIsProxy(objId);
1164 242 : writer.guardHasProxyHandler(objId, Wrapper::wrapperHandler(obj));
1165 :
1166 : // Load the object wrapped by the CCW
1167 0 : ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
1168 :
1169 : // If the compartment of the wrapped object is different we should fail.
1170 726 : writer.guardCompartment(wrapperTargetId, wrappedTargetGlobal, unwrapped->compartment());
1171 :
1172 0 : ObjOperandId unwrappedId = wrapperTargetId;
1173 0 : if (isWindowProxy) {
1174 : // For the WindowProxy case also unwrap the inner window.
1175 : // We avoid loadObject, because storing cross compartment objects in
1176 : // stubs / JIT code is tricky.
1177 0 : writer.guardClass(wrapperTargetId, GuardClassKind::WindowProxy);
1178 0 : unwrappedId = writer.loadWrapperTarget(wrapperTargetId);
1179 : }
1180 :
1181 726 : EmitReadSlotResult<SlotReadType::CrossCompartment>(writer, unwrapped, holder, shape, unwrappedId);
1182 726 : EmitReadSlotReturn(writer, unwrapped, holder, shape, /* wrapResult = */ true);
1183 :
1184 0 : trackAttached("CCWSlot");
1185 242 : return true;
1186 : }
1187 :
1188 : static bool
1189 0 : GetXrayExpandoShapeWrapper(JSContext* cx, HandleObject xray, MutableHandleObject wrapper)
1190 : {
1191 0 : Value v = GetProxyReservedSlot(xray, GetXrayJitInfo()->xrayHolderSlot);
1192 0 : if (v.isObject()) {
1193 0 : NativeObject* holder = &v.toObject().as<NativeObject>();
1194 0 : v = holder->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot);
1195 0 : if (v.isObject()) {
1196 0 : RootedNativeObject expando(cx, &UncheckedUnwrap(&v.toObject())->as<NativeObject>());
1197 0 : wrapper.set(NewWrapperWithObjectShape(cx, expando));
1198 0 : return wrapper != nullptr;
1199 : }
1200 : }
1201 0 : wrapper.set(nullptr);
1202 0 : return true;
1203 : }
1204 :
1205 : bool
1206 495 : GetPropIRGenerator::tryAttachXrayCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId,
1207 : HandleId id)
1208 : {
1209 990 : if (!IsProxy(obj))
1210 : return false;
1211 :
1212 219 : XrayJitInfo* info = GetXrayJitInfo();
1213 657 : if (!info || !info->isCrossCompartmentXray(GetProxyHandler(obj)))
1214 : return false;
1215 :
1216 0 : if (!info->globalHasExclusiveExpandos(cx_->global()))
1217 : return false;
1218 :
1219 0 : RootedObject target(cx_, UncheckedUnwrap(obj));
1220 :
1221 0 : RootedObject expandoShapeWrapper(cx_);
1222 0 : if (!GetXrayExpandoShapeWrapper(cx_, obj, &expandoShapeWrapper)) {
1223 0 : cx_->recoverFromOutOfMemory();
1224 0 : return false;
1225 : }
1226 :
1227 : // Look for a getter we can call on the xray or its prototype chain.
1228 0 : Rooted<PropertyDescriptor> desc(cx_);
1229 0 : RootedObject holder(cx_, obj);
1230 0 : AutoObjectVector prototypes(cx_);
1231 0 : AutoObjectVector prototypeExpandoShapeWrappers(cx_);
1232 : while (true) {
1233 0 : if (!GetOwnPropertyDescriptor(cx_, holder, id, &desc)) {
1234 0 : cx_->clearPendingException();
1235 0 : return false;
1236 : }
1237 0 : if (desc.object())
1238 : break;
1239 0 : if (!GetPrototype(cx_, holder, &holder)) {
1240 0 : cx_->clearPendingException();
1241 0 : return false;
1242 : }
1243 0 : if (!holder || !IsProxy(holder) || !info->isCrossCompartmentXray(GetProxyHandler(holder)))
1244 : return false;
1245 0 : RootedObject prototypeExpandoShapeWrapper(cx_);
1246 0 : if (!GetXrayExpandoShapeWrapper(cx_, holder, &prototypeExpandoShapeWrapper) ||
1247 0 : !prototypes.append(holder) ||
1248 0 : !prototypeExpandoShapeWrappers.append(prototypeExpandoShapeWrapper))
1249 : {
1250 0 : cx_->recoverFromOutOfMemory();
1251 0 : return false;
1252 : }
1253 : }
1254 0 : if (!desc.isAccessorDescriptor())
1255 : return false;
1256 :
1257 0 : RootedObject getter(cx_, desc.getterObject());
1258 0 : if (!getter || !getter->is<JSFunction>() || !getter->as<JSFunction>().isNative())
1259 : return false;
1260 :
1261 0 : maybeEmitIdGuard(id);
1262 0 : writer.guardIsProxy(objId);
1263 0 : writer.guardHasProxyHandler(objId, GetProxyHandler(obj));
1264 :
1265 : // Load the object wrapped by the CCW
1266 0 : ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
1267 :
1268 : // Test the wrapped object's class. The properties held by xrays or their
1269 : // prototypes will be invariant for objects of a given class, except for
1270 : // changes due to xray expandos or xray prototype mutations.
1271 0 : writer.guardAnyClass(wrapperTargetId, target->getClass());
1272 :
1273 : // Make sure the expandos on the xray and its prototype chain match up with
1274 : // what we expect. The expando shape needs to be consistent, to ensure it
1275 : // has not had any shadowing properties added, and the expando cannot have
1276 : // any custom prototype (xray prototypes are stable otherwise).
1277 : //
1278 : // We can only do this for xrays with exclusive access to their expandos
1279 : // (as we checked earlier), which store a pointer to their expando
1280 : // directly. Xrays in other compartments may share their expandos with each
1281 : // other and a VM call is needed just to find the expando.
1282 0 : writer.guardXrayExpandoShapeAndDefaultProto(objId, expandoShapeWrapper);
1283 0 : for (size_t i = 0; i < prototypes.length(); i++) {
1284 0 : JSObject* proto = prototypes[i];
1285 0 : ObjOperandId protoId = writer.loadObject(proto);
1286 0 : writer.guardXrayExpandoShapeAndDefaultProto(protoId, prototypeExpandoShapeWrappers[i]);
1287 : }
1288 :
1289 0 : writer.callNativeGetterResult(objId, &getter->as<JSFunction>());
1290 0 : writer.typeMonitorResult();
1291 :
1292 0 : trackAttached("XrayGetter");
1293 0 : return true;
1294 : }
1295 :
1296 : bool
1297 0 : GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
1298 : bool handleDOMProxies)
1299 : {
1300 418 : MOZ_ASSERT(obj->is<ProxyObject>());
1301 :
1302 0 : writer.guardIsProxy(objId);
1303 :
1304 209 : if (!handleDOMProxies) {
1305 : // Ensure that the incoming object is not a DOM proxy, so that we can get to
1306 : // the specialized stubs
1307 0 : writer.guardNotDOMProxy(objId);
1308 : }
1309 :
1310 209 : if (cacheKind_ == CacheKind::GetProp || mode_ == ICState::Mode::Specialized) {
1311 0 : MOZ_ASSERT(!isSuper());
1312 0 : maybeEmitIdGuard(id);
1313 0 : writer.callProxyGetResult(objId, id);
1314 : } else {
1315 : // Attach a stub that handles every id.
1316 5 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
1317 0 : MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
1318 10 : MOZ_ASSERT(!isSuper());
1319 0 : writer.callProxyGetByValueResult(objId, getElemKeyValueId());
1320 : }
1321 :
1322 418 : writer.typeMonitorResult();
1323 :
1324 0 : trackAttached("GenericProxy");
1325 209 : return true;
1326 : }
1327 :
1328 : ObjOperandId
1329 0 : IRGenerator::guardDOMProxyExpandoObjectAndShape(JSObject* obj, ObjOperandId objId,
1330 : const Value& expandoVal, JSObject* expandoObj)
1331 : {
1332 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
1333 :
1334 0 : TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
1335 :
1336 : // Shape determines Class, so now it must be a DOM proxy.
1337 0 : ValOperandId expandoValId;
1338 0 : if (expandoVal.isObject())
1339 0 : expandoValId = writer.loadDOMExpandoValue(objId);
1340 : else
1341 0 : expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
1342 :
1343 : // Guard the expando is an object and shape guard.
1344 0 : ObjOperandId expandoObjId = writer.guardIsObject(expandoValId);
1345 0 : TestMatchingHolder(writer, expandoObj, expandoObjId);
1346 0 : return expandoObjId;
1347 : }
1348 :
1349 : bool
1350 0 : GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id)
1351 : {
1352 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
1353 :
1354 0 : RootedValue expandoVal(cx_, GetProxyPrivate(obj));
1355 0 : RootedObject expandoObj(cx_);
1356 0 : if (expandoVal.isObject()) {
1357 0 : expandoObj = &expandoVal.toObject();
1358 : } else {
1359 0 : MOZ_ASSERT(!expandoVal.isUndefined(),
1360 : "How did a missing expando manage to shadow things?");
1361 0 : auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
1362 0 : MOZ_ASSERT(expandoAndGeneration);
1363 0 : expandoObj = &expandoAndGeneration->expando.toObject();
1364 : }
1365 :
1366 : // Try to do the lookup on the expando object.
1367 0 : RootedNativeObject holder(cx_);
1368 0 : RootedShape propShape(cx_);
1369 : NativeGetPropCacheability canCache =
1370 0 : CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &propShape, pc_,
1371 0 : resultFlags_, isTemporarilyUnoptimizable_);
1372 0 : if (canCache != CanAttachReadSlot && canCache != CanAttachCallGetter)
1373 : return false;
1374 0 : if (!holder)
1375 : return false;
1376 :
1377 0 : MOZ_ASSERT(holder == expandoObj);
1378 :
1379 0 : maybeEmitIdGuard(id);
1380 : ObjOperandId expandoObjId =
1381 0 : guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
1382 :
1383 0 : if (canCache == CanAttachReadSlot) {
1384 : // Load from the expando's slots.
1385 0 : EmitLoadSlotResult(writer, expandoObjId, &expandoObj->as<NativeObject>(), propShape);
1386 0 : writer.typeMonitorResult();
1387 : } else {
1388 : // Call the getter. Note that we pass objId, the DOM proxy, as |this|
1389 : // and not the expando object.
1390 0 : MOZ_ASSERT(canCache == CanAttachCallGetter);
1391 0 : EmitCallGetterResultNoGuards(writer, expandoObj, expandoObj, propShape, objId);
1392 : }
1393 :
1394 0 : trackAttached("DOMProxyExpando");
1395 0 : return true;
1396 : }
1397 :
1398 : bool
1399 0 : GetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id)
1400 : {
1401 0 : MOZ_ASSERT(!isSuper());
1402 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
1403 :
1404 0 : maybeEmitIdGuard(id);
1405 0 : TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
1406 0 : writer.callProxyGetResult(objId, id);
1407 0 : writer.typeMonitorResult();
1408 :
1409 0 : trackAttached("DOMProxyShadowed");
1410 0 : return true;
1411 : }
1412 :
1413 : // Callers are expected to have already guarded on the shape of the
1414 : // object, which guarantees the object is a DOM proxy.
1415 : static void
1416 0 : CheckDOMProxyExpandoDoesNotShadow(CacheIRWriter& writer, JSObject* obj, jsid id,
1417 : ObjOperandId objId)
1418 : {
1419 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
1420 :
1421 0 : Value expandoVal = GetProxyPrivate(obj);
1422 :
1423 10 : ValOperandId expandoId;
1424 0 : if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
1425 1 : auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
1426 1 : expandoId = writer.loadDOMExpandoValueGuardGeneration(objId, expandoAndGeneration);
1427 0 : expandoVal = expandoAndGeneration->expando;
1428 : } else {
1429 0 : expandoId = writer.loadDOMExpandoValue(objId);
1430 : }
1431 :
1432 10 : if (expandoVal.isUndefined()) {
1433 : // Guard there's no expando object.
1434 0 : writer.guardType(expandoId, JSVAL_TYPE_UNDEFINED);
1435 0 : } else if (expandoVal.isObject()) {
1436 : // Guard the proxy either has no expando object or, if it has one, that
1437 : // the shape matches the current expando object.
1438 1 : NativeObject& expandoObj = expandoVal.toObject().as<NativeObject>();
1439 0 : MOZ_ASSERT(!expandoObj.containsPure(id));
1440 1 : writer.guardDOMExpandoMissingOrGuardShape(expandoId, expandoObj.lastProperty());
1441 : } else {
1442 0 : MOZ_CRASH("Invalid expando value");
1443 : }
1444 0 : }
1445 :
1446 : bool
1447 0 : GetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id)
1448 : {
1449 10 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
1450 :
1451 0 : RootedObject checkObj(cx_, obj->staticPrototype());
1452 0 : if (!checkObj)
1453 : return false;
1454 :
1455 0 : RootedNativeObject holder(cx_);
1456 30 : RootedShape shape(cx_);
1457 50 : NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape,
1458 : pc_, resultFlags_,
1459 10 : isTemporarilyUnoptimizable_);
1460 10 : if (canCache == CanAttachNone)
1461 : return false;
1462 :
1463 10 : maybeEmitIdGuard(id);
1464 :
1465 : // Guard that our expando object hasn't started shadowing this property.
1466 20 : TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
1467 0 : CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
1468 :
1469 10 : if (holder) {
1470 : // Found the property on the prototype chain. Treat it like a native
1471 : // getprop.
1472 20 : GeneratePrototypeGuards(writer, obj, holder, objId);
1473 :
1474 : // Guard on the holder of the property.
1475 0 : ObjOperandId holderId = writer.loadObject(holder);
1476 10 : TestMatchingHolder(writer, holder, holderId);
1477 :
1478 10 : if (canCache == CanAttachReadSlot) {
1479 6 : EmitLoadSlotResult(writer, holderId, holder, shape);
1480 3 : writer.typeMonitorResult();
1481 : } else {
1482 : // EmitCallGetterResultNoGuards expects |obj| to be the object the
1483 : // property is on to do some checks. Since we actually looked at
1484 : // checkObj, and no extra guards will be generated, we can just
1485 : // pass that instead.
1486 7 : MOZ_ASSERT(canCache == CanAttachCallGetter);
1487 14 : MOZ_ASSERT(!isSuper());
1488 0 : EmitCallGetterResultNoGuards(writer, checkObj, holder, shape, objId);
1489 : }
1490 : } else {
1491 : // Property was not found on the prototype chain. Deoptimize down to
1492 : // proxy get call.
1493 0 : MOZ_ASSERT(!isSuper());
1494 0 : writer.callProxyGetResult(objId, id);
1495 0 : writer.typeMonitorResult();
1496 : }
1497 :
1498 0 : trackAttached("DOMProxyUnshadowed");
1499 10 : return true;
1500 : }
1501 :
1502 : bool
1503 495 : GetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id)
1504 : {
1505 0 : ProxyStubType type = GetProxyStubType(cx_, obj, id);
1506 495 : if (type == ProxyStubType::None)
1507 : return false;
1508 :
1509 : // The proxy stubs don't currently support |super| access.
1510 438 : if (isSuper())
1511 : return false;
1512 :
1513 219 : if (mode_ == ICState::Mode::Megamorphic)
1514 12 : return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
1515 :
1516 207 : switch (type) {
1517 : case ProxyStubType::None:
1518 : break;
1519 : case ProxyStubType::DOMExpando:
1520 0 : if (tryAttachDOMProxyExpando(obj, objId, id))
1521 : return true;
1522 0 : if (*isTemporarilyUnoptimizable_) {
1523 : // Scripted getter without JIT code. Just wait.
1524 : return false;
1525 : }
1526 : MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
1527 : case ProxyStubType::DOMShadowed:
1528 0 : return tryAttachDOMProxyShadowed(obj, objId, id);
1529 : case ProxyStubType::DOMUnshadowed:
1530 10 : if (tryAttachDOMProxyUnshadowed(obj, objId, id))
1531 : return true;
1532 0 : if (*isTemporarilyUnoptimizable_) {
1533 : // Scripted getter without JIT code. Just wait.
1534 : return false;
1535 : }
1536 0 : return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
1537 : case ProxyStubType::Generic:
1538 197 : return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ false);
1539 : }
1540 :
1541 0 : MOZ_CRASH("Unexpected ProxyStubType");
1542 : }
1543 :
1544 : bool
1545 0 : GetPropIRGenerator::tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id)
1546 : {
1547 1714 : if (!obj->is<UnboxedPlainObject>())
1548 : return false;
1549 :
1550 240 : const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
1551 120 : if (!property)
1552 : return false;
1553 :
1554 0 : if (!cx_->runtime()->jitSupportsFloatingPoint)
1555 : return false;
1556 :
1557 0 : maybeEmitIdGuard(id);
1558 119 : writer.guardGroupForLayout(objId, obj->group());
1559 0 : writer.loadUnboxedPropertyResult(objId, property->type,
1560 238 : UnboxedPlainObject::offsetOfData() + property->offset);
1561 0 : if (property->type == JSVAL_TYPE_OBJECT)
1562 49 : writer.typeMonitorResult();
1563 : else
1564 0 : writer.returnFromIC();
1565 :
1566 119 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
1567 :
1568 0 : trackAttached("Unboxed");
1569 119 : return true;
1570 : }
1571 :
1572 : bool
1573 0 : GetPropIRGenerator::tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id)
1574 : {
1575 1476 : if (!obj->is<UnboxedPlainObject>())
1576 : return false;
1577 :
1578 0 : UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
1579 1 : if (!expando)
1580 : return false;
1581 :
1582 0 : Shape* shape = expando->lookup(cx_, id);
1583 0 : if (!shape || !shape->isDataProperty())
1584 : return false;
1585 :
1586 0 : maybeEmitIdGuard(id);
1587 2 : EmitReadSlotResult(writer, obj, obj, shape, objId);
1588 2 : EmitReadSlotReturn(writer, obj, obj, shape);
1589 :
1590 0 : trackAttached("UnboxedExpando");
1591 1 : return true;
1592 : }
1593 :
1594 : static TypedThingLayout
1595 0 : GetTypedThingLayout(const Class* clasp)
1596 : {
1597 0 : if (IsTypedArrayClass(clasp))
1598 : return Layout_TypedArray;
1599 0 : if (IsOutlineTypedObjectClass(clasp))
1600 : return Layout_OutlineTypedObject;
1601 0 : if (IsInlineTypedObjectClass(clasp))
1602 : return Layout_InlineTypedObject;
1603 0 : MOZ_CRASH("Bad object class");
1604 : }
1605 :
1606 : bool
1607 0 : GetPropIRGenerator::tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id)
1608 : {
1609 1474 : if (!obj->is<TypedObject>())
1610 : return false;
1611 :
1612 0 : if (!cx_->runtime()->jitSupportsFloatingPoint || cx_->zone()->detachedTypedObjects)
1613 : return false;
1614 :
1615 0 : TypedObject* typedObj = &obj->as<TypedObject>();
1616 0 : if (!typedObj->typeDescr().is<StructTypeDescr>())
1617 : return false;
1618 :
1619 0 : StructTypeDescr* structDescr = &typedObj->typeDescr().as<StructTypeDescr>();
1620 : size_t fieldIndex;
1621 0 : if (!structDescr->fieldIndex(id, &fieldIndex))
1622 : return false;
1623 :
1624 0 : TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
1625 0 : if (!fieldDescr->is<SimpleTypeDescr>())
1626 : return false;
1627 :
1628 0 : TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
1629 :
1630 0 : uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
1631 0 : uint32_t typeDescr = SimpleTypeDescrKey(&fieldDescr->as<SimpleTypeDescr>());
1632 :
1633 0 : maybeEmitIdGuard(id);
1634 0 : writer.guardNoDetachedTypedObjects();
1635 0 : writer.guardGroupForLayout(objId, obj->group());
1636 0 : writer.loadTypedObjectResult(objId, fieldOffset, layout, typeDescr);
1637 :
1638 : // Only monitor the result if the type produced by this stub might vary.
1639 0 : bool monitorLoad = false;
1640 0 : if (SimpleTypeDescrKeyIsScalar(typeDescr)) {
1641 0 : Scalar::Type type = ScalarTypeFromSimpleTypeDescrKey(typeDescr);
1642 0 : monitorLoad = type == Scalar::Uint32;
1643 : } else {
1644 0 : ReferenceTypeDescr::Type type = ReferenceTypeFromSimpleTypeDescrKey(typeDescr);
1645 0 : monitorLoad = type != ReferenceTypeDescr::TYPE_STRING;
1646 : }
1647 :
1648 0 : if (monitorLoad)
1649 0 : writer.typeMonitorResult();
1650 : else
1651 0 : writer.returnFromIC();
1652 :
1653 0 : trackAttached("TypedObject");
1654 0 : return true;
1655 : }
1656 :
1657 : bool
1658 0 : GetPropIRGenerator::tryAttachObjectLength(HandleObject obj, ObjOperandId objId, HandleId id)
1659 : {
1660 36560 : if (!JSID_IS_ATOM(id, cx_->names().length))
1661 : return false;
1662 :
1663 252 : if (!(resultFlags_ & GetPropertyResultFlags::AllowInt32))
1664 : return false;
1665 :
1666 252 : if (obj->is<ArrayObject>()) {
1667 : // Make sure int32 is added to the TypeSet before we attach a stub, so
1668 : // the stub can return int32 values without monitoring the result.
1669 0 : if (obj->as<ArrayObject>().length() > INT32_MAX)
1670 : return false;
1671 :
1672 0 : maybeEmitIdGuard(id);
1673 0 : writer.guardClass(objId, GuardClassKind::Array);
1674 99 : writer.loadInt32ArrayLengthResult(objId);
1675 198 : writer.returnFromIC();
1676 :
1677 0 : trackAttached("ArrayLength");
1678 0 : return true;
1679 : }
1680 :
1681 0 : if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
1682 0 : maybeEmitIdGuard(id);
1683 8 : if (obj->is<MappedArgumentsObject>()) {
1684 0 : writer.guardClass(objId, GuardClassKind::MappedArguments);
1685 : } else {
1686 8 : MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
1687 0 : writer.guardClass(objId, GuardClassKind::UnmappedArguments);
1688 : }
1689 4 : writer.loadArgumentsObjectLengthResult(objId);
1690 8 : writer.returnFromIC();
1691 :
1692 4 : trackAttached("ArgumentsObjectLength");
1693 4 : return true;
1694 : }
1695 :
1696 : return false;
1697 : }
1698 :
1699 : bool
1700 0 : GetPropIRGenerator::tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id)
1701 : {
1702 : // Function properties are lazily resolved so they might not be defined yet.
1703 : // And we might end up in a situation where we always have a fresh function
1704 : // object during the IC generation.
1705 990 : if (!obj->is<JSFunction>())
1706 : return false;
1707 :
1708 1 : JSObject* holder = nullptr;
1709 0 : PropertyResult prop;
1710 : // This property exists already, don't attach the stub.
1711 0 : if (LookupPropertyPure(cx_, obj, id, &holder, &prop))
1712 : return false;
1713 :
1714 1 : JSFunction* fun = &obj->as<JSFunction>();
1715 :
1716 5 : if (JSID_IS_ATOM(id, cx_->names().length)) {
1717 : // length was probably deleted from the function.
1718 0 : if (fun->hasResolvedLength())
1719 : return false;
1720 :
1721 : // Lazy functions don't store the length.
1722 0 : if (fun->isInterpretedLazy())
1723 : return false;
1724 :
1725 0 : maybeEmitIdGuard(id);
1726 0 : writer.guardClass(objId, GuardClassKind::JSFunction);
1727 0 : writer.loadFunctionLengthResult(objId);
1728 0 : writer.returnFromIC();
1729 :
1730 0 : trackAttached("FunctionLength");
1731 0 : return true;
1732 : }
1733 :
1734 : return false;
1735 : }
1736 :
1737 : bool
1738 0 : GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id)
1739 : {
1740 0 : if (!obj->is<ModuleNamespaceObject>())
1741 : return false;
1742 :
1743 0 : Rooted<ModuleNamespaceObject*> ns(cx_, &obj->as<ModuleNamespaceObject>());
1744 0 : RootedModuleEnvironmentObject env(cx_);
1745 0 : RootedShape shape(cx_);
1746 0 : if (!ns->bindings().lookup(id, env.address(), shape.address()))
1747 : return false;
1748 :
1749 : // Don't emit a stub until the target binding has been initialized.
1750 0 : if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
1751 : return false;
1752 :
1753 0 : if (IsIonEnabled(cx_))
1754 0 : EnsureTrackPropertyTypes(cx_, env, shape->propid());
1755 :
1756 : // Check for the specific namespace object.
1757 0 : maybeEmitIdGuard(id);
1758 0 : writer.guardSpecificObject(objId, ns);
1759 :
1760 0 : ObjOperandId envId = writer.loadObject(env);
1761 0 : EmitLoadSlotResult(writer, envId, env, shape);
1762 0 : writer.typeMonitorResult();
1763 :
1764 0 : trackAttached("ModuleNamespace");
1765 0 : return true;
1766 : }
1767 :
1768 : bool
1769 0 : GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId, HandleId id)
1770 : {
1771 : JSValueType primitiveType;
1772 381 : RootedNativeObject proto(cx_);
1773 0 : if (val_.isString()) {
1774 0 : if (JSID_IS_ATOM(id, cx_->names().length)) {
1775 : // String length is special-cased, see js::GetProperty.
1776 : return false;
1777 : }
1778 0 : primitiveType = JSVAL_TYPE_STRING;
1779 0 : proto = MaybeNativeObject(cx_->global()->maybeGetPrototype(JSProto_String));
1780 0 : } else if (val_.isNumber()) {
1781 0 : primitiveType = JSVAL_TYPE_DOUBLE;
1782 0 : proto = MaybeNativeObject(cx_->global()->maybeGetPrototype(JSProto_Number));
1783 0 : } else if (val_.isBoolean()) {
1784 0 : primitiveType = JSVAL_TYPE_BOOLEAN;
1785 0 : proto = MaybeNativeObject(cx_->global()->maybeGetPrototype(JSProto_Boolean));
1786 54 : } else if (val_.isSymbol()) {
1787 0 : primitiveType = JSVAL_TYPE_SYMBOL;
1788 0 : proto = MaybeNativeObject(cx_->global()->maybeGetPrototype(JSProto_Symbol));
1789 : } else {
1790 81 : MOZ_ASSERT(val_.isNullOrUndefined() || val_.isMagic());
1791 : return false;
1792 : }
1793 0 : if (!proto)
1794 : return false;
1795 :
1796 0 : RootedShape shape(cx_);
1797 240 : RootedNativeObject holder(cx_);
1798 400 : NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, proto, id, &holder, &shape, pc_,
1799 : resultFlags_,
1800 80 : isTemporarilyUnoptimizable_);
1801 0 : if (type != CanAttachReadSlot)
1802 : return false;
1803 :
1804 80 : if (holder) {
1805 : // Instantiate this property, for use during Ion compilation.
1806 0 : if (IsIonEnabled(cx_))
1807 160 : EnsureTrackPropertyTypes(cx_, holder, id);
1808 : }
1809 :
1810 0 : writer.guardType(valId, primitiveType);
1811 80 : maybeEmitIdGuard(id);
1812 :
1813 0 : ObjOperandId protoId = writer.loadObject(proto);
1814 240 : EmitReadSlotResult(writer, proto, holder, shape, protoId);
1815 240 : EmitReadSlotReturn(writer, proto, holder, shape);
1816 :
1817 0 : trackAttached("Primitive");
1818 80 : return true;
1819 : }
1820 :
1821 : bool
1822 0 : GetPropIRGenerator::tryAttachStringLength(ValOperandId valId, HandleId id)
1823 : {
1824 0 : if (!val_.isString() || !JSID_IS_ATOM(id, cx_->names().length))
1825 : return false;
1826 :
1827 0 : StringOperandId strId = writer.guardIsString(valId);
1828 0 : maybeEmitIdGuard(id);
1829 18 : writer.loadStringLengthResult(strId);
1830 36 : writer.returnFromIC();
1831 :
1832 0 : trackAttached("StringLength");
1833 18 : return true;
1834 : }
1835 :
1836 : bool
1837 43 : GetPropIRGenerator::tryAttachStringChar(ValOperandId valId, ValOperandId indexId)
1838 : {
1839 0 : MOZ_ASSERT(idVal_.isInt32());
1840 :
1841 86 : if (!val_.isString())
1842 : return false;
1843 :
1844 0 : int32_t index = idVal_.toInt32();
1845 5 : if (index < 0)
1846 : return false;
1847 :
1848 0 : JSString* str = val_.toString();
1849 0 : if (size_t(index) >= str->length())
1850 : return false;
1851 :
1852 : // This follows JSString::getChar, otherwise we fail to attach getChar in a lot of cases.
1853 5 : if (str->isRope()) {
1854 0 : JSRope* rope = &str->asRope();
1855 :
1856 : // Make sure the left side contains the index.
1857 0 : if (size_t(index) >= rope->leftChild()->length())
1858 : return false;
1859 :
1860 0 : str = rope->leftChild();
1861 : }
1862 :
1863 15 : if (!str->isLinear() ||
1864 0 : str->asLinear().latin1OrTwoByteChar(index) >= StaticStrings::UNIT_STATIC_LIMIT)
1865 : {
1866 : return false;
1867 : }
1868 :
1869 0 : StringOperandId strId = writer.guardIsString(valId);
1870 0 : Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
1871 5 : writer.loadStringCharResult(strId, int32IndexId);
1872 10 : writer.returnFromIC();
1873 :
1874 0 : trackAttached("StringChar");
1875 5 : return true;
1876 : }
1877 :
1878 : bool
1879 0 : GetPropIRGenerator::tryAttachMagicArgumentsName(ValOperandId valId, HandleId id)
1880 : {
1881 58 : if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
1882 : return false;
1883 :
1884 0 : if (!JSID_IS_ATOM(id, cx_->names().length) && !JSID_IS_ATOM(id, cx_->names().callee))
1885 : return false;
1886 :
1887 0 : maybeEmitIdGuard(id);
1888 0 : writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
1889 54 : writer.guardFrameHasNoArgumentsObject();
1890 :
1891 0 : if (JSID_IS_ATOM(id, cx_->names().length)) {
1892 0 : writer.loadFrameNumActualArgsResult();
1893 27 : writer.returnFromIC();
1894 : } else {
1895 0 : MOZ_ASSERT(JSID_IS_ATOM(id, cx_->names().callee));
1896 0 : writer.loadFrameCalleeResult();
1897 0 : writer.typeMonitorResult();
1898 : }
1899 :
1900 0 : trackAttached("MagicArgumentsName");
1901 27 : return true;
1902 : }
1903 :
1904 : bool
1905 38 : GetPropIRGenerator::tryAttachMagicArgument(ValOperandId valId, ValOperandId indexId)
1906 : {
1907 0 : MOZ_ASSERT(idVal_.isInt32());
1908 :
1909 76 : if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
1910 : return false;
1911 :
1912 0 : writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
1913 76 : writer.guardFrameHasNoArgumentsObject();
1914 :
1915 0 : Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
1916 38 : writer.loadFrameArgumentResult(int32IndexId);
1917 76 : writer.typeMonitorResult();
1918 :
1919 0 : trackAttached("MagicArgument");
1920 38 : return true;
1921 : }
1922 :
1923 : bool
1924 3 : GetPropIRGenerator::tryAttachArgumentsObjectArg(HandleObject obj, ObjOperandId objId,
1925 : Int32OperandId indexId)
1926 : {
1927 9 : if (!obj->is<ArgumentsObject>() || obj->as<ArgumentsObject>().hasOverriddenElement())
1928 : return false;
1929 :
1930 6 : if (!(resultFlags_ & GetPropertyResultFlags::Monitored))
1931 : return false;
1932 :
1933 6 : if (obj->is<MappedArgumentsObject>()) {
1934 0 : writer.guardClass(objId, GuardClassKind::MappedArguments);
1935 : } else {
1936 0 : MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
1937 3 : writer.guardClass(objId, GuardClassKind::UnmappedArguments);
1938 : }
1939 :
1940 3 : writer.loadArgumentsObjectArgResult(objId, indexId);
1941 6 : writer.typeMonitorResult();
1942 :
1943 0 : trackAttached("ArgumentsObjectArg");
1944 3 : return true;
1945 : }
1946 :
1947 : bool
1948 138 : GetPropIRGenerator::tryAttachDenseElement(HandleObject obj, ObjOperandId objId,
1949 : uint32_t index, Int32OperandId indexId)
1950 : {
1951 276 : if (!obj->isNative())
1952 : return false;
1953 :
1954 0 : NativeObject* nobj = &obj->as<NativeObject>();
1955 0 : if (!nobj->containsDenseElement(index))
1956 : return false;
1957 :
1958 0 : TestMatchingNativeReceiver(writer, nobj, objId);
1959 125 : writer.loadDenseElementResult(objId, indexId);
1960 250 : writer.typeMonitorResult();
1961 :
1962 0 : trackAttached("DenseElement");
1963 125 : return true;
1964 : }
1965 :
1966 : static bool
1967 13 : CanAttachDenseElementHole(NativeObject* obj, bool ownProp, bool allowIndexedReceiver = false)
1968 : {
1969 : // Make sure the objects on the prototype don't have any indexed properties
1970 : // or that such properties can't appear without a shape change.
1971 : // Otherwise returning undefined for holes would obviously be incorrect,
1972 : // because we would have to lookup a property on the prototype instead.
1973 : do {
1974 : // The first two checks are also relevant to the receiver object.
1975 64 : if (!allowIndexedReceiver && obj->isIndexed())
1976 : return false;
1977 29 : allowIndexedReceiver = false;
1978 :
1979 58 : if (ClassCanHaveExtraProperties(obj->getClass()))
1980 : return false;
1981 :
1982 : // Don't need to check prototype for OwnProperty checks
1983 29 : if (ownProp)
1984 : return true;
1985 :
1986 29 : JSObject* proto = obj->staticPrototype();
1987 29 : if (!proto)
1988 : break;
1989 :
1990 19 : if (!proto->isNative())
1991 : return false;
1992 :
1993 : // Make sure objects on the prototype don't have dense elements.
1994 38 : if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
1995 : return false;
1996 :
1997 19 : obj = &proto->as<NativeObject>();
1998 : } while (true);
1999 :
2000 : return true;
2001 : }
2002 :
2003 : bool
2004 13 : GetPropIRGenerator::tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId,
2005 : uint32_t index, Int32OperandId indexId)
2006 : {
2007 26 : if (!obj->isNative())
2008 : return false;
2009 :
2010 13 : NativeObject* nobj = &obj->as<NativeObject>();
2011 13 : if (nobj->containsDenseElement(index))
2012 : return false;
2013 0 : if (!CanAttachDenseElementHole(nobj, false))
2014 : return false;
2015 :
2016 : // Guard on the shape, to prevent non-dense elements from appearing.
2017 0 : TestMatchingNativeReceiver(writer, nobj, objId);
2018 0 : GeneratePrototypeHoleGuards(writer, nobj, objId);
2019 10 : writer.loadDenseElementHoleResult(objId, indexId);
2020 20 : writer.typeMonitorResult();
2021 :
2022 0 : trackAttached("DenseElementHole");
2023 10 : return true;
2024 : }
2025 :
2026 : static bool
2027 0 : IsPrimitiveArrayTypedObject(JSObject* obj)
2028 : {
2029 144 : if (!obj->is<TypedObject>())
2030 : return false;
2031 0 : TypeDescr& descr = obj->as<TypedObject>().typeDescr();
2032 0 : return descr.is<ArrayTypeDescr>() &&
2033 0 : descr.as<ArrayTypeDescr>().elementType().is<ScalarTypeDescr>();
2034 : }
2035 :
2036 : static Scalar::Type
2037 0 : PrimitiveArrayTypedObjectType(JSObject* obj)
2038 : {
2039 0 : MOZ_ASSERT(IsPrimitiveArrayTypedObject(obj));
2040 0 : TypeDescr& descr = obj->as<TypedObject>().typeDescr();
2041 0 : return descr.as<ArrayTypeDescr>().elementType().as<ScalarTypeDescr>().type();
2042 : }
2043 :
2044 :
2045 : static Scalar::Type
2046 0 : TypedThingElementType(JSObject* obj)
2047 : {
2048 0 : return obj->is<TypedArrayObject>()
2049 0 : ? obj->as<TypedArrayObject>().type()
2050 0 : : PrimitiveArrayTypedObjectType(obj);
2051 : }
2052 :
2053 : static bool
2054 : TypedThingRequiresFloatingPoint(JSObject* obj)
2055 : {
2056 0 : Scalar::Type type = TypedThingElementType(obj);
2057 : return type == Scalar::Uint32 ||
2058 0 : type == Scalar::Float32 ||
2059 : type == Scalar::Float64;
2060 : }
2061 :
2062 : bool
2063 138 : GetPropIRGenerator::tryAttachTypedElement(HandleObject obj, ObjOperandId objId,
2064 : uint32_t index, Int32OperandId indexId)
2065 : {
2066 414 : if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
2067 : return false;
2068 :
2069 0 : if (!cx_->runtime()->jitSupportsFloatingPoint && TypedThingRequiresFloatingPoint(obj))
2070 : return false;
2071 :
2072 : // Ensure the index is in-bounds so the element type gets monitored.
2073 0 : if (obj->is<TypedArrayObject>() && index >= obj->as<TypedArrayObject>().length())
2074 : return false;
2075 :
2076 : // Don't attach typed object stubs if the underlying storage could be
2077 : // detached, as the stub will always bail out.
2078 0 : if (IsPrimitiveArrayTypedObject(obj) && cx_->zone()->detachedTypedObjects)
2079 : return false;
2080 :
2081 0 : TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
2082 :
2083 0 : if (IsPrimitiveArrayTypedObject(obj)) {
2084 0 : writer.guardNoDetachedTypedObjects();
2085 0 : writer.guardGroupForLayout(objId, obj->group());
2086 : } else {
2087 0 : writer.guardShapeForClass(objId, obj->as<TypedArrayObject>().shape());
2088 : }
2089 :
2090 0 : writer.loadTypedElementResult(objId, indexId, layout, TypedThingElementType(obj));
2091 :
2092 : // Reading from Uint32Array may produce an int32 now but a double value
2093 : // later, so ensure we monitor the result.
2094 0 : if (TypedThingElementType(obj) == Scalar::Type::Uint32)
2095 0 : writer.typeMonitorResult();
2096 : else
2097 0 : writer.returnFromIC();
2098 :
2099 0 : trackAttached("TypedElement");
2100 0 : return true;
2101 : }
2102 :
2103 : bool
2104 3 : GetPropIRGenerator::tryAttachUnboxedElementHole(HandleObject obj, ObjOperandId objId,
2105 : uint32_t index, Int32OperandId indexId)
2106 : {
2107 0 : if (!obj->is<UnboxedPlainObject>())
2108 : return false;
2109 :
2110 : // Only support unboxed objects with no elements (i.e. no expando)
2111 0 : UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
2112 0 : if (expando)
2113 : return false;
2114 :
2115 0 : if (JSObject* proto = obj->staticPrototype()) {
2116 : // Start the check at the first object on the [[Prototype]],
2117 : // which must be native now.
2118 0 : if (!proto->isNative())
2119 : return false;
2120 :
2121 0 : if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
2122 : return false;
2123 :
2124 0 : if (!CanAttachDenseElementHole(&proto->as<NativeObject>(), false))
2125 : return false;
2126 : }
2127 :
2128 : // Guard on the group and prevent expandos from appearing.
2129 0 : Maybe<ObjOperandId> tempId;
2130 0 : TestMatchingReceiver(writer, obj, objId, &tempId);
2131 :
2132 : // Guard that the prototype chain has no elements.
2133 0 : GeneratePrototypeHoleGuards(writer, obj, objId);
2134 :
2135 0 : writer.loadUndefinedResult();
2136 : // No monitor: We know undefined must be in the typeset already.
2137 0 : writer.returnFromIC();
2138 :
2139 0 : trackAttached("UnboxedElementHole");
2140 0 : return true;
2141 : }
2142 :
2143 : bool
2144 145 : GetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId)
2145 : {
2146 290 : if (!obj->is<ProxyObject>())
2147 : return false;
2148 :
2149 : // The proxy stubs don't currently support |super| access.
2150 14 : if (isSuper())
2151 : return false;
2152 :
2153 7 : writer.guardIsProxy(objId);
2154 :
2155 : // We are not guarding against DOM proxies here, because there is no other
2156 : // specialized DOM IC we could attach.
2157 : // We could call maybeEmitIdGuard here and then emit CallProxyGetResult,
2158 : // but for GetElem we prefer to attach a stub that can handle any Value
2159 : // so we don't attach a new stub for every id.
2160 0 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
2161 0 : MOZ_ASSERT(!isSuper());
2162 7 : writer.callProxyGetByValueResult(objId, getElemKeyValueId());
2163 14 : writer.typeMonitorResult();
2164 :
2165 0 : trackAttached("ProxyElement");
2166 7 : return true;
2167 : }
2168 :
2169 : void
2170 1 : GetPropIRGenerator::trackAttached(const char* name)
2171 : {
2172 : #ifdef JS_CACHEIR_SPEW
2173 0 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
2174 0 : sp.valueProperty("base", val_);
2175 0 : sp.valueProperty("property", idVal_);
2176 : }
2177 : #endif
2178 0 : }
2179 :
2180 : void
2181 1790 : IRGenerator::emitIdGuard(ValOperandId valId, jsid id)
2182 : {
2183 0 : if (JSID_IS_SYMBOL(id)) {
2184 0 : SymbolOperandId symId = writer.guardIsSymbol(valId);
2185 287 : writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
2186 : } else {
2187 1503 : MOZ_ASSERT(JSID_IS_ATOM(id));
2188 1503 : StringOperandId strId = writer.guardIsString(valId);
2189 0 : writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
2190 : }
2191 0 : }
2192 :
2193 : void
2194 6885 : GetPropIRGenerator::maybeEmitIdGuard(jsid id)
2195 : {
2196 6885 : if (cacheKind_ == CacheKind::GetProp || cacheKind_ == CacheKind::GetPropSuper) {
2197 : // Constant PropertyName, no guards necessary.
2198 0 : MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
2199 : return;
2200 : }
2201 :
2202 0 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
2203 991 : emitIdGuard(getElemKeyValueId(), id);
2204 : }
2205 :
2206 : void
2207 1448 : SetPropIRGenerator::maybeEmitIdGuard(jsid id)
2208 : {
2209 1448 : if (cacheKind_ == CacheKind::SetProp) {
2210 : // Constant PropertyName, no guards necessary.
2211 0 : MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
2212 : return;
2213 : }
2214 :
2215 327 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
2216 0 : emitIdGuard(setElemKeyValueId(), id);
2217 : }
2218 :
2219 0 : GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
2220 : ICState::Mode mode, HandleObject env,
2221 3290 : HandlePropertyName name)
2222 : : IRGenerator(cx, script, pc, CacheKind::GetName, mode),
2223 : env_(env),
2224 6580 : name_(name)
2225 0 : {}
2226 :
2227 : bool
2228 3290 : GetNameIRGenerator::tryAttachStub()
2229 : {
2230 0 : MOZ_ASSERT(cacheKind_ == CacheKind::GetName);
2231 :
2232 0 : AutoAssertNoPendingException aanpe(cx_);
2233 :
2234 0 : ObjOperandId envId(writer.setInputOperandId(0));
2235 13160 : RootedId id(cx_, NameToId(name_));
2236 :
2237 3290 : if (tryAttachGlobalNameValue(envId, id))
2238 : return true;
2239 0 : if (tryAttachGlobalNameGetter(envId, id))
2240 : return true;
2241 3237 : if (tryAttachEnvironmentName(envId, id))
2242 : return true;
2243 :
2244 0 : trackAttached(IRGenerator::NotAttached);
2245 3178 : return false;
2246 : }
2247 :
2248 : bool
2249 65 : CanAttachGlobalName(JSContext* cx, Handle<LexicalEnvironmentObject*> globalLexical, HandleId id,
2250 : MutableHandleNativeObject holder, MutableHandleShape shape)
2251 : {
2252 : // The property must be found, and it must be found as a normal data property.
2253 130 : RootedNativeObject current(cx, globalLexical);
2254 : while (true) {
2255 0 : shape.set(current->lookup(cx, id));
2256 133 : if (shape)
2257 : break;
2258 :
2259 68 : if (current == globalLexical) {
2260 128 : current = &globalLexical->global();
2261 : } else {
2262 : // In the browser the global prototype chain should be immutable.
2263 4 : if (!current->staticPrototypeIsImmutable())
2264 : return false;
2265 :
2266 4 : JSObject* proto = current->staticPrototype();
2267 8 : if (!proto || !proto->is<NativeObject>())
2268 : return false;
2269 :
2270 0 : current = &proto->as<NativeObject>();
2271 : }
2272 : }
2273 :
2274 0 : holder.set(current);
2275 65 : return true;
2276 : }
2277 :
2278 : bool
2279 0 : GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
2280 : {
2281 9876 : if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
2282 : return false;
2283 :
2284 0 : Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
2285 53 : MOZ_ASSERT(globalLexical->isGlobal());
2286 :
2287 159 : RootedNativeObject holder(cx_);
2288 0 : RootedShape shape(cx_);
2289 106 : if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
2290 : return false;
2291 :
2292 : // The property must be found, and it must be found as a normal data property.
2293 53 : if (!shape->isDataProperty())
2294 : return false;
2295 :
2296 : // This might still be an uninitialized lexical.
2297 0 : if (holder->getSlot(shape->slot()).isMagic())
2298 : return false;
2299 :
2300 : // Instantiate this global property, for use during Ion compilation.
2301 41 : if (IsIonEnabled(cx_))
2302 0 : EnsureTrackPropertyTypes(cx_, holder, id);
2303 :
2304 41 : if (holder == globalLexical) {
2305 : // There is no need to guard on the shape. Lexical bindings are
2306 : // non-configurable, and this stub cannot be shared across globals.
2307 2 : size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
2308 1 : writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
2309 : } else {
2310 : // Check the prototype chain from the global to the holder
2311 : // prototype. Ignore the global lexical scope as it doesn't figure
2312 : // into the prototype chain. We guard on the global lexical
2313 : // scope's shape independently.
2314 120 : if (!IsCacheableGetPropReadSlot(&globalLexical->global(), holder, PropertyResult(shape)))
2315 0 : return false;
2316 :
2317 : // Shape guard for global lexical.
2318 40 : writer.guardShape(objId, globalLexical->lastProperty());
2319 :
2320 : // Guard on the shape of the GlobalObject.
2321 40 : ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
2322 0 : writer.guardShape(globalId, globalLexical->global().lastProperty());
2323 :
2324 40 : ObjOperandId holderId = globalId;
2325 80 : if (holder != &globalLexical->global()) {
2326 : // Shape guard holder.
2327 0 : holderId = writer.loadObject(holder);
2328 0 : writer.guardShape(holderId, holder->lastProperty());
2329 : }
2330 :
2331 0 : EmitLoadSlotResult(writer, holderId, holder, shape);
2332 : }
2333 :
2334 82 : writer.typeMonitorResult();
2335 :
2336 0 : trackAttached("GlobalNameValue");
2337 41 : return true;
2338 : }
2339 :
2340 : bool
2341 0 : GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id)
2342 : {
2343 9712 : if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
2344 : return false;
2345 :
2346 0 : Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
2347 12 : MOZ_ASSERT(globalLexical->isGlobal());
2348 :
2349 0 : RootedNativeObject holder(cx_);
2350 36 : RootedShape shape(cx_);
2351 24 : if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
2352 : return false;
2353 :
2354 12 : if (holder == globalLexical)
2355 : return false;
2356 :
2357 36 : if (!IsCacheableGetPropCallNative(&globalLexical->global(), holder, shape))
2358 : return false;
2359 :
2360 12 : if (IsIonEnabled(cx_))
2361 24 : EnsureTrackPropertyTypes(cx_, holder, id);
2362 :
2363 : // Shape guard for global lexical.
2364 12 : writer.guardShape(objId, globalLexical->lastProperty());
2365 :
2366 : // Guard on the shape of the GlobalObject.
2367 0 : ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
2368 0 : writer.guardShape(globalId, globalLexical->global().lastProperty());
2369 :
2370 24 : if (holder != &globalLexical->global()) {
2371 : // Shape guard holder.
2372 1 : ObjOperandId holderId = writer.loadObject(holder);
2373 0 : writer.guardShape(holderId, holder->lastProperty());
2374 : }
2375 :
2376 36 : EmitCallGetterResultNoGuards(writer, &globalLexical->global(), holder, shape, globalId);
2377 :
2378 0 : trackAttached("GlobalNameGetter");
2379 12 : return true;
2380 : }
2381 :
2382 : static bool
2383 146 : NeedEnvironmentShapeGuard(JSObject* envObj)
2384 : {
2385 146 : if (!envObj->is<CallObject>())
2386 : return true;
2387 :
2388 : // We can skip a guard on the call object if the script's bindings are
2389 : // guaranteed to be immutable (and thus cannot introduce shadowing
2390 : // variables). The function might have been relazified under rare
2391 : // conditions. In that case, we pessimistically create the guard.
2392 0 : CallObject* callObj = &envObj->as<CallObject>();
2393 34 : JSFunction* fun = &callObj->callee();
2394 68 : if (!fun->hasScript() || fun->nonLazyScript()->funHasExtensibleScope())
2395 : return true;
2396 :
2397 11 : return false;
2398 : }
2399 :
2400 : bool
2401 0 : GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
2402 : {
2403 0 : if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
2404 : return false;
2405 :
2406 0 : RootedObject env(cx_, env_);
2407 0 : RootedShape shape(cx_);
2408 0 : RootedNativeObject holder(cx_);
2409 :
2410 233 : while (env) {
2411 292 : if (env->is<GlobalObject>()) {
2412 69 : shape = env->as<GlobalObject>().lookup(cx_, id);
2413 0 : if (shape)
2414 : break;
2415 : return false;
2416 : }
2417 :
2418 369 : if (!env->is<EnvironmentObject>() || env->is<WithEnvironmentObject>())
2419 : return false;
2420 :
2421 0 : MOZ_ASSERT(!env->hasUncacheableProto());
2422 :
2423 : // Check for an 'own' property on the env. There is no need to
2424 : // check the prototype as non-with scopes do not inherit properties
2425 : // from any prototype.
2426 369 : shape = env->as<NativeObject>().lookup(cx_, id);
2427 123 : if (shape)
2428 : break;
2429 :
2430 174 : env = env->enclosingEnvironment();
2431 : }
2432 :
2433 118 : holder = &env->as<NativeObject>();
2434 0 : if (!IsCacheableGetPropReadSlot(holder, holder, PropertyResult(shape)))
2435 : return false;
2436 0 : if (holder->getSlot(shape->slot()).isMagic())
2437 : return false;
2438 :
2439 59 : ObjOperandId lastObjId = objId;
2440 0 : env = env_;
2441 233 : while (env) {
2442 146 : if (NeedEnvironmentShapeGuard(env))
2443 0 : writer.guardShape(lastObjId, env->maybeShape());
2444 :
2445 146 : if (env == holder)
2446 : break;
2447 :
2448 0 : lastObjId = writer.loadEnclosingEnvironment(lastObjId);
2449 174 : env = env->enclosingEnvironment();
2450 : }
2451 :
2452 177 : if (holder->isFixedSlot(shape->slot())) {
2453 0 : writer.loadEnvironmentFixedSlotResult(lastObjId, NativeObject::getFixedSlotOffset(shape->slot()));
2454 : } else {
2455 0 : size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
2456 0 : writer.loadEnvironmentDynamicSlotResult(lastObjId, dynamicSlotOffset);
2457 : }
2458 118 : writer.typeMonitorResult();
2459 :
2460 0 : trackAttached("EnvironmentName");
2461 59 : return true;
2462 : }
2463 :
2464 : void
2465 1 : GetNameIRGenerator::trackAttached(const char* name)
2466 : {
2467 : #ifdef JS_CACHEIR_SPEW
2468 0 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
2469 0 : sp.valueProperty("base", ObjectValue(*env_));
2470 0 : sp.valueProperty("property", StringValue(name_));
2471 : }
2472 : #endif
2473 3290 : }
2474 :
2475 0 : BindNameIRGenerator::BindNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
2476 : ICState::Mode mode, HandleObject env,
2477 45 : HandlePropertyName name)
2478 : : IRGenerator(cx, script, pc, CacheKind::BindName, mode),
2479 : env_(env),
2480 90 : name_(name)
2481 0 : {}
2482 :
2483 : bool
2484 45 : BindNameIRGenerator::tryAttachStub()
2485 : {
2486 0 : MOZ_ASSERT(cacheKind_ == CacheKind::BindName);
2487 :
2488 0 : AutoAssertNoPendingException aanpe(cx_);
2489 :
2490 0 : ObjOperandId envId(writer.setInputOperandId(0));
2491 180 : RootedId id(cx_, NameToId(name_));
2492 :
2493 0 : if (tryAttachGlobalName(envId, id))
2494 : return true;
2495 45 : if (tryAttachEnvironmentName(envId, id))
2496 : return true;
2497 :
2498 0 : trackAttached(IRGenerator::NotAttached);
2499 45 : return false;
2500 : }
2501 :
2502 : bool
2503 0 : BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId, HandleId id)
2504 : {
2505 180 : if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
2506 : return false;
2507 :
2508 0 : Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
2509 0 : MOZ_ASSERT(globalLexical->isGlobal());
2510 :
2511 0 : JSObject* result = nullptr;
2512 0 : if (Shape* shape = globalLexical->lookup(cx_, id)) {
2513 : // If this is an uninitialized lexical or a const, we need to return a
2514 : // RuntimeLexicalErrorObject.
2515 0 : if (globalLexical->getSlot(shape->slot()).isMagic() || !shape->writable())
2516 : return false;
2517 0 : result = globalLexical;
2518 : } else {
2519 0 : result = &globalLexical->global();
2520 : }
2521 :
2522 0 : if (result == globalLexical) {
2523 : // Lexical bindings are non-configurable so we can just return the
2524 : // global lexical.
2525 0 : writer.loadObjectResult(objId);
2526 : } else {
2527 : // If the property exists on the global and is non-configurable, it cannot be
2528 : // shadowed by the lexical scope so we can just return the global without a
2529 : // shape guard.
2530 0 : Shape* shape = result->as<GlobalObject>().lookup(cx_, id);
2531 0 : if (!shape || shape->configurable())
2532 0 : writer.guardShape(objId, globalLexical->lastProperty());
2533 0 : ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
2534 0 : writer.loadObjectResult(globalId);
2535 : }
2536 0 : writer.returnFromIC();
2537 :
2538 0 : trackAttached("GlobalName");
2539 0 : return true;
2540 : }
2541 :
2542 : bool
2543 0 : BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
2544 : {
2545 90 : if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
2546 : return false;
2547 :
2548 0 : RootedObject env(cx_, env_);
2549 0 : RootedShape shape(cx_);
2550 : while (true) {
2551 0 : if (!env->is<GlobalObject>() && !env->is<EnvironmentObject>())
2552 : return false;
2553 0 : if (env->is<WithEnvironmentObject>())
2554 : return false;
2555 :
2556 0 : MOZ_ASSERT(!env->hasUncacheableProto());
2557 :
2558 : // When we reach an unqualified variables object (like the global) we
2559 : // have to stop looking and return that object.
2560 0 : if (env->isUnqualifiedVarObj())
2561 : break;
2562 :
2563 : // Check for an 'own' property on the env. There is no need to
2564 : // check the prototype as non-with scopes do not inherit properties
2565 : // from any prototype.
2566 0 : shape = env->as<NativeObject>().lookup(cx_, id);
2567 0 : if (shape)
2568 : break;
2569 :
2570 0 : env = env->enclosingEnvironment();
2571 : }
2572 :
2573 : // If this is an uninitialized lexical or a const, we need to return a
2574 : // RuntimeLexicalErrorObject.
2575 0 : RootedNativeObject holder(cx_, &env->as<NativeObject>());
2576 0 : if (shape &&
2577 0 : holder->is<EnvironmentObject>() &&
2578 0 : (holder->getSlot(shape->slot()).isMagic() || !shape->writable()))
2579 : {
2580 : return false;
2581 : }
2582 :
2583 0 : ObjOperandId lastObjId = objId;
2584 0 : env = env_;
2585 0 : while (env) {
2586 0 : if (NeedEnvironmentShapeGuard(env) && !env->is<GlobalObject>())
2587 0 : writer.guardShape(lastObjId, env->maybeShape());
2588 :
2589 0 : if (env == holder)
2590 : break;
2591 :
2592 0 : lastObjId = writer.loadEnclosingEnvironment(lastObjId);
2593 0 : env = env->enclosingEnvironment();
2594 : }
2595 0 : writer.loadObjectResult(lastObjId);
2596 0 : writer.returnFromIC();
2597 :
2598 0 : trackAttached("EnvironmentName");
2599 0 : return true;
2600 : }
2601 :
2602 : void
2603 1 : BindNameIRGenerator::trackAttached(const char* name)
2604 : {
2605 : #ifdef JS_CACHEIR_SPEW
2606 0 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
2607 0 : sp.valueProperty("base", ObjectValue(*env_));
2608 0 : sp.valueProperty("property", StringValue(name_));
2609 : }
2610 : #endif
2611 45 : }
2612 :
2613 0 : HasPropIRGenerator::HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
2614 : CacheKind cacheKind, ICState::Mode mode,
2615 600 : HandleValue idVal, HandleValue val)
2616 : : IRGenerator(cx, script, pc, cacheKind, mode),
2617 : val_(val),
2618 1200 : idVal_(idVal)
2619 600 : { }
2620 :
2621 : bool
2622 17 : HasPropIRGenerator::tryAttachDense(HandleObject obj, ObjOperandId objId,
2623 : uint32_t index, Int32OperandId indexId)
2624 : {
2625 34 : if (!obj->isNative())
2626 : return false;
2627 :
2628 0 : NativeObject* nobj = &obj->as<NativeObject>();
2629 0 : if (!nobj->containsDenseElement(index))
2630 : return false;
2631 :
2632 : // Guard shape to ensure object class is NativeObject.
2633 0 : TestMatchingNativeReceiver(writer, nobj, objId);
2634 17 : writer.loadDenseElementExistsResult(objId, indexId);
2635 34 : writer.returnFromIC();
2636 :
2637 0 : trackAttached("DenseHasProp");
2638 17 : return true;
2639 : }
2640 :
2641 : bool
2642 0 : HasPropIRGenerator::tryAttachDenseHole(HandleObject obj, ObjOperandId objId,
2643 : uint32_t index, Int32OperandId indexId)
2644 : {
2645 0 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2646 :
2647 0 : if (!obj->isNative())
2648 : return false;
2649 :
2650 0 : NativeObject* nobj = &obj->as<NativeObject>();
2651 0 : if (nobj->containsDenseElement(index))
2652 : return false;
2653 0 : if (!CanAttachDenseElementHole(nobj, hasOwn))
2654 : return false;
2655 :
2656 : // Guard shape to ensure class is NativeObject and to prevent non-dense
2657 : // elements being added. Also ensures prototype doesn't change if dynamic
2658 : // checks aren't emitted.
2659 0 : TestMatchingNativeReceiver(writer, nobj, objId);
2660 :
2661 : // Generate prototype guards if needed. This includes monitoring that
2662 : // properties were not added in the chain.
2663 0 : if (!hasOwn)
2664 0 : GeneratePrototypeHoleGuards(writer, nobj, objId);
2665 :
2666 0 : writer.loadDenseElementHoleExistsResult(objId, indexId);
2667 0 : writer.returnFromIC();
2668 :
2669 0 : trackAttached("DenseHasPropHole");
2670 0 : return true;
2671 : }
2672 :
2673 : bool
2674 0 : HasPropIRGenerator::tryAttachSparse(HandleObject obj, ObjOperandId objId,
2675 : Int32OperandId indexId)
2676 : {
2677 0 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2678 :
2679 0 : if (!obj->isNative())
2680 : return false;
2681 0 : if (!obj->as<NativeObject>().isIndexed())
2682 : return false;
2683 0 : if (!CanAttachDenseElementHole(&obj->as<NativeObject>(), hasOwn,
2684 : /* allowIndexedReceiver = */ true))
2685 : {
2686 : return false;
2687 : }
2688 :
2689 : // Guard that this is a native object.
2690 0 : writer.guardIsNativeObject(objId);
2691 :
2692 : // Generate prototype guards if needed. This includes monitoring that
2693 : // properties were not added in the chain.
2694 0 : if (!hasOwn) {
2695 : // If GeneratePrototypeHoleGuards below won't add guards for prototype,
2696 : // we should add our own since we aren't guarding shape.
2697 0 : if (!obj->hasUncacheableProto())
2698 0 : GuardGroupProto(writer, obj, objId);
2699 :
2700 0 : GeneratePrototypeHoleGuards(writer, obj, objId);
2701 : }
2702 :
2703 : // Because of the prototype guard we know that the prototype chain
2704 : // does not include any dense or sparse (i.e indexed) properties.
2705 0 : writer.callObjectHasSparseElementResult(objId, indexId);
2706 0 : writer.returnFromIC();
2707 :
2708 0 : trackAttached("Sparse");
2709 0 : return true;
2710 : }
2711 :
2712 :
2713 : bool
2714 0 : HasPropIRGenerator::tryAttachNamedProp(HandleObject obj, ObjOperandId objId,
2715 : HandleId key, ValOperandId keyId)
2716 : {
2717 0 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2718 :
2719 533 : JSObject* holder = nullptr;
2720 533 : PropertyResult prop;
2721 :
2722 533 : if (hasOwn) {
2723 0 : if (!LookupOwnPropertyPure(cx_, obj, key, &prop))
2724 : return false;
2725 :
2726 0 : holder = obj;
2727 : } else {
2728 1038 : if (!LookupPropertyPure(cx_, obj, key, &holder, &prop))
2729 : return false;
2730 : }
2731 0 : if (!prop)
2732 : return false;
2733 :
2734 218 : if (tryAttachMegamorphic(objId, keyId))
2735 : return true;
2736 573 : if (tryAttachNative(obj, objId, key, keyId, prop, holder))
2737 : return true;
2738 26 : if (tryAttachUnboxed(obj, objId, key, keyId))
2739 : return true;
2740 0 : if (tryAttachTypedObject(obj, objId, key, keyId))
2741 : return true;
2742 0 : if (tryAttachUnboxedExpando(obj, objId, key, keyId))
2743 : return true;
2744 :
2745 0 : return false;
2746 : }
2747 :
2748 : bool
2749 528 : HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId, ValOperandId keyId)
2750 : {
2751 0 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2752 :
2753 0 : if (mode_ != ICState::Mode::Megamorphic)
2754 : return false;
2755 :
2756 56 : writer.megamorphicHasPropResult(objId, keyId, hasOwn);
2757 112 : writer.returnFromIC();
2758 0 : trackAttached("MegamorphicHasProp");
2759 56 : return true;
2760 : }
2761 :
2762 : bool
2763 191 : HasPropIRGenerator::tryAttachNative(JSObject* obj, ObjOperandId objId, jsid key,
2764 : ValOperandId keyId, PropertyResult prop, JSObject* holder)
2765 : {
2766 191 : if (!prop.isNativeProperty())
2767 : return false;
2768 :
2769 0 : if (!IsCacheableProtoChain(obj, holder))
2770 : return false;
2771 :
2772 178 : Maybe<ObjOperandId> tempId;
2773 0 : emitIdGuard(keyId, key);
2774 0 : EmitReadSlotGuard(writer, obj, holder, objId, &tempId);
2775 356 : writer.loadBooleanResult(true);
2776 356 : writer.returnFromIC();
2777 :
2778 0 : trackAttached("NativeHasProp");
2779 178 : return true;
2780 : }
2781 :
2782 : bool
2783 13 : HasPropIRGenerator::tryAttachUnboxed(JSObject* obj, ObjOperandId objId,
2784 : jsid key, ValOperandId keyId)
2785 : {
2786 13 : if (!obj->is<UnboxedPlainObject>())
2787 : return false;
2788 :
2789 0 : const UnboxedLayout::Property* prop = obj->as<UnboxedPlainObject>().layout().lookup(key);
2790 0 : if (!prop)
2791 : return false;
2792 :
2793 0 : emitIdGuard(keyId, key);
2794 0 : writer.guardGroupForLayout(objId, obj->group());
2795 26 : writer.loadBooleanResult(true);
2796 26 : writer.returnFromIC();
2797 :
2798 0 : trackAttached("UnboxedHasProp");
2799 13 : return true;
2800 : }
2801 :
2802 : bool
2803 0 : HasPropIRGenerator::tryAttachUnboxedExpando(JSObject* obj, ObjOperandId objId,
2804 : jsid key, ValOperandId keyId)
2805 : {
2806 0 : if (!obj->is<UnboxedPlainObject>())
2807 : return false;
2808 :
2809 0 : UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
2810 0 : if (!expando)
2811 : return false;
2812 :
2813 0 : Shape* shape = expando->lookup(cx_, key);
2814 0 : if (!shape)
2815 : return false;
2816 :
2817 0 : Maybe<ObjOperandId> tempId;
2818 0 : emitIdGuard(keyId, key);
2819 0 : EmitReadSlotGuard(writer, obj, obj, objId, &tempId);
2820 0 : writer.loadBooleanResult(true);
2821 0 : writer.returnFromIC();
2822 :
2823 0 : trackAttached("UnboxedExpandoHasProp");
2824 0 : return true;
2825 : }
2826 :
2827 : bool
2828 0 : HasPropIRGenerator::tryAttachTypedArray(HandleObject obj, ObjOperandId objId,
2829 : Int32OperandId indexId)
2830 : {
2831 0 : if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
2832 : return false;
2833 :
2834 0 : TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
2835 :
2836 0 : if (IsPrimitiveArrayTypedObject(obj))
2837 0 : writer.guardGroupForLayout(objId, obj->group());
2838 : else
2839 0 : writer.guardShapeForClass(objId, obj->as<TypedArrayObject>().shape());
2840 :
2841 0 : writer.loadTypedElementExistsResult(objId, indexId, layout);
2842 :
2843 0 : writer.returnFromIC();
2844 :
2845 0 : trackAttached("TypedArrayObject");
2846 0 : return true;
2847 : }
2848 :
2849 : bool
2850 0 : HasPropIRGenerator::tryAttachTypedObject(JSObject* obj, ObjOperandId objId,
2851 : jsid key, ValOperandId keyId)
2852 : {
2853 0 : if (!obj->is<TypedObject>())
2854 : return false;
2855 :
2856 0 : if (!obj->as<TypedObject>().typeDescr().hasProperty(cx_->names(), key))
2857 : return false;
2858 :
2859 0 : emitIdGuard(keyId, key);
2860 0 : writer.guardGroupForLayout(objId, obj->group());
2861 0 : writer.loadBooleanResult(true);
2862 0 : writer.returnFromIC();
2863 :
2864 0 : trackAttached("TypedObjectHasProp");
2865 0 : return true;
2866 : }
2867 :
2868 : bool
2869 0 : HasPropIRGenerator::tryAttachSlotDoesNotExist(JSObject* obj, ObjOperandId objId,
2870 : jsid key, ValOperandId keyId)
2871 : {
2872 0 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2873 :
2874 0 : emitIdGuard(keyId, key);
2875 0 : if (hasOwn) {
2876 18 : Maybe<ObjOperandId> tempId;
2877 0 : TestMatchingReceiver(writer, obj, objId, &tempId);
2878 : } else {
2879 544 : Maybe<ObjOperandId> tempId;
2880 0 : EmitReadSlotGuard(writer, obj, nullptr, objId, &tempId);
2881 : }
2882 562 : writer.loadBooleanResult(false);
2883 562 : writer.returnFromIC();
2884 :
2885 0 : trackAttached("DoesNotExist");
2886 281 : return true;
2887 : }
2888 :
2889 : bool
2890 315 : HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj, ObjOperandId objId,
2891 : HandleId key, ValOperandId keyId)
2892 : {
2893 0 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2894 :
2895 : // Check that property doesn't exist on |obj| or it's prototype chain. These
2896 : // checks allow Native/Unboxed/Typed objects with a NativeObject prototype
2897 : // chain. They return false if unknown such as resolve hooks or proxies.
2898 315 : if (hasOwn) {
2899 22 : if (!CheckHasNoSuchOwnProperty(cx_, obj, key))
2900 : return false;
2901 : } else {
2902 608 : if (!CheckHasNoSuchProperty(cx_, obj, key))
2903 : return false;
2904 : }
2905 :
2906 1 : if (tryAttachMegamorphic(objId, keyId))
2907 : return true;
2908 562 : if (tryAttachSlotDoesNotExist(obj, objId, key, keyId))
2909 : return true;
2910 :
2911 0 : return false;
2912 : }
2913 :
2914 : bool
2915 0 : HasPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId,
2916 : ValOperandId keyId)
2917 : {
2918 0 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2919 :
2920 0 : if (!obj->is<ProxyObject>())
2921 : return false;
2922 :
2923 0 : writer.guardIsProxy(objId);
2924 5 : writer.callProxyHasPropResult(objId, keyId, hasOwn);
2925 10 : writer.returnFromIC();
2926 :
2927 0 : trackAttached("ProxyHasProp");
2928 5 : return true;
2929 : }
2930 :
2931 : bool
2932 0 : HasPropIRGenerator::tryAttachStub()
2933 : {
2934 600 : MOZ_ASSERT(cacheKind_ == CacheKind::In ||
2935 : cacheKind_ == CacheKind::HasOwn);
2936 :
2937 1800 : AutoAssertNoPendingException aanpe(cx_);
2938 :
2939 : // NOTE: Argument order is PROPERTY, OBJECT
2940 0 : ValOperandId keyId(writer.setInputOperandId(0));
2941 1200 : ValOperandId valId(writer.setInputOperandId(1));
2942 :
2943 0 : if (!val_.isObject()) {
2944 45 : trackAttached(IRGenerator::NotAttached);
2945 45 : return false;
2946 : }
2947 1665 : RootedObject obj(cx_, &val_.toObject());
2948 555 : ObjOperandId objId = writer.guardIsObject(valId);
2949 :
2950 : // Optimize Proxies
2951 0 : if (tryAttachProxyElement(obj, objId, keyId))
2952 : return true;
2953 :
2954 1650 : RootedId id(cx_);
2955 : bool nameOrSymbol;
2956 0 : if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
2957 0 : cx_->clearPendingException();
2958 0 : return false;
2959 : }
2960 :
2961 550 : if (nameOrSymbol) {
2962 0 : if (tryAttachNamedProp(obj, objId, id, keyId))
2963 : return true;
2964 630 : if (tryAttachDoesNotExist(obj, objId, id, keyId))
2965 : return true;
2966 :
2967 0 : trackAttached(IRGenerator::NotAttached);
2968 0 : return false;
2969 : }
2970 :
2971 : uint32_t index;
2972 17 : Int32OperandId indexId;
2973 0 : if (maybeGuardInt32Index(idVal_, keyId, &index, &indexId)) {
2974 34 : if (tryAttachDense(obj, objId, index, indexId))
2975 : return true;
2976 0 : if (tryAttachDenseHole(obj, objId, index, indexId))
2977 : return true;
2978 0 : if (tryAttachTypedArray(obj, objId, indexId))
2979 : return true;
2980 0 : if (tryAttachSparse(obj, objId, indexId))
2981 : return true;
2982 :
2983 0 : trackAttached(IRGenerator::NotAttached);
2984 0 : return false;
2985 : }
2986 :
2987 0 : trackAttached(IRGenerator::NotAttached);
2988 0 : return false;
2989 : }
2990 :
2991 : void
2992 1 : HasPropIRGenerator::trackAttached(const char* name)
2993 : {
2994 : #ifdef JS_CACHEIR_SPEW
2995 0 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
2996 0 : sp.valueProperty("base", val_);
2997 0 : sp.valueProperty("property", idVal_);
2998 : }
2999 : #endif
3000 600 : }
3001 :
3002 : bool
3003 0 : IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
3004 : uint32_t* int32Index, Int32OperandId* int32IndexId)
3005 : {
3006 398 : if (index.isNumber()) {
3007 : int32_t indexSigned;
3008 391 : if (index.isInt32()) {
3009 0 : indexSigned = index.toInt32();
3010 : } else {
3011 : // We allow negative zero here.
3012 0 : if (!mozilla::NumberEqualsInt32(index.toDouble(), &indexSigned))
3013 : return false;
3014 0 : if (!cx_->runtime()->jitSupportsFloatingPoint)
3015 : return false;
3016 : }
3017 :
3018 0 : if (indexSigned < 0)
3019 : return false;
3020 :
3021 0 : *int32Index = uint32_t(indexSigned);
3022 0 : *int32IndexId = writer.guardIsInt32Index(indexId);
3023 0 : return true;
3024 : }
3025 :
3026 0 : if (index.isString()) {
3027 0 : int32_t indexSigned = GetIndexFromString(index.toString());
3028 0 : if (indexSigned < 0)
3029 : return false;
3030 :
3031 2 : StringOperandId strId = writer.guardIsString(indexId);
3032 2 : *int32Index = uint32_t(indexSigned);
3033 2 : *int32IndexId = writer.guardAndGetIndexFromString(strId);
3034 2 : return true;
3035 : }
3036 :
3037 : return false;
3038 : }
3039 :
3040 2977 : SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
3041 : CacheKind cacheKind, ICState::Mode mode,
3042 : bool* isTemporarilyUnoptimizable,
3043 : HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal,
3044 2977 : bool needsTypeBarrier, bool maybeHasExtraIndexedProps)
3045 : : IRGenerator(cx, script, pc, cacheKind, mode),
3046 : lhsVal_(lhsVal),
3047 : idVal_(idVal),
3048 : rhsVal_(rhsVal),
3049 : isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
3050 : typeCheckInfo_(cx, needsTypeBarrier),
3051 : preliminaryObjectAction_(PreliminaryObjectAction::None),
3052 : attachedTypedArrayOOBStub_(false),
3053 8931 : maybeHasExtraIndexedProps_(maybeHasExtraIndexedProps)
3054 0 : {}
3055 :
3056 : bool
3057 0 : SetPropIRGenerator::tryAttachStub()
3058 : {
3059 0 : AutoAssertNoPendingException aanpe(cx_);
3060 :
3061 0 : ValOperandId objValId(writer.setInputOperandId(0));
3062 0 : ValOperandId rhsValId;
3063 0 : if (cacheKind_ == CacheKind::SetProp) {
3064 0 : rhsValId = ValOperandId(writer.setInputOperandId(1));
3065 : } else {
3066 642 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
3067 0 : MOZ_ASSERT(setElemKeyValueId().id() == 1);
3068 642 : writer.setInputOperandId(1);
3069 0 : rhsValId = ValOperandId(writer.setInputOperandId(2));
3070 : }
3071 :
3072 5793 : RootedId id(cx_);
3073 : bool nameOrSymbol;
3074 0 : if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
3075 0 : cx_->clearPendingException();
3076 0 : return false;
3077 : }
3078 :
3079 0 : if (lhsVal_.isObject()) {
3080 5793 : RootedObject obj(cx_, &lhsVal_.toObject());
3081 :
3082 0 : ObjOperandId objId = writer.guardIsObject(objValId);
3083 0 : if (IsPropertySetOp(JSOp(*pc_))) {
3084 1045 : if (tryAttachMegamorphicSetElement(obj, objId, rhsValId))
3085 : return true;
3086 : }
3087 0 : if (nameOrSymbol) {
3088 3302 : if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
3089 : return true;
3090 2242 : if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
3091 : return true;
3092 0 : if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
3093 : return true;
3094 0 : if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId))
3095 : return true;
3096 0 : if (IsPropertySetOp(JSOp(*pc_))) {
3097 1554 : if (tryAttachSetArrayLength(obj, objId, id, rhsValId))
3098 : return true;
3099 1548 : if (tryAttachSetter(obj, objId, id, rhsValId))
3100 : return true;
3101 1528 : if (tryAttachWindowProxy(obj, objId, id, rhsValId))
3102 : return true;
3103 1528 : if (tryAttachProxy(obj, objId, id, rhsValId))
3104 : return true;
3105 : }
3106 : return false;
3107 : }
3108 :
3109 486 : if (IsPropertySetOp(JSOp(*pc_))) {
3110 0 : if (tryAttachProxyElement(obj, objId, rhsValId))
3111 : return true;
3112 : }
3113 :
3114 : uint32_t index;
3115 243 : Int32OperandId indexId;
3116 0 : if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) {
3117 476 : if (tryAttachSetDenseElement(obj, objId, index, indexId, rhsValId))
3118 : return true;
3119 420 : if (tryAttachSetDenseElementHole(obj, objId, index, indexId, rhsValId))
3120 : return true;
3121 12 : if (tryAttachSetTypedElement(obj, objId, index, indexId, rhsValId))
3122 : return true;
3123 6 : return false;
3124 : }
3125 : return false;
3126 : }
3127 : return false;
3128 : }
3129 :
3130 : static void
3131 0 : EmitStoreSlotAndReturn(CacheIRWriter& writer, ObjOperandId objId, NativeObject* nobj, Shape* shape,
3132 : ValOperandId rhsId)
3133 : {
3134 0 : if (nobj->isFixedSlot(shape->slot())) {
3135 938 : size_t offset = NativeObject::getFixedSlotOffset(shape->slot());
3136 0 : writer.storeFixedSlot(objId, offset, rhsId);
3137 : } else {
3138 60 : size_t offset = nobj->dynamicSlotIndex(shape->slot()) * sizeof(Value);
3139 60 : writer.storeDynamicSlot(objId, offset, rhsId);
3140 : }
3141 529 : writer.returnFromIC();
3142 0 : }
3143 :
3144 : static Shape*
3145 1540 : LookupShapeForSetSlot(JSOp op, NativeObject* obj, jsid id)
3146 : {
3147 1540 : Shape* shape = obj->lookupPure(id);
3148 1540 : if (!shape || !shape->isDataProperty() || !shape->writable())
3149 : return nullptr;
3150 :
3151 : // If this is an op like JSOP_INITELEM / [[DefineOwnProperty]], the
3152 : // property's attributes may have to be changed too, so make sure it's a
3153 : // simple data property.
3154 619 : if (IsPropertyInitOp(op) && (!shape->configurable() || !shape->enumerable()))
3155 : return nullptr;
3156 :
3157 619 : return shape;
3158 : }
3159 :
3160 : static bool
3161 1651 : CanAttachNativeSetSlot(JSContext* cx, JSOp op, HandleObject obj, HandleId id,
3162 : bool* isTemporarilyUnoptimizable, MutableHandleShape propShape)
3163 : {
3164 3302 : if (!obj->isNative())
3165 : return false;
3166 :
3167 0 : propShape.set(LookupShapeForSetSlot(op, &obj->as<NativeObject>(), id));
3168 1 : if (!propShape)
3169 : return false;
3170 :
3171 619 : ObjectGroup* group = JSObject::getGroup(cx, obj);
3172 619 : if (!group) {
3173 0 : cx->recoverFromOutOfMemory();
3174 0 : return false;
3175 : }
3176 :
3177 : // For some property writes, such as the initial overwrite of global
3178 : // properties, TI will not mark the property as having been
3179 : // overwritten. Don't attach a stub in this case, so that we don't
3180 : // execute another write to the property without TI seeing that write.
3181 1238 : EnsureTrackPropertyTypes(cx, obj, id);
3182 1238 : if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
3183 89 : *isTemporarilyUnoptimizable = true;
3184 89 : return false;
3185 : }
3186 :
3187 : return true;
3188 : }
3189 :
3190 : bool
3191 1651 : SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
3192 : ValOperandId rhsId)
3193 : {
3194 0 : RootedShape propShape(cx_);
3195 0 : if (!CanAttachNativeSetSlot(cx_, JSOp(*pc_), obj, id, isTemporarilyUnoptimizable_, &propShape))
3196 : return false;
3197 :
3198 0 : if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp) {
3199 3 : writer.megamorphicStoreSlot(objId, JSID_TO_ATOM(id)->asPropertyName(), rhsId,
3200 2 : typeCheckInfo_.needsTypeBarrier());
3201 0 : writer.returnFromIC();
3202 1 : trackAttached("MegamorphicNativeSlot");
3203 1 : return true;
3204 : }
3205 :
3206 0 : maybeEmitIdGuard(id);
3207 :
3208 : // If we need a property type barrier (always in Baseline, sometimes in
3209 : // Ion), guard on both the shape and the group. If Ion knows the property
3210 : // types match, we don't need the group guard.
3211 0 : NativeObject* nobj = &obj->as<NativeObject>();
3212 0 : if (typeCheckInfo_.needsTypeBarrier())
3213 529 : writer.guardGroupForTypeBarrier(objId, nobj->group());
3214 0 : TestMatchingNativeReceiver(writer, nobj, objId);
3215 :
3216 0 : if (IsPreliminaryObject(obj))
3217 0 : preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
3218 : else
3219 0 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
3220 :
3221 529 : typeCheckInfo_.set(nobj->group(), id);
3222 529 : EmitStoreSlotAndReturn(writer, objId, nobj, propShape, rhsId);
3223 :
3224 0 : trackAttached("NativeSlot");
3225 529 : return true;
3226 : }
3227 :
3228 : bool
3229 1121 : SetPropIRGenerator::tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId,
3230 : HandleId id, ValOperandId rhsId)
3231 : {
3232 2242 : if (!obj->is<UnboxedPlainObject>())
3233 : return false;
3234 :
3235 0 : UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
3236 99 : if (!expando)
3237 : return false;
3238 :
3239 0 : Shape* propShape = LookupShapeForSetSlot(JSOp(*pc_), expando, id);
3240 0 : if (!propShape)
3241 : return false;
3242 :
3243 0 : maybeEmitIdGuard(id);
3244 :
3245 0 : Maybe<ObjOperandId> expandoId;
3246 0 : TestMatchingReceiver(writer, obj, objId, &expandoId);
3247 :
3248 : // Property types must be added to the unboxed object's group, not the
3249 : // expando's group (it has unknown properties).
3250 0 : typeCheckInfo_.set(obj->group(), id);
3251 0 : EmitStoreSlotAndReturn(writer, expandoId.ref(), expando, propShape, rhsId);
3252 :
3253 0 : trackAttached("UnboxedExpando");
3254 0 : return true;
3255 : }
3256 :
3257 : static void
3258 67 : EmitGuardUnboxedPropertyType(CacheIRWriter& writer, JSValueType propType, ValOperandId valId)
3259 : {
3260 67 : if (propType == JSVAL_TYPE_OBJECT) {
3261 : // Unboxed objects store NullValue as nullptr object.
3262 33 : writer.guardIsObjectOrNull(valId);
3263 : } else {
3264 0 : writer.guardType(valId, propType);
3265 : }
3266 67 : }
3267 :
3268 : bool
3269 1121 : SetPropIRGenerator::tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
3270 : ValOperandId rhsId)
3271 : {
3272 2242 : if (!obj->is<UnboxedPlainObject>())
3273 : return false;
3274 :
3275 297 : if (!cx_->runtime()->jitSupportsFloatingPoint)
3276 : return false;
3277 :
3278 0 : const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
3279 0 : if (!property)
3280 : return false;
3281 :
3282 0 : maybeEmitIdGuard(id);
3283 0 : writer.guardGroupForLayout(objId, obj->group());
3284 67 : EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
3285 0 : writer.storeUnboxedProperty(objId, property->type,
3286 0 : UnboxedPlainObject::offsetOfData() + property->offset,
3287 67 : rhsId);
3288 0 : writer.returnFromIC();
3289 :
3290 134 : typeCheckInfo_.set(obj->group(), id);
3291 67 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
3292 :
3293 0 : trackAttached("Unboxed");
3294 67 : return true;
3295 : }
3296 :
3297 : bool
3298 1054 : SetPropIRGenerator::tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id,
3299 : ValOperandId rhsId)
3300 : {
3301 2108 : if (!obj->is<TypedObject>())
3302 : return false;
3303 :
3304 0 : if (!cx_->runtime()->jitSupportsFloatingPoint || cx_->zone()->detachedTypedObjects)
3305 : return false;
3306 :
3307 0 : if (!obj->as<TypedObject>().typeDescr().is<StructTypeDescr>())
3308 : return false;
3309 :
3310 0 : StructTypeDescr* structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>();
3311 : size_t fieldIndex;
3312 0 : if (!structDescr->fieldIndex(id, &fieldIndex))
3313 : return false;
3314 :
3315 0 : TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
3316 0 : if (!fieldDescr->is<SimpleTypeDescr>())
3317 : return false;
3318 :
3319 0 : uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
3320 0 : TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
3321 :
3322 0 : maybeEmitIdGuard(id);
3323 0 : writer.guardNoDetachedTypedObjects();
3324 0 : writer.guardGroupForLayout(objId, obj->group());
3325 :
3326 0 : typeCheckInfo_.set(obj->group(), id);
3327 :
3328 : // Scalar types can always be stored without a type update stub.
3329 0 : if (fieldDescr->is<ScalarTypeDescr>()) {
3330 0 : Scalar::Type type = fieldDescr->as<ScalarTypeDescr>().type();
3331 0 : writer.storeTypedObjectScalarProperty(objId, fieldOffset, layout, type, rhsId);
3332 0 : writer.returnFromIC();
3333 :
3334 0 : trackAttached("TypedObject");
3335 0 : return true;
3336 : }
3337 :
3338 : // For reference types, guard on the RHS type first, so that
3339 : // StoreTypedObjectReferenceProperty is infallible.
3340 0 : ReferenceTypeDescr::Type type = fieldDescr->as<ReferenceTypeDescr>().type();
3341 0 : switch (type) {
3342 : case ReferenceTypeDescr::TYPE_ANY:
3343 : break;
3344 : case ReferenceTypeDescr::TYPE_OBJECT:
3345 0 : writer.guardIsObjectOrNull(rhsId);
3346 0 : break;
3347 : case ReferenceTypeDescr::TYPE_STRING:
3348 0 : writer.guardType(rhsId, JSVAL_TYPE_STRING);
3349 0 : break;
3350 : }
3351 :
3352 0 : writer.storeTypedObjectReferenceProperty(objId, fieldOffset, layout, type, rhsId);
3353 0 : writer.returnFromIC();
3354 :
3355 0 : trackAttached("TypedObject");
3356 0 : return true;
3357 : }
3358 :
3359 : void
3360 1 : SetPropIRGenerator::trackAttached(const char* name)
3361 : {
3362 : #ifdef JS_CACHEIR_SPEW
3363 5796 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
3364 0 : sp.valueProperty("base", lhsVal_);
3365 0 : sp.valueProperty("property", idVal_);
3366 0 : sp.valueProperty("value", rhsVal_);
3367 : }
3368 : #endif
3369 0 : }
3370 :
3371 : static bool
3372 0 : IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
3373 : {
3374 754 : if (!shape || !IsCacheableProtoChain(obj, holder))
3375 : return false;
3376 :
3377 44 : if (!shape->hasSetterValue())
3378 : return false;
3379 :
3380 28 : if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
3381 : return false;
3382 :
3383 14 : JSFunction& setter = shape->setterObject()->as<JSFunction>();
3384 14 : if (!setter.isNativeWithCppEntry())
3385 : return false;
3386 :
3387 8 : if (setter.isClassConstructor())
3388 : return false;
3389 :
3390 16 : if (setter.hasJitInfo() && !setter.jitInfo()->needsOuterizedThisObject())
3391 : return true;
3392 :
3393 0 : return !IsWindow(obj);
3394 : }
3395 :
3396 : static bool
3397 756 : IsCacheableSetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
3398 : bool* isTemporarilyUnoptimizable = nullptr)
3399 : {
3400 756 : if (!shape || !IsCacheableProtoChain(obj, holder))
3401 : return false;
3402 :
3403 46 : if (IsWindow(obj))
3404 : return false;
3405 :
3406 46 : if (!shape->hasSetterValue())
3407 : return false;
3408 :
3409 32 : if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
3410 : return false;
3411 :
3412 0 : JSFunction& setter = shape->setterObject()->as<JSFunction>();
3413 16 : if (setter.isNativeWithCppEntry())
3414 : return false;
3415 :
3416 : // Natives with jit entry can use the scripted path.
3417 0 : if (setter.isNativeWithJitEntry())
3418 : return true;
3419 :
3420 12 : if (!setter.hasScript()) {
3421 0 : if (isTemporarilyUnoptimizable)
3422 0 : *isTemporarilyUnoptimizable = true;
3423 : return false;
3424 : }
3425 :
3426 12 : if (setter.isClassConstructor())
3427 : return false;
3428 :
3429 12 : return true;
3430 : }
3431 :
3432 : static bool
3433 0 : CanAttachSetter(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id,
3434 : MutableHandleObject holder, MutableHandleShape propShape,
3435 : bool* isTemporarilyUnoptimizable)
3436 : {
3437 : // Don't attach a setter stub for ops like JSOP_INITELEM.
3438 1548 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
3439 :
3440 774 : PropertyResult prop;
3441 2322 : if (!LookupPropertyPure(cx, obj, id, holder.address(), &prop))
3442 : return false;
3443 :
3444 0 : if (prop.isNonNativeProperty())
3445 : return false;
3446 :
3447 750 : propShape.set(prop.maybeShape());
3448 2994 : if (!IsCacheableSetPropCallScripted(obj, holder, propShape, isTemporarilyUnoptimizable) &&
3449 2232 : !IsCacheableSetPropCallNative(obj, holder, propShape))
3450 : {
3451 : return false;
3452 : }
3453 :
3454 : return true;
3455 : }
3456 :
3457 : static void
3458 0 : EmitCallSetterNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
3459 : Shape* shape, ObjOperandId objId, ValOperandId rhsId)
3460 : {
3461 10 : if (IsCacheableSetPropCallNative(obj, holder, shape)) {
3462 4 : JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
3463 4 : MOZ_ASSERT(target->isNativeWithCppEntry());
3464 0 : writer.callNativeSetter(objId, target, rhsId);
3465 : writer.returnFromIC();
3466 : return;
3467 : }
3468 :
3469 6 : MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
3470 :
3471 6 : JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
3472 6 : MOZ_ASSERT(target->hasJitEntry());
3473 0 : writer.callScriptedSetter(objId, target, rhsId);
3474 : writer.returnFromIC();
3475 : }
3476 :
3477 : bool
3478 0 : SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
3479 : ValOperandId rhsId)
3480 : {
3481 0 : RootedObject holder(cx_);
3482 2322 : RootedShape propShape(cx_);
3483 2322 : if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &propShape, isTemporarilyUnoptimizable_))
3484 : return false;
3485 :
3486 0 : maybeEmitIdGuard(id);
3487 :
3488 : // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
3489 : // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
3490 : // require outerizing).
3491 0 : if (mode_ == ICState::Mode::Specialized || IsWindow(obj)) {
3492 20 : Maybe<ObjOperandId> expandoId;
3493 10 : TestMatchingReceiver(writer, obj, objId, &expandoId);
3494 :
3495 0 : if (obj != holder) {
3496 20 : GeneratePrototypeGuards(writer, obj, holder, objId);
3497 :
3498 : // Guard on the holder's shape.
3499 10 : ObjOperandId holderId = writer.loadObject(holder);
3500 10 : TestMatchingHolder(writer, holder, holderId);
3501 : }
3502 : } else {
3503 0 : writer.guardHasGetterSetter(objId, propShape);
3504 : }
3505 :
3506 30 : EmitCallSetterNoGuards(writer, obj, holder, propShape, objId, rhsId);
3507 :
3508 0 : trackAttached("Setter");
3509 10 : return true;
3510 : }
3511 :
3512 : bool
3513 777 : SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id,
3514 : ValOperandId rhsId)
3515 : {
3516 : // Don't attach an array length stub for ops like JSOP_INITELEM.
3517 1554 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
3518 :
3519 2334 : if (!obj->is<ArrayObject>() ||
3520 792 : !JSID_IS_ATOM(id, cx_->names().length) ||
3521 0 : !obj->as<ArrayObject>().lengthIsWritable())
3522 : {
3523 : return false;
3524 : }
3525 :
3526 0 : maybeEmitIdGuard(id);
3527 0 : writer.guardClass(objId, GuardClassKind::Array);
3528 6 : writer.callSetArrayLength(objId, IsStrictSetPC(pc_), rhsId);
3529 6 : writer.returnFromIC();
3530 :
3531 0 : trackAttached("SetArrayLength");
3532 3 : return true;
3533 : }
3534 :
3535 : bool
3536 238 : SetPropIRGenerator::tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId, uint32_t index,
3537 : Int32OperandId indexId, ValOperandId rhsId)
3538 : {
3539 476 : if (!obj->isNative())
3540 : return false;
3541 :
3542 0 : NativeObject* nobj = &obj->as<NativeObject>();
3543 0 : if (!nobj->containsDenseElement(index) || nobj->getElementsHeader()->isFrozen())
3544 : return false;
3545 :
3546 0 : if (typeCheckInfo_.needsTypeBarrier())
3547 28 : writer.guardGroupForTypeBarrier(objId, nobj->group());
3548 56 : TestMatchingNativeReceiver(writer, nobj, objId);
3549 :
3550 28 : writer.storeDenseElement(objId, indexId, rhsId);
3551 0 : writer.returnFromIC();
3552 :
3553 : // Type inference uses JSID_VOID for the element types.
3554 28 : typeCheckInfo_.set(nobj->group(), JSID_VOID);
3555 :
3556 0 : trackAttached("SetDenseElement");
3557 28 : return true;
3558 : }
3559 :
3560 : static bool
3561 261 : CanAttachAddElement(NativeObject* obj, bool isInit)
3562 : {
3563 : // Make sure the objects on the prototype don't have any indexed properties
3564 : // or that such properties can't appear without a shape change.
3565 : do {
3566 : // The first two checks are also relevant to the receiver object.
3567 0 : if (obj->isIndexed())
3568 : return false;
3569 :
3570 760 : const Class* clasp = obj->getClass();
3571 821 : if (clasp != &ArrayObject::class_ &&
3572 61 : (clasp->getAddProperty() ||
3573 61 : clasp->getResolve() ||
3574 61 : clasp->getOpsLookupProperty() ||
3575 : clasp->getOpsSetProperty()))
3576 : {
3577 : return false;
3578 : }
3579 :
3580 : // If we're initializing a property instead of setting one, the objects
3581 : // on the prototype are not relevant.
3582 380 : if (isInit)
3583 : break;
3584 :
3585 179 : JSObject* proto = obj->staticPrototype();
3586 179 : if (!proto)
3587 : break;
3588 :
3589 119 : if (!proto->isNative())
3590 : return false;
3591 :
3592 : // We have to make sure the proto has no non-writable (frozen) elements
3593 : // because we're not allowed to shadow them. There are a few cases to
3594 : // consider:
3595 : //
3596 : // * If the proto is extensible, its Shape will change when it's made
3597 : // non-extensible.
3598 : //
3599 : // * If the proto is already non-extensible, no new elements will be
3600 : // added, so if there are no elements now it doesn't matter if the
3601 : // object is frozen later on.
3602 119 : NativeObject* nproto = &proto->as<NativeObject>();
3603 119 : if (!nproto->isExtensible() && nproto->getDenseInitializedLength() > 0)
3604 : return false;
3605 :
3606 : obj = nproto;
3607 : } while (true);
3608 :
3609 : return true;
3610 : }
3611 :
3612 : bool
3613 210 : SetPropIRGenerator::tryAttachSetDenseElementHole(HandleObject obj, ObjOperandId objId,
3614 : uint32_t index, Int32OperandId indexId,
3615 : ValOperandId rhsId)
3616 : {
3617 630 : if (!obj->isNative() || rhsVal_.isMagic(JS_ELEMENTS_HOLE))
3618 : return false;
3619 :
3620 210 : JSOp op = JSOp(*pc_);
3621 0 : MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
3622 :
3623 210 : if (op == JSOP_INITHIDDENELEM)
3624 : return false;
3625 :
3626 210 : NativeObject* nobj = &obj->as<NativeObject>();
3627 210 : if (!nobj->isExtensible())
3628 : return false;
3629 :
3630 420 : MOZ_ASSERT(!nobj->getElementsHeader()->isFrozen(),
3631 : "Extensible objects should not have frozen elements");
3632 :
3633 0 : uint32_t initLength = nobj->getDenseInitializedLength();
3634 :
3635 : // Optimize if we're adding an element at initLength or writing to a hole.
3636 : // Don't handle the adding case if the current accesss is in bounds, to
3637 : // ensure we always call noteArrayWriteHole.
3638 210 : bool isAdd = index == initLength;
3639 0 : bool isHoleInBounds = index < initLength && !nobj->containsDenseElement(index);
3640 210 : if (!isAdd && !isHoleInBounds)
3641 : return false;
3642 :
3643 : // Can't add new elements to arrays with non-writable length.
3644 610 : if (isAdd && nobj->is<ArrayObject>() && !nobj->as<ArrayObject>().lengthIsWritable())
3645 : return false;
3646 :
3647 : // Typed arrays don't have dense elements.
3648 408 : if (nobj->is<TypedArrayObject>())
3649 : return false;
3650 :
3651 : // Check for other indexed properties or class hooks.
3652 0 : if (!CanAttachAddElement(nobj, IsPropertyInitOp(op)))
3653 : return false;
3654 :
3655 204 : if (typeCheckInfo_.needsTypeBarrier())
3656 0 : writer.guardGroupForTypeBarrier(objId, nobj->group());
3657 0 : TestMatchingNativeReceiver(writer, nobj, objId);
3658 :
3659 : // Also shape guard the proto chain, unless this is an INITELEM or we know
3660 : // the proto chain has no indexed props.
3661 204 : if (IsPropertySetOp(op) && maybeHasExtraIndexedProps_)
3662 3 : ShapeGuardProtoChain(writer, obj, objId);
3663 :
3664 204 : writer.storeDenseElementHole(objId, indexId, rhsId, isAdd);
3665 0 : writer.returnFromIC();
3666 :
3667 : // Type inference uses JSID_VOID for the element types.
3668 204 : typeCheckInfo_.set(nobj->group(), JSID_VOID);
3669 :
3670 0 : trackAttached(isAdd ? "AddDenseElement" : "StoreDenseElementHole");
3671 204 : return true;
3672 : }
3673 :
3674 : bool
3675 6 : SetPropIRGenerator::tryAttachSetTypedElement(HandleObject obj, ObjOperandId objId,
3676 : uint32_t index, Int32OperandId indexId,
3677 : ValOperandId rhsId)
3678 : {
3679 18 : if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
3680 : return false;
3681 :
3682 0 : if (!rhsVal_.isNumber())
3683 : return false;
3684 :
3685 0 : if (!cx_->runtime()->jitSupportsFloatingPoint && TypedThingRequiresFloatingPoint(obj))
3686 : return false;
3687 :
3688 0 : bool handleOutOfBounds = false;
3689 0 : if (obj->is<TypedArrayObject>()) {
3690 0 : handleOutOfBounds = (index >= obj->as<TypedArrayObject>().length());
3691 : } else {
3692 : // Typed objects throw on out of bounds accesses. Don't attach
3693 : // a stub in this case.
3694 0 : if (index >= obj->as<TypedObject>().length())
3695 : return false;
3696 :
3697 : // Don't attach stubs if the underlying storage for typed objects
3698 : // in the zone could be detached, as the stub will always bail out.
3699 0 : if (cx_->zone()->detachedTypedObjects)
3700 : return false;
3701 : }
3702 :
3703 0 : Scalar::Type elementType = TypedThingElementType(obj);
3704 0 : TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
3705 :
3706 0 : if (IsPrimitiveArrayTypedObject(obj)) {
3707 0 : writer.guardNoDetachedTypedObjects();
3708 0 : writer.guardGroupForLayout(objId, obj->group());
3709 : } else {
3710 0 : writer.guardShapeForClass(objId, obj->as<TypedArrayObject>().shape());
3711 : }
3712 :
3713 0 : writer.storeTypedElement(objId, indexId, rhsId, layout, elementType, handleOutOfBounds);
3714 0 : writer.returnFromIC();
3715 :
3716 0 : if (handleOutOfBounds)
3717 0 : attachedTypedArrayOOBStub_ = true;
3718 :
3719 0 : trackAttached(handleOutOfBounds ? "SetTypedElementOOB" : "SetTypedElement");
3720 0 : return true;
3721 : }
3722 :
3723 : bool
3724 0 : SetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
3725 : ValOperandId rhsId, bool handleDOMProxies)
3726 : {
3727 14 : MOZ_ASSERT(obj->is<ProxyObject>());
3728 :
3729 7 : writer.guardIsProxy(objId);
3730 :
3731 0 : if (!handleDOMProxies) {
3732 : // Ensure that the incoming object is not a DOM proxy, so that we can
3733 : // get to the specialized stubs. If handleDOMProxies is true, we were
3734 : // unable to attach a specialized DOM stub, so we just handle all
3735 : // proxies here.
3736 0 : writer.guardNotDOMProxy(objId);
3737 : }
3738 :
3739 0 : if (cacheKind_ == CacheKind::SetProp || mode_ == ICState::Mode::Specialized) {
3740 0 : maybeEmitIdGuard(id);
3741 0 : writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
3742 : } else {
3743 : // Attach a stub that handles every id.
3744 0 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
3745 0 : MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
3746 0 : writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
3747 : }
3748 :
3749 14 : writer.returnFromIC();
3750 :
3751 0 : trackAttached("GenericProxy");
3752 7 : return true;
3753 : }
3754 :
3755 : bool
3756 0 : SetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id,
3757 : ValOperandId rhsId)
3758 : {
3759 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
3760 :
3761 0 : maybeEmitIdGuard(id);
3762 0 : TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
3763 0 : writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
3764 0 : writer.returnFromIC();
3765 :
3766 0 : trackAttached("DOMProxyShadowed");
3767 0 : return true;
3768 : }
3769 :
3770 : bool
3771 0 : SetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id,
3772 : ValOperandId rhsId)
3773 : {
3774 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
3775 :
3776 0 : RootedObject proto(cx_, obj->staticPrototype());
3777 0 : if (!proto)
3778 : return false;
3779 :
3780 0 : RootedObject holder(cx_);
3781 0 : RootedShape propShape(cx_);
3782 0 : if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &propShape, isTemporarilyUnoptimizable_))
3783 : return false;
3784 :
3785 0 : maybeEmitIdGuard(id);
3786 :
3787 : // Guard that our expando object hasn't started shadowing this property.
3788 0 : TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
3789 0 : CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
3790 :
3791 0 : GeneratePrototypeGuards(writer, obj, holder, objId);
3792 :
3793 : // Guard on the holder of the property.
3794 0 : ObjOperandId holderId = writer.loadObject(holder);
3795 0 : TestMatchingHolder(writer, holder, holderId);
3796 :
3797 : // EmitCallSetterNoGuards expects |obj| to be the object the property is
3798 : // on to do some checks. Since we actually looked at proto, and no extra
3799 : // guards will be generated, we can just pass that instead.
3800 0 : EmitCallSetterNoGuards(writer, proto, holder, propShape, objId, rhsId);
3801 :
3802 0 : trackAttached("DOMProxyUnshadowed");
3803 0 : return true;
3804 : }
3805 :
3806 : bool
3807 0 : SetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id,
3808 : ValOperandId rhsId)
3809 : {
3810 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
3811 :
3812 0 : RootedValue expandoVal(cx_, GetProxyPrivate(obj));
3813 0 : RootedObject expandoObj(cx_);
3814 0 : if (expandoVal.isObject()) {
3815 0 : expandoObj = &expandoVal.toObject();
3816 : } else {
3817 0 : MOZ_ASSERT(!expandoVal.isUndefined(),
3818 : "How did a missing expando manage to shadow things?");
3819 0 : auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
3820 0 : MOZ_ASSERT(expandoAndGeneration);
3821 0 : expandoObj = &expandoAndGeneration->expando.toObject();
3822 : }
3823 :
3824 0 : RootedShape propShape(cx_);
3825 0 : if (CanAttachNativeSetSlot(cx_, JSOp(*pc_), expandoObj, id, isTemporarilyUnoptimizable_,
3826 : &propShape))
3827 : {
3828 0 : maybeEmitIdGuard(id);
3829 : ObjOperandId expandoObjId =
3830 0 : guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
3831 :
3832 0 : NativeObject* nativeExpandoObj = &expandoObj->as<NativeObject>();
3833 0 : writer.guardGroupForTypeBarrier(expandoObjId, nativeExpandoObj->group());
3834 0 : typeCheckInfo_.set(nativeExpandoObj->group(), id);
3835 :
3836 0 : EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, propShape, rhsId);
3837 0 : trackAttached("DOMProxyExpandoSlot");
3838 : return true;
3839 : }
3840 :
3841 0 : RootedObject holder(cx_);
3842 0 : if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &propShape,
3843 : isTemporarilyUnoptimizable_))
3844 : {
3845 : // Note that we don't actually use the expandoObjId here after the
3846 : // shape guard. The DOM proxy (objId) is passed to the setter as
3847 : // |this|.
3848 0 : maybeEmitIdGuard(id);
3849 0 : guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
3850 :
3851 0 : MOZ_ASSERT(holder == expandoObj);
3852 0 : EmitCallSetterNoGuards(writer, expandoObj, expandoObj, propShape, objId, rhsId);
3853 0 : trackAttached("DOMProxyExpandoSetter");
3854 0 : return true;
3855 : }
3856 :
3857 : return false;
3858 : }
3859 :
3860 : bool
3861 764 : SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id,
3862 : ValOperandId rhsId)
3863 : {
3864 : // Don't attach a proxy stub for ops like JSOP_INITELEM.
3865 1528 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
3866 :
3867 0 : ProxyStubType type = GetProxyStubType(cx_, obj, id);
3868 764 : if (type == ProxyStubType::None)
3869 : return false;
3870 :
3871 7 : if (mode_ == ICState::Mode::Megamorphic)
3872 1 : return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
3873 :
3874 6 : switch (type) {
3875 : case ProxyStubType::None:
3876 : break;
3877 : case ProxyStubType::DOMExpando:
3878 0 : if (tryAttachDOMProxyExpando(obj, objId, id, rhsId))
3879 : return true;
3880 0 : if (*isTemporarilyUnoptimizable_) {
3881 : // Scripted setter without JIT code. Just wait.
3882 : return false;
3883 : }
3884 : MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
3885 : case ProxyStubType::DOMShadowed:
3886 0 : return tryAttachDOMProxyShadowed(obj, objId, id, rhsId);
3887 : case ProxyStubType::DOMUnshadowed:
3888 0 : if (tryAttachDOMProxyUnshadowed(obj, objId, id, rhsId))
3889 : return true;
3890 0 : if (*isTemporarilyUnoptimizable_) {
3891 : // Scripted setter without JIT code. Just wait.
3892 : return false;
3893 : }
3894 0 : return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
3895 : case ProxyStubType::Generic:
3896 6 : return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ false);
3897 : }
3898 :
3899 0 : MOZ_CRASH("Unexpected ProxyStubType");
3900 : }
3901 :
3902 : bool
3903 0 : SetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId)
3904 : {
3905 : // Don't attach a proxy stub for ops like JSOP_INITELEM.
3906 0 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
3907 :
3908 84 : if (!obj->is<ProxyObject>())
3909 : return false;
3910 :
3911 0 : writer.guardIsProxy(objId);
3912 :
3913 : // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
3914 : // proxies here as we don't have specialized DOM stubs for this.
3915 0 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
3916 0 : writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
3917 0 : writer.returnFromIC();
3918 :
3919 0 : trackAttached("ProxyElement");
3920 0 : return true;
3921 : }
3922 :
3923 : bool
3924 0 : SetPropIRGenerator::tryAttachMegamorphicSetElement(HandleObject obj, ObjOperandId objId,
3925 : ValOperandId rhsId)
3926 : {
3927 2090 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
3928 :
3929 1045 : if (mode_ != ICState::Mode::Megamorphic || cacheKind_ != CacheKind::SetElem)
3930 : return false;
3931 :
3932 : // The generic proxy stubs are faster.
3933 74 : if (obj->is<ProxyObject>())
3934 : return false;
3935 :
3936 74 : writer.megamorphicSetElement(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
3937 74 : writer.returnFromIC();
3938 :
3939 0 : trackAttached("MegamorphicSetElement");
3940 37 : return true;
3941 : }
3942 :
3943 : bool
3944 764 : SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id,
3945 : ValOperandId rhsId)
3946 : {
3947 : // Attach a stub when the receiver is a WindowProxy and we can do the set
3948 : // on the Window (the global object).
3949 :
3950 0 : if (!IsWindowProxy(obj))
3951 : return false;
3952 :
3953 : // If we're megamorphic prefer a generic proxy stub that handles a lot more
3954 : // cases.
3955 0 : if (mode_ == ICState::Mode::Megamorphic)
3956 : return false;
3957 :
3958 : // This must be a WindowProxy for the current Window/global. Else it would
3959 : // be a cross-compartment wrapper and IsWindowProxy returns false for
3960 : // those.
3961 0 : MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
3962 0 : MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
3963 :
3964 : // Now try to do the set on the Window (the current global).
3965 0 : Handle<GlobalObject*> windowObj = cx_->global();
3966 :
3967 0 : RootedShape propShape(cx_);
3968 0 : if (!CanAttachNativeSetSlot(cx_, JSOp(*pc_), windowObj, id, isTemporarilyUnoptimizable_,
3969 : &propShape))
3970 : {
3971 : return false;
3972 : }
3973 :
3974 0 : maybeEmitIdGuard(id);
3975 :
3976 0 : writer.guardClass(objId, GuardClassKind::WindowProxy);
3977 0 : ObjOperandId windowObjId = writer.loadObject(windowObj);
3978 :
3979 0 : writer.guardShape(windowObjId, windowObj->lastProperty());
3980 0 : writer.guardGroupForTypeBarrier(windowObjId, windowObj->group());
3981 0 : typeCheckInfo_.set(windowObj->group(), id);
3982 :
3983 0 : EmitStoreSlotAndReturn(writer, windowObjId, windowObj, propShape, rhsId);
3984 :
3985 0 : trackAttached("WindowProxySlot");
3986 0 : return true;
3987 : }
3988 :
3989 : bool
3990 0 : SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape)
3991 : {
3992 0 : AutoAssertNoPendingException aanpe(cx_);
3993 :
3994 0 : ValOperandId objValId(writer.setInputOperandId(0));
3995 0 : ValOperandId rhsValId;
3996 0 : if (cacheKind_ == CacheKind::SetProp) {
3997 0 : rhsValId = ValOperandId(writer.setInputOperandId(1));
3998 : } else {
3999 352 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
4000 0 : MOZ_ASSERT(setElemKeyValueId().id() == 1);
4001 352 : writer.setInputOperandId(1);
4002 0 : rhsValId = ValOperandId(writer.setInputOperandId(2));
4003 : }
4004 :
4005 3138 : RootedId id(cx_);
4006 : bool nameOrSymbol;
4007 0 : if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
4008 0 : cx_->clearPendingException();
4009 0 : return false;
4010 : }
4011 :
4012 0 : if (!lhsVal_.isObject() || !nameOrSymbol)
4013 : return false;
4014 :
4015 3105 : RootedObject obj(cx_, &lhsVal_.toObject());
4016 :
4017 1035 : PropertyResult prop;
4018 : JSObject* holder;
4019 0 : if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
4020 : return false;
4021 2060 : if (obj != holder)
4022 : return false;
4023 :
4024 0 : Shape* propShape = nullptr;
4025 999 : NativeObject* holderOrExpando = nullptr;
4026 :
4027 1998 : if (obj->isNative()) {
4028 0 : propShape = prop.shape();
4029 0 : holderOrExpando = &obj->as<NativeObject>();
4030 : } else {
4031 0 : if (!obj->is<UnboxedPlainObject>())
4032 : return false;
4033 32 : UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
4034 32 : if (!expando)
4035 : return false;
4036 32 : propShape = expando->lookupPure(id);
4037 0 : if (!propShape)
4038 : return false;
4039 : holderOrExpando = expando;
4040 : }
4041 :
4042 999 : MOZ_ASSERT(propShape);
4043 :
4044 : // The property must be the last added property of the object.
4045 0 : if (holderOrExpando->lastProperty() != propShape)
4046 : return false;
4047 :
4048 : // Object must be extensible, oldShape must be immediate parent of
4049 : // current shape.
4050 0 : if (!obj->nonProxyIsExtensible() || propShape->previous() != oldShape)
4051 : return false;
4052 :
4053 : // Basic shape checks.
4054 2600 : if (propShape->inDictionary() ||
4055 1725 : !propShape->isDataProperty() ||
4056 854 : !propShape->writable())
4057 : {
4058 : return false;
4059 : }
4060 :
4061 : // Watch out for resolve hooks.
4062 4220 : if (ClassMayResolveId(cx_->names(), obj->getClass(), id, obj)) {
4063 : // The JSFunction resolve hook defines a (non-configurable and
4064 : // non-enumerable) |prototype| property on certain functions. Scripts
4065 : // often assign a custom |prototype| object and we want to optimize
4066 : // this |prototype| set and eliminate the default object allocation.
4067 : //
4068 : // We check group->maybeInterpretedFunction() here and guard on the
4069 : // group. The group is unique for a particular function so this ensures
4070 : // we don't add the default prototype property to functions that don't
4071 : // have it.
4072 12 : if (!obj->is<JSFunction>() ||
4073 18 : !JSID_IS_ATOM(id, cx_->names().prototype) ||
4074 0 : !oldGroup->maybeInterpretedFunction() ||
4075 0 : !obj->as<JSFunction>().needsPrototypeProperty())
4076 : {
4077 : return false;
4078 : }
4079 6 : MOZ_ASSERT(!propShape->configurable());
4080 0 : MOZ_ASSERT(!propShape->enumerable());
4081 : }
4082 :
4083 : // Also watch out for addProperty hooks. Ignore the Array addProperty hook,
4084 : // because it doesn't do anything for non-index properties.
4085 844 : DebugOnly<uint32_t> index;
4086 1688 : MOZ_ASSERT_IF(obj->is<ArrayObject>(), !IdIsIndex(id, &index));
4087 0 : if (!obj->is<ArrayObject>() && obj->getClass()->getAddProperty())
4088 : return false;
4089 :
4090 : // Walk up the object prototype chain and ensure that all prototypes are
4091 : // native, and that all prototypes have no setter defined on the property.
4092 0 : for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) {
4093 0 : if (!proto->isNative())
4094 : return false;
4095 :
4096 : // If prototype defines this property in a non-plain way, don't optimize.
4097 1632 : Shape* protoShape = proto->as<NativeObject>().lookup(cx_, id);
4098 1684 : if (protoShape && !protoShape->hasDefaultSetter())
4099 : return false;
4100 :
4101 : // Otherwise, if there's no such property, watch out for a resolve hook
4102 : // that would need to be invoked and thus prevent inlining of property
4103 : // addition. Allow the JSFunction resolve hook as it only defines plain
4104 : // data properties and we don't need to invoke it for objects on the
4105 : // proto chain.
4106 4896 : if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto) &&
4107 3 : !proto->is<JSFunction>())
4108 : {
4109 : return false;
4110 : }
4111 : }
4112 :
4113 0 : ObjOperandId objId = writer.guardIsObject(objValId);
4114 0 : maybeEmitIdGuard(id);
4115 :
4116 : // In addition to guarding for type barrier, we need this group guard (or
4117 : // shape guard below) to ensure class is unchanged.
4118 2496 : MOZ_ASSERT(!oldGroup->hasUncacheableClass() || obj->is<ShapedObject>());
4119 1664 : writer.guardGroupForTypeBarrier(objId, oldGroup);
4120 :
4121 : // If we are adding a property to an object for which the new script
4122 : // properties analysis hasn't been performed yet, make sure the stub fails
4123 : // after we run the analysis as a group change may be required here. The
4124 : // group change is not required for correctness but improves type
4125 : // information elsewhere.
4126 1664 : AutoSweepObjectGroup sweep(oldGroup);
4127 0 : if (oldGroup->newScript(sweep) && !oldGroup->newScript(sweep)->analyzed()) {
4128 562 : writer.guardGroupHasUnanalyzedNewScript(oldGroup);
4129 281 : MOZ_ASSERT(IsPreliminaryObject(obj));
4130 281 : preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
4131 : } else {
4132 0 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
4133 : }
4134 :
4135 : // Shape guard the holder.
4136 0 : ObjOperandId holderId = objId;
4137 1664 : if (!obj->isNative()) {
4138 0 : MOZ_ASSERT(obj->as<UnboxedPlainObject>().maybeExpando());
4139 0 : holderId = writer.guardAndLoadUnboxedExpando(objId);
4140 : }
4141 832 : writer.guardShape(holderId, oldShape);
4142 :
4143 832 : ShapeGuardProtoChain(writer, obj, objId);
4144 :
4145 832 : ObjectGroup* newGroup = obj->group();
4146 :
4147 : // Check if we have to change the object's group. If we're adding an
4148 : // unboxed expando property, we pass the expando object to AddAndStore*Slot.
4149 : // That's okay because we only have to do a group change if the object is a
4150 : // PlainObject.
4151 0 : bool changeGroup = oldGroup != newGroup;
4152 0 : MOZ_ASSERT_IF(changeGroup, obj->is<PlainObject>());
4153 :
4154 1664 : if (holderOrExpando->isFixedSlot(propShape->slot())) {
4155 0 : size_t offset = NativeObject::getFixedSlotOffset(propShape->slot());
4156 0 : writer.addAndStoreFixedSlot(holderId, offset, rhsValId, propShape,
4157 0 : changeGroup, newGroup);
4158 0 : trackAttached("AddSlot");
4159 : } else {
4160 0 : size_t offset = holderOrExpando->dynamicSlotIndex(propShape->slot()) * sizeof(Value);
4161 0 : uint32_t numOldSlots = NativeObject::dynamicSlotsCount(oldShape);
4162 452 : uint32_t numNewSlots = NativeObject::dynamicSlotsCount(propShape);
4163 0 : if (numOldSlots == numNewSlots) {
4164 0 : writer.addAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
4165 0 : changeGroup, newGroup);
4166 0 : trackAttached("AddSlot");
4167 : } else {
4168 118 : MOZ_ASSERT(numNewSlots > numOldSlots);
4169 0 : writer.allocateAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
4170 118 : changeGroup, newGroup, numNewSlots);
4171 0 : trackAttached("AllocateSlot");
4172 : }
4173 : }
4174 1664 : writer.returnFromIC();
4175 :
4176 0 : typeCheckInfo_.set(oldGroup, id);
4177 : return true;
4178 : }
4179 :
4180 0 : InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
4181 589 : ICState::Mode mode, HandleValue lhs, HandleObject rhs)
4182 : : IRGenerator(cx, script, pc, CacheKind::InstanceOf, mode),
4183 : lhsVal_(lhs),
4184 1179 : rhsObj_(rhs)
4185 0 : { }
4186 :
4187 : bool
4188 590 : InstanceOfIRGenerator::tryAttachStub()
4189 : {
4190 0 : MOZ_ASSERT(cacheKind_ == CacheKind::InstanceOf);
4191 0 : AutoAssertNoPendingException aanpe(cx_);
4192 :
4193 : // Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared to handle.
4194 0 : if (!rhsObj_->is<JSFunction>()) {
4195 0 : trackAttached(IRGenerator::NotAttached);
4196 0 : return false;
4197 : }
4198 :
4199 590 : HandleFunction fun = rhsObj_.as<JSFunction>();
4200 :
4201 1180 : if (fun->isBoundFunction()) {
4202 0 : trackAttached(IRGenerator::NotAttached);
4203 0 : return false;
4204 : }
4205 :
4206 : // If the user has supplied their own @@hasInstance method we shouldn't
4207 : // clobber it.
4208 1770 : if (!js::FunctionHasDefaultHasInstance(fun, cx_->wellKnownSymbols())) {
4209 0 : trackAttached(IRGenerator::NotAttached);
4210 0 : return false;
4211 : }
4212 :
4213 : // Refuse to optimize any function whose [[Prototype]] isn't
4214 : // Function.prototype.
4215 0 : if (!fun->hasStaticPrototype() || fun->hasUncacheableProto()) {
4216 0 : trackAttached(IRGenerator::NotAttached);
4217 0 : return false;
4218 : }
4219 :
4220 1180 : Value funProto = cx_->global()->getPrototype(JSProto_Function);
4221 1180 : if (!funProto.isObject() || fun->staticPrototype() != &funProto.toObject()) {
4222 0 : trackAttached(IRGenerator::NotAttached);
4223 0 : return false;
4224 : }
4225 :
4226 : // Ensure that the function's prototype slot is the same.
4227 84 : Shape* shape = fun->lookupPure(cx_->names().prototype);
4228 0 : if (!shape || !shape->isDataProperty()) {
4229 0 : trackAttached(IRGenerator::NotAttached);
4230 0 : return false;
4231 : }
4232 :
4233 1 : uint32_t slot = shape->slot();
4234 :
4235 42 : MOZ_ASSERT(fun->numFixedSlots() == 0, "Stub code relies on this");
4236 0 : if (!fun->getSlot(slot).isObject()) {
4237 0 : trackAttached(IRGenerator::NotAttached);
4238 0 : return false;
4239 : }
4240 :
4241 21 : JSObject* prototypeObject = &fun->getSlot(slot).toObject();
4242 :
4243 : // Abstract Objects
4244 42 : ValOperandId lhs(writer.setInputOperandId(0));
4245 42 : ValOperandId rhs(writer.setInputOperandId(1));
4246 :
4247 21 : ObjOperandId rhsId = writer.guardIsObject(rhs);
4248 0 : writer.guardShape(rhsId, fun->lastProperty());
4249 :
4250 : // Load prototypeObject into the cache -- consumed twice in the IC
4251 21 : ObjOperandId protoId = writer.loadObject(prototypeObject);
4252 : // Ensure that rhs[slot] == prototypeObject.
4253 0 : writer.guardFunctionPrototype(rhsId, slot, protoId);
4254 :
4255 : // Needn't guard LHS is object, because the actual stub can handle that
4256 : // and correctly return false.
4257 21 : writer.loadInstanceOfObjectResult(lhs, protoId, slot);
4258 42 : writer.returnFromIC();
4259 0 : trackAttached("InstanceOf");
4260 21 : return true;
4261 : }
4262 :
4263 : void
4264 1 : InstanceOfIRGenerator::trackAttached(const char* name)
4265 : {
4266 : #ifdef JS_CACHEIR_SPEW
4267 0 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4268 0 : sp.valueProperty("lhs", lhsVal_);
4269 0 : sp.valueProperty("rhs", ObjectValue(*rhsObj_));
4270 : }
4271 : #endif
4272 0 : }
4273 :
4274 162 : TypeOfIRGenerator::TypeOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
4275 162 : ICState::Mode mode, HandleValue value)
4276 : : IRGenerator(cx, script, pc, CacheKind::TypeOf, mode),
4277 324 : val_(value)
4278 0 : { }
4279 :
4280 : bool
4281 162 : TypeOfIRGenerator::tryAttachStub()
4282 : {
4283 162 : MOZ_ASSERT(cacheKind_ == CacheKind::TypeOf);
4284 :
4285 486 : AutoAssertNoPendingException aanpe(cx_);
4286 :
4287 0 : ValOperandId valId(writer.setInputOperandId(0));
4288 :
4289 162 : if (tryAttachPrimitive(valId))
4290 : return true;
4291 :
4292 0 : MOZ_ALWAYS_TRUE(tryAttachObject(valId));
4293 : return true;
4294 : }
4295 :
4296 : bool
4297 0 : TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId)
4298 : {
4299 324 : if (!val_.isPrimitive())
4300 : return false;
4301 :
4302 0 : if (val_.isNumber())
4303 0 : writer.guardIsNumber(valId);
4304 : else
4305 0 : writer.guardType(valId, val_.extractNonDoubleType());
4306 :
4307 585 : writer.loadStringResult(TypeName(js::TypeOfValue(val_), cx_->names()));
4308 234 : writer.returnFromIC();
4309 :
4310 117 : return true;
4311 : }
4312 :
4313 : bool
4314 0 : TypeOfIRGenerator::tryAttachObject(ValOperandId valId)
4315 : {
4316 0 : if (!val_.isObject())
4317 : return false;
4318 :
4319 45 : ObjOperandId objId = writer.guardIsObject(valId);
4320 45 : writer.loadTypeOfObjectResult(objId);
4321 0 : writer.returnFromIC();
4322 :
4323 45 : return true;
4324 : }
4325 :
4326 83 : GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
4327 83 : ICState::Mode mode, HandleValue value)
4328 : : IRGenerator(cx, script, pc, CacheKind::GetIterator, mode),
4329 166 : val_(value)
4330 0 : { }
4331 :
4332 : bool
4333 83 : GetIteratorIRGenerator::tryAttachStub()
4334 : {
4335 83 : MOZ_ASSERT(cacheKind_ == CacheKind::GetIterator);
4336 :
4337 0 : AutoAssertNoPendingException aanpe(cx_);
4338 :
4339 83 : if (mode_ == ICState::Mode::Megamorphic)
4340 : return false;
4341 :
4342 162 : ValOperandId valId(writer.setInputOperandId(0));
4343 0 : if (!val_.isObject())
4344 : return false;
4345 :
4346 243 : RootedObject obj(cx_, &val_.toObject());
4347 :
4348 81 : ObjOperandId objId = writer.guardIsObject(valId);
4349 81 : if (tryAttachNativeIterator(objId, obj))
4350 : return true;
4351 :
4352 53 : return false;
4353 : }
4354 :
4355 : bool
4356 0 : GetIteratorIRGenerator::tryAttachNativeIterator(ObjOperandId objId, HandleObject obj)
4357 : {
4358 81 : MOZ_ASSERT(JSOp(*pc_) == JSOP_ITER);
4359 :
4360 81 : PropertyIteratorObject* iterobj = LookupInIteratorCache(cx_, obj);
4361 81 : if (!iterobj)
4362 : return false;
4363 :
4364 56 : MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
4365 :
4366 : // Guard on the receiver's shape/group.
4367 0 : Maybe<ObjOperandId> expandoId;
4368 0 : TestMatchingReceiver(writer, obj, objId, &expandoId);
4369 :
4370 : // Ensure the receiver or its expando object has no dense elements.
4371 56 : if (obj->isNative())
4372 0 : writer.guardNoDenseElements(objId);
4373 0 : else if (expandoId)
4374 0 : writer.guardNoDenseElements(*expandoId);
4375 :
4376 : // Do the same for the objects on the proto chain.
4377 0 : GeneratePrototypeHoleGuards(writer, obj, objId);
4378 :
4379 : ObjOperandId iterId =
4380 56 : writer.guardAndGetIterator(objId, iterobj, &ObjectRealm::get(obj).enumerators);
4381 28 : writer.loadObjectResult(iterId);
4382 0 : writer.returnFromIC();
4383 :
4384 0 : return true;
4385 : }
4386 :
4387 3834 : CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, JSOp op,
4388 : ICState::Mode mode, uint32_t argc,
4389 3834 : HandleValue callee, HandleValue thisval, HandleValueArray args)
4390 : : IRGenerator(cx, script, pc, CacheKind::Call, mode),
4391 : op_(op),
4392 : argc_(argc),
4393 : callee_(callee),
4394 : thisval_(thisval),
4395 : args_(args),
4396 : typeCheckInfo_(cx, /* needsTypeBarrier = */ true),
4397 11502 : cacheIRStubKind_(BaselineCacheIRStubKind::Regular)
4398 3834 : { }
4399 :
4400 : bool
4401 3 : CallIRGenerator::tryAttachStringSplit()
4402 : {
4403 : // Only optimize StringSplitString(str, str)
4404 9 : if (argc_ != 2 || !args_[0].isString() || !args_[1].isString())
4405 : return false;
4406 :
4407 : // Just for now: if they're both atoms, then do not optimize using
4408 : // CacheIR and allow the legacy "ConstStringSplit" BaselineIC optimization
4409 : // to proceed.
4410 0 : if (args_[0].toString()->isAtom() && args_[1].toString()->isAtom())
4411 : return false;
4412 :
4413 : // Get the object group to use for this location.
4414 0 : RootedObjectGroup group(cx_, ObjectGroupRealm::getStringSplitStringGroup(cx_));
4415 2 : if (!group)
4416 : return false;
4417 :
4418 6 : AutoAssertNoPendingException aanpe(cx_);
4419 4 : Int32OperandId argcId(writer.setInputOperandId(0));
4420 :
4421 : // Ensure argc == 1.
4422 2 : writer.guardSpecificInt32Immediate(argcId, 2);
4423 :
4424 : // 2 arguments. Stack-layout here is (bottom to top):
4425 : //
4426 : // 3: Callee
4427 : // 2: ThisValue
4428 : // 1: Arg0
4429 : // 0: Arg1 <-- Top of stack
4430 :
4431 : // Ensure callee is the |String_split| native function.
4432 0 : ValOperandId calleeValId = writer.loadStackValue(3);
4433 0 : ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
4434 2 : writer.guardIsNativeFunction(calleeObjId, js::intrinsic_StringSplitString);
4435 :
4436 : // Ensure arg0 is a string.
4437 0 : ValOperandId arg0ValId = writer.loadStackValue(1);
4438 2 : StringOperandId arg0StrId = writer.guardIsString(arg0ValId);
4439 :
4440 : // Ensure arg1 is a string.
4441 0 : ValOperandId arg1ValId = writer.loadStackValue(0);
4442 2 : StringOperandId arg1StrId = writer.guardIsString(arg1ValId);
4443 :
4444 : // Call custom string splitter VM-function.
4445 2 : writer.callStringSplitResult(arg0StrId, arg1StrId, group);
4446 0 : writer.typeMonitorResult();
4447 :
4448 2 : cacheIRStubKind_ = BaselineCacheIRStubKind::Monitored;
4449 2 : trackAttached("StringSplitString");
4450 :
4451 8 : TypeScript::Monitor(cx_, script_, pc_, TypeSet::ObjectType(group));
4452 :
4453 : return true;
4454 : }
4455 :
4456 : bool
4457 57 : CallIRGenerator::tryAttachArrayPush()
4458 : {
4459 : // Only optimize on obj.push(val);
4460 0 : if (argc_ != 1 || !thisval_.isObject())
4461 : return false;
4462 :
4463 : // Where |obj| is a native array.
4464 171 : RootedObject thisobj(cx_, &thisval_.toObject());
4465 114 : if (!thisobj->is<ArrayObject>())
4466 : return false;
4467 :
4468 114 : RootedArrayObject thisarray(cx_, &thisobj->as<ArrayObject>());
4469 :
4470 : // And the object group for the array is not collecting preliminary objects.
4471 0 : AutoSweepObjectGroup sweep(thisobj->group());
4472 57 : if (thisobj->group()->maybePreliminaryObjects(sweep))
4473 : return false;
4474 :
4475 : // Check for other indexed properties or class hooks.
4476 57 : if (!CanAttachAddElement(thisarray, /* isInit = */ false))
4477 : return false;
4478 :
4479 : // Can't add new elements to arrays with non-writable length.
4480 114 : if (!thisarray->lengthIsWritable())
4481 : return false;
4482 :
4483 : // Check that array is extensible.
4484 0 : if (!thisarray->isExtensible())
4485 : return false;
4486 :
4487 114 : MOZ_ASSERT(!thisarray->getElementsHeader()->isFrozen(),
4488 : "Extensible arrays should not have frozen elements");
4489 0 : MOZ_ASSERT(thisarray->lengthIsWritable());
4490 :
4491 : // After this point, we can generate code fine.
4492 :
4493 : // Generate code.
4494 171 : AutoAssertNoPendingException aanpe(cx_);
4495 114 : Int32OperandId argcId(writer.setInputOperandId(0));
4496 :
4497 : // Ensure argc == 1.
4498 57 : writer.guardSpecificInt32Immediate(argcId, 1);
4499 :
4500 : // 1 argument only. Stack-layout here is (bottom to top):
4501 : //
4502 : // 2: Callee
4503 : // 1: ThisValue
4504 : // 0: Arg0 <-- Top of stack.
4505 :
4506 : // Guard callee is the |js::array_push| native function.
4507 0 : ValOperandId calleeValId = writer.loadStackValue(2);
4508 0 : ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
4509 57 : writer.guardIsNativeFunction(calleeObjId, js::array_push);
4510 :
4511 : // Guard this is an array object.
4512 57 : ValOperandId thisValId = writer.loadStackValue(1);
4513 0 : ObjOperandId thisObjId = writer.guardIsObject(thisValId);
4514 :
4515 : // This is a soft assert, documenting the fact that we pass 'true'
4516 : // for needsTypeBarrier when constructing typeCheckInfo_ for CallIRGenerator.
4517 : // Can be removed safely if the assumption becomes false.
4518 0 : MOZ_ASSERT(typeCheckInfo_.needsTypeBarrier());
4519 :
4520 : // Guard that the group and shape matches.
4521 0 : if (typeCheckInfo_.needsTypeBarrier())
4522 57 : writer.guardGroupForTypeBarrier(thisObjId, thisobj->group());
4523 114 : TestMatchingNativeReceiver(writer, thisarray, thisObjId);
4524 :
4525 : // Guard proto chain shapes.
4526 57 : ShapeGuardProtoChain(writer, thisobj, thisObjId);
4527 :
4528 : // arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
4529 57 : ValOperandId argId = writer.loadStackValue(0);
4530 0 : writer.arrayPush(thisObjId, argId);
4531 :
4532 0 : writer.returnFromIC();
4533 :
4534 : // Set the type-check info, and the stub kind to Updated
4535 57 : typeCheckInfo_.set(thisobj->group(), JSID_VOID);
4536 :
4537 57 : cacheIRStubKind_ = BaselineCacheIRStubKind::Updated;
4538 :
4539 0 : trackAttached("ArrayPush");
4540 : return true;
4541 : }
4542 :
4543 : bool
4544 16 : CallIRGenerator::tryAttachArrayJoin()
4545 : {
4546 : // Only handle argc <= 1.
4547 16 : if (argc_ > 1)
4548 : return false;
4549 :
4550 : // Only optimize on obj.join(...);
4551 0 : if (!thisval_.isObject())
4552 : return false;
4553 :
4554 : // Where |obj| is a native array.
4555 48 : RootedObject thisobj(cx_, &thisval_.toObject());
4556 32 : if (!thisobj->is<ArrayObject>())
4557 : return false;
4558 :
4559 32 : RootedArrayObject thisarray(cx_, &thisobj->as<ArrayObject>());
4560 :
4561 : // And the array is of length 0 or 1.
4562 32 : if (thisarray->length() > 1)
4563 : return false;
4564 :
4565 : // And the array is packed.
4566 20 : if (thisarray->getDenseInitializedLength() != thisarray->length())
4567 : return false;
4568 :
4569 : // We don't need to worry about indexed properties because we can perform
4570 : // hole check manually.
4571 :
4572 : // Generate code.
4573 15 : AutoAssertNoPendingException aanpe(cx_);
4574 10 : Int32OperandId argcId(writer.setInputOperandId(0));
4575 :
4576 : // if 0 arguments:
4577 : // 1: Callee
4578 : // 0: ThisValue <-- Top of stack.
4579 : //
4580 : // if 1 argument:
4581 : // 2: Callee
4582 : // 1: ThisValue
4583 : // 0: Arg0 [optional] <-- Top of stack.
4584 :
4585 : // Guard callee is the |js::array_join| native function.
4586 0 : uint32_t calleeIndex = (argc_ == 0) ? 1 : 2;
4587 5 : ValOperandId calleeValId = writer.loadStackValue(calleeIndex);
4588 0 : ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
4589 0 : writer.guardIsNativeFunction(calleeObjId, js::array_join);
4590 :
4591 5 : if (argc_ == 1) {
4592 : // If argcount is 1, guard that the argument is a string.
4593 0 : ValOperandId argValId = writer.loadStackValue(0);
4594 0 : writer.guardIsString(argValId);
4595 : }
4596 :
4597 : // Guard this is an array object.
4598 5 : uint32_t thisIndex = (argc_ == 0) ? 0 : 1;
4599 0 : ValOperandId thisValId = writer.loadStackValue(thisIndex);
4600 5 : ObjOperandId thisObjId = writer.guardIsObject(thisValId);
4601 0 : writer.guardClass(thisObjId, GuardClassKind::Array);
4602 :
4603 : // Do the join.
4604 5 : writer.arrayJoinResult(thisObjId);
4605 :
4606 10 : writer.returnFromIC();
4607 :
4608 : // The result of this stub does not need to be monitored because it will
4609 : // always return a string. We will add String to the stack typeset when
4610 : // attaching this stub.
4611 :
4612 : // Set the stub kind to Regular
4613 5 : cacheIRStubKind_ = BaselineCacheIRStubKind::Regular;
4614 :
4615 0 : trackAttached("ArrayJoin");
4616 : return true;
4617 : }
4618 :
4619 : bool
4620 3834 : CallIRGenerator::tryAttachStub()
4621 : {
4622 : // Only optimize on JSOP_CALL or JSOP_CALL_IGNORES_RV. No fancy business for now.
4623 3834 : if ((op_ != JSOP_CALL) && (op_ != JSOP_CALL_IGNORES_RV))
4624 : return false;
4625 :
4626 : // Only optimize when the mode is Specialized.
4627 3017 : if (mode_ != ICState::Mode::Specialized)
4628 : return false;
4629 :
4630 : // Ensure callee is a function.
4631 11420 : if (!callee_.isObject() || !callee_.toObject().is<JSFunction>())
4632 : return false;
4633 :
4634 0 : RootedFunction calleeFunc(cx_, &callee_.toObject().as<JSFunction>());
4635 :
4636 : // Check for native-function optimizations.
4637 4932 : if (calleeFunc->isNative()) {
4638 0 : if (calleeFunc->native() == js::intrinsic_StringSplitString) {
4639 0 : if (tryAttachStringSplit())
4640 : return true;
4641 : }
4642 :
4643 0 : if (calleeFunc->native() == js::array_push) {
4644 0 : if (tryAttachArrayPush())
4645 : return true;
4646 : }
4647 :
4648 1029 : if (calleeFunc->native() == js::array_join) {
4649 16 : if (tryAttachArrayJoin())
4650 : return true;
4651 : }
4652 : }
4653 :
4654 : return false;
4655 : }
4656 :
4657 : void
4658 1 : CallIRGenerator::trackAttached(const char* name)
4659 : {
4660 : #ifdef JS_CACHEIR_SPEW
4661 192 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4662 0 : sp.valueProperty("callee", callee_);
4663 0 : sp.valueProperty("thisval", thisval_);
4664 0 : sp.valueProperty("argc", Int32Value(argc_));
4665 : }
4666 : #endif
4667 64 : }
4668 :
4669 : // Class which holds a shape pointer for use when caches might reference data in other zones.
4670 : static const Class shapeContainerClass = {
4671 : "ShapeContainer",
4672 : JSCLASS_HAS_RESERVED_SLOTS(1)
4673 : };
4674 :
4675 : static const size_t SHAPE_CONTAINER_SLOT = 0;
4676 :
4677 : JSObject*
4678 0 : jit::NewWrapperWithObjectShape(JSContext* cx, HandleNativeObject obj)
4679 : {
4680 0 : MOZ_ASSERT(cx->compartment() != obj->compartment());
4681 :
4682 0 : RootedObject wrapper(cx);
4683 : {
4684 0 : AutoRealm ar(cx, obj);
4685 0 : wrapper = NewObjectWithClassProto(cx, &shapeContainerClass, nullptr);
4686 0 : if (!obj)
4687 0 : return nullptr;
4688 0 : wrapper->as<NativeObject>().setSlot(SHAPE_CONTAINER_SLOT, PrivateGCThingValue(obj->lastProperty()));
4689 : }
4690 0 : if (!JS_WrapObject(cx, &wrapper))
4691 : return nullptr;
4692 0 : MOZ_ASSERT(IsWrapper(wrapper));
4693 0 : return wrapper;
4694 : }
4695 :
4696 : void
4697 0 : jit::LoadShapeWrapperContents(MacroAssembler& masm, Register obj, Register dst, Label* failure)
4698 : {
4699 0 : masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), dst);
4700 0 : Address privateAddr(dst, detail::ProxyReservedSlots::offsetOfPrivateSlot());
4701 0 : masm.branchTestObject(Assembler::NotEqual, privateAddr, failure);
4702 0 : masm.unboxObject(privateAddr, dst);
4703 0 : masm.unboxNonDouble(Address(dst, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT)), dst,
4704 0 : JSVAL_TYPE_PRIVATE_GCTHING);
4705 0 : }
4706 :
4707 0 : CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
4708 : ICState::Mode mode, JSOp op,
4709 876 : HandleValue lhsVal, HandleValue rhsVal)
4710 : : IRGenerator(cx, script, pc, CacheKind::Compare, mode),
4711 1752 : op_(op), lhsVal_(lhsVal), rhsVal_(rhsVal)
4712 0 : { }
4713 :
4714 : bool
4715 744 : CompareIRGenerator::tryAttachString(ValOperandId lhsId, ValOperandId rhsId)
4716 : {
4717 0 : MOZ_ASSERT(IsEqualityOp(op_));
4718 :
4719 0 : if (!lhsVal_.isString() || !rhsVal_.isString())
4720 : return false;
4721 :
4722 0 : StringOperandId lhsStrId = writer.guardIsString(lhsId);
4723 0 : StringOperandId rhsStrId = writer.guardIsString(rhsId);
4724 267 : writer.compareStringResult(op_, lhsStrId, rhsStrId);
4725 534 : writer.returnFromIC();
4726 :
4727 0 : trackAttached("String");
4728 267 : return true;
4729 : }
4730 :
4731 : bool
4732 477 : CompareIRGenerator::tryAttachObject(ValOperandId lhsId, ValOperandId rhsId)
4733 : {
4734 0 : MOZ_ASSERT(IsEqualityOp(op_));
4735 :
4736 0 : if (!lhsVal_.isObject() || !rhsVal_.isObject())
4737 : return false;
4738 :
4739 0 : ObjOperandId lhsObjId = writer.guardIsObject(lhsId);
4740 0 : ObjOperandId rhsObjId = writer.guardIsObject(rhsId);
4741 14 : writer.compareObjectResult(op_, lhsObjId, rhsObjId);
4742 28 : writer.returnFromIC();
4743 :
4744 0 : trackAttached("Object");
4745 14 : return true;
4746 : }
4747 :
4748 : bool
4749 463 : CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId)
4750 : {
4751 0 : MOZ_ASSERT(IsEqualityOp(op_));
4752 :
4753 0 : if (!lhsVal_.isSymbol() || !rhsVal_.isSymbol())
4754 : return false;
4755 :
4756 0 : SymbolOperandId lhsSymId = writer.guardIsSymbol(lhsId);
4757 0 : SymbolOperandId rhsSymId = writer.guardIsSymbol(rhsId);
4758 0 : writer.compareSymbolResult(op_, lhsSymId, rhsSymId);
4759 0 : writer.returnFromIC();
4760 :
4761 0 : trackAttached("Symbol");
4762 0 : return true;
4763 : }
4764 :
4765 : bool
4766 463 : CompareIRGenerator::tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperandId rhsId)
4767 : {
4768 926 : MOZ_ASSERT(IsEqualityOp(op_));
4769 :
4770 463 : if (op_ != JSOP_STRICTEQ && op_ != JSOP_STRICTNE)
4771 : return false;
4772 :
4773 : // Probably can't hit some of these.
4774 0 : if (SameType(lhsVal_, rhsVal_) || (lhsVal_.isNumber() && rhsVal_.isNumber()))
4775 : return false;
4776 :
4777 : // Compare tags
4778 0 : ValueTagOperandId lhsTypeId = writer.loadValueTag(lhsId);
4779 0 : ValueTagOperandId rhsTypeId = writer.loadValueTag(rhsId);
4780 152 : writer.guardTagNotEqual(lhsTypeId, rhsTypeId);
4781 :
4782 : // Now that we've passed the guard, we know differing types, so return the bool result.
4783 304 : writer.loadBooleanResult(op_ == JSOP_STRICTNE ? true : false);
4784 304 : writer.returnFromIC();
4785 :
4786 0 : trackAttached("StrictDifferentTypes");
4787 152 : return true;
4788 : }
4789 :
4790 : bool
4791 876 : CompareIRGenerator::tryAttachStub()
4792 : {
4793 0 : MOZ_ASSERT(cacheKind_ == CacheKind::Compare);
4794 1752 : MOZ_ASSERT(IsEqualityOp(op_) ||
4795 : op_ == JSOP_LE || op_ == JSOP_LT ||
4796 : op_ == JSOP_GE || op_ == JSOP_GT);
4797 :
4798 0 : AutoAssertNoPendingException aanpe(cx_);
4799 :
4800 1752 : ValOperandId lhsId(writer.setInputOperandId(0));
4801 0 : ValOperandId rhsId(writer.setInputOperandId(1));
4802 :
4803 0 : if (IsEqualityOp(op_)) {
4804 744 : if (tryAttachString(lhsId, rhsId))
4805 : return true;
4806 477 : if (tryAttachObject(lhsId, rhsId))
4807 : return true;
4808 0 : if (tryAttachSymbol(lhsId, rhsId))
4809 : return true;
4810 463 : if (tryAttachStrictDifferentTypes(lhsId, rhsId))
4811 : return true;
4812 :
4813 0 : trackAttached(IRGenerator::NotAttached);
4814 311 : return false;
4815 : }
4816 :
4817 0 : trackAttached(IRGenerator::NotAttached);
4818 132 : return false;
4819 : }
4820 :
4821 : void
4822 1 : CompareIRGenerator::trackAttached(const char* name)
4823 : {
4824 : #ifdef JS_CACHEIR_SPEW
4825 0 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4826 0 : sp.valueProperty("lhs", lhsVal_);
4827 0 : sp.valueProperty("rhs", rhsVal_);
4828 : }
4829 : #endif
4830 0 : }
4831 :
4832 705 : ToBoolIRGenerator::ToBoolIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
4833 705 : HandleValue val)
4834 : : IRGenerator(cx, script, pc, CacheKind::ToBool, mode),
4835 1410 : val_(val)
4836 705 : {}
4837 :
4838 : void
4839 705 : ToBoolIRGenerator::trackAttached(const char* name)
4840 : {
4841 : #ifdef JS_CACHEIR_SPEW
4842 2115 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4843 0 : sp.valueProperty("val", val_);
4844 : }
4845 : #endif
4846 0 : }
4847 :
4848 : bool
4849 705 : ToBoolIRGenerator::tryAttachStub()
4850 : {
4851 2115 : AutoAssertNoPendingException aanpe(cx_);
4852 :
4853 705 : if (tryAttachInt32())
4854 : return true;
4855 639 : if (tryAttachDouble())
4856 : return true;
4857 637 : if (tryAttachString())
4858 : return true;
4859 535 : if (tryAttachNullOrUndefined())
4860 : return true;
4861 1 : if (tryAttachObject())
4862 : return true;
4863 0 : if (tryAttachSymbol())
4864 : return true;
4865 :
4866 0 : trackAttached(IRGenerator::NotAttached);
4867 0 : return false;
4868 : }
4869 :
4870 : bool
4871 0 : ToBoolIRGenerator::tryAttachInt32()
4872 : {
4873 0 : if (!val_.isInt32())
4874 : return false;
4875 :
4876 0 : ValOperandId valId(writer.setInputOperandId(0));
4877 66 : writer.guardType(valId, JSVAL_TYPE_INT32);
4878 66 : writer.loadInt32TruthyResult(valId);
4879 132 : writer.returnFromIC();
4880 0 : trackAttached("ToBoolInt32");
4881 66 : return true;
4882 : }
4883 :
4884 : bool
4885 0 : ToBoolIRGenerator::tryAttachDouble()
4886 : {
4887 0 : if (!val_.isDouble() || !cx_->runtime()->jitSupportsFloatingPoint)
4888 : return false;
4889 :
4890 0 : ValOperandId valId(writer.setInputOperandId(0));
4891 2 : writer.guardType(valId, JSVAL_TYPE_DOUBLE);
4892 2 : writer.loadDoubleTruthyResult(valId);
4893 4 : writer.returnFromIC();
4894 0 : trackAttached("ToBoolDouble");
4895 2 : return true;
4896 : }
4897 :
4898 : bool
4899 0 : ToBoolIRGenerator::tryAttachSymbol()
4900 : {
4901 0 : if (!val_.isSymbol())
4902 : return false;
4903 :
4904 0 : ValOperandId valId(writer.setInputOperandId(0));
4905 0 : writer.guardType(valId, JSVAL_TYPE_SYMBOL);
4906 0 : writer.loadBooleanResult(true);
4907 0 : writer.returnFromIC();
4908 0 : trackAttached("ToBoolSymbol");
4909 0 : return true;
4910 : }
4911 :
4912 : bool
4913 0 : ToBoolIRGenerator::tryAttachString()
4914 : {
4915 0 : if (!val_.isString())
4916 : return false;
4917 :
4918 0 : ValOperandId valId(writer.setInputOperandId(0));
4919 102 : StringOperandId strId = writer.guardIsString(valId);
4920 102 : writer.loadStringTruthyResult(strId);
4921 204 : writer.returnFromIC();
4922 0 : trackAttached("ToBoolString");
4923 102 : return true;
4924 : }
4925 :
4926 : bool
4927 0 : ToBoolIRGenerator::tryAttachNullOrUndefined()
4928 : {
4929 0 : if (!val_.isNullOrUndefined())
4930 : return false;
4931 :
4932 0 : ValOperandId valId(writer.setInputOperandId(0));
4933 319 : writer.guardIsNullOrUndefined(valId);
4934 638 : writer.loadBooleanResult(false);
4935 638 : writer.returnFromIC();
4936 0 : trackAttached("ToBoolNullOrUndefined");
4937 319 : return true;
4938 : }
4939 :
4940 : bool
4941 0 : ToBoolIRGenerator::tryAttachObject()
4942 : {
4943 0 : if (!val_.isObject())
4944 : return false;
4945 :
4946 0 : ValOperandId valId(writer.setInputOperandId(0));
4947 216 : ObjOperandId objId = writer.guardIsObject(valId);
4948 216 : writer.loadObjectTruthyResult(objId);
4949 0 : writer.returnFromIC();
4950 0 : trackAttached("ToBoolObject");
4951 216 : return true;
4952 : }
4953 :
4954 535 : GetIntrinsicIRGenerator::GetIntrinsicIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
4955 535 : HandleValue val)
4956 : : IRGenerator(cx, script, pc, CacheKind::GetIntrinsic, mode)
4957 1070 : , val_(val)
4958 535 : {}
4959 :
4960 : void
4961 535 : GetIntrinsicIRGenerator::trackAttached(const char* name)
4962 : {
4963 : #ifdef JS_CACHEIR_SPEW
4964 1605 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4965 0 : sp.valueProperty("val", val_);
4966 : }
4967 : #endif
4968 0 : }
4969 :
4970 : bool
4971 0 : GetIntrinsicIRGenerator::tryAttachStub()
4972 : {
4973 0 : writer.loadValueResult(val_);
4974 0 : writer.returnFromIC();
4975 535 : trackAttached("GetIntrinsic");
4976 535 : return true;
4977 : }
4978 0 : UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
4979 0 : JSOp op, HandleValue val, HandleValue res)
4980 : : IRGenerator(cx, script, pc, CacheKind::UnaryArith, mode),
4981 : op_(op),
4982 : val_(val),
4983 2 : res_(res)
4984 1 : { }
4985 :
4986 : void
4987 1 : UnaryArithIRGenerator::trackAttached(const char* name)
4988 : {
4989 : #ifdef JS_CACHEIR_SPEW
4990 3 : if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
4991 0 : sp.valueProperty("val", val_);
4992 : }
4993 : #endif
4994 0 : }
4995 :
4996 : bool
4997 1 : UnaryArithIRGenerator::tryAttachStub()
4998 : {
4999 0 : if (tryAttachInt32())
5000 : return true;
5001 1 : if (tryAttachNumber())
5002 : return true;
5003 :
5004 0 : trackAttached(IRGenerator::NotAttached);
5005 0 : return false;
5006 : }
5007 :
5008 : bool
5009 0 : UnaryArithIRGenerator::tryAttachInt32()
5010 : {
5011 0 : if (!val_.isInt32() || !res_.isInt32())
5012 : return false;
5013 :
5014 0 : ValOperandId valId(writer.setInputOperandId(0));
5015 :
5016 0 : Int32OperandId intId = writer.guardIsInt32(valId);
5017 0 : switch (op_) {
5018 : case JSOP_BITNOT:
5019 0 : writer.int32NotResult(intId);
5020 0 : trackAttached("UnaryArith.Int32Not");
5021 0 : break;
5022 : case JSOP_NEG:
5023 0 : writer.int32NegationResult(intId);
5024 0 : trackAttached("UnaryArith.Int32Neg");
5025 0 : break;
5026 : default:
5027 0 : MOZ_CRASH("Unexected OP");
5028 : }
5029 :
5030 0 : writer.returnFromIC();
5031 0 : return true;
5032 : }
5033 :
5034 : bool
5035 0 : UnaryArithIRGenerator::tryAttachNumber()
5036 : {
5037 0 : if (!val_.isNumber() || !res_.isNumber() || !cx_->runtime()->jitSupportsFloatingPoint)
5038 : return false;
5039 :
5040 0 : ValOperandId valId(writer.setInputOperandId(0));
5041 0 : writer.guardType(valId, JSVAL_TYPE_DOUBLE);
5042 0 : Int32OperandId truncatedId;
5043 0 : switch (op_) {
5044 : case JSOP_BITNOT:
5045 0 : truncatedId = writer.truncateDoubleToUInt32(valId);
5046 0 : writer.int32NotResult(truncatedId);
5047 0 : trackAttached("UnaryArith.DoubleNot");
5048 0 : break;
5049 : case JSOP_NEG:
5050 1 : writer.doubleNegationResult(valId);
5051 1 : trackAttached("UnaryArith.DoubleNeg");
5052 0 : break;
5053 : default:
5054 : MOZ_CRASH("Unexpected OP");
5055 : }
5056 :
5057 : writer.returnFromIC();
5058 : return true;
5059 : }
|