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/BaselineIC.h"
13 : #include "jit/CacheIRSpewer.h"
14 : #include "jit/IonCaches.h"
15 :
16 : #include "vm/SelfHosting.h"
17 : #include "jsobjinlines.h"
18 :
19 : #include "vm/EnvironmentObject-inl.h"
20 : #include "vm/UnboxedObject-inl.h"
21 :
22 : using namespace js;
23 : using namespace js::jit;
24 :
25 : using mozilla::DebugOnly;
26 : using mozilla::Maybe;
27 :
28 : const char* js::jit::CacheKindNames[] = {
29 : #define DEFINE_KIND(kind) #kind,
30 : CACHE_IR_KINDS(DEFINE_KIND)
31 : #undef DEFINE_KIND
32 : };
33 :
34 :
35 12114 : IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
36 12114 : ICState::Mode mode)
37 : : writer(cx),
38 : cx_(cx),
39 : script_(script),
40 : pc_(pc),
41 : cacheKind_(cacheKind),
42 12114 : mode_(mode)
43 12114 : {}
44 :
45 4189 : GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
46 : CacheKind cacheKind, ICState::Mode mode,
47 : bool* isTemporarilyUnoptimizable, HandleValue val,
48 : HandleValue idVal, HandleValue receiver,
49 4189 : CanAttachGetter canAttachGetter)
50 : : IRGenerator(cx, script, pc, cacheKind, mode),
51 : val_(val),
52 : idVal_(idVal),
53 : receiver_(receiver),
54 : isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
55 : canAttachGetter_(canAttachGetter),
56 4189 : preliminaryObjectAction_(PreliminaryObjectAction::None)
57 4189 : {}
58 :
59 : static void
60 3671 : EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder,
61 : Shape* shape)
62 : {
63 3671 : if (holder->isFixedSlot(shape->slot())) {
64 1034 : writer.loadFixedSlotResult(holderOp, NativeObject::getFixedSlotOffset(shape->slot()));
65 : } else {
66 2637 : size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
67 2637 : writer.loadDynamicSlotResult(holderOp, dynamicSlotOffset);
68 : }
69 3671 : }
70 :
71 : // DOM proxies
72 : // -----------
73 : //
74 : // DOM proxies are proxies that are used to implement various DOM objects like
75 : // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
76 : // object that stores extra properties added to the object. The following
77 : // CacheIR instructions are only used with DOM proxies:
78 : //
79 : // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
80 : // returns either an UndefinedValue (no expando), ObjectValue (the expando
81 : // object), or PrivateValue(ExpandoAndGeneration*).
82 : //
83 : // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
84 : // slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
85 : // generation, then returns expandoAndGeneration->expando. This Value is
86 : // either an UndefinedValue or ObjectValue.
87 : //
88 : // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
89 : // expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
90 : // returns the expandoAndGeneration->expando Value.
91 : //
92 : // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
93 : // guards it's either UndefinedValue or an object with the expected shape.
94 :
95 : enum class ProxyStubType {
96 : None,
97 : DOMExpando,
98 : DOMShadowed,
99 : DOMUnshadowed,
100 : Generic
101 : };
102 :
103 : static ProxyStubType
104 943 : GetProxyStubType(JSContext* cx, HandleObject obj, HandleId id)
105 : {
106 943 : if (!obj->is<ProxyObject>())
107 810 : return ProxyStubType::None;
108 :
109 133 : if (!IsCacheableDOMProxy(obj))
110 127 : return ProxyStubType::Generic;
111 :
112 6 : DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
113 6 : if (shadows == ShadowCheckFailed) {
114 0 : cx->clearPendingException();
115 0 : return ProxyStubType::None;
116 : }
117 :
118 6 : if (DOMProxyIsShadowing(shadows)) {
119 0 : if (shadows == ShadowsViaDirectExpando || shadows == ShadowsViaIndirectExpando)
120 0 : return ProxyStubType::DOMExpando;
121 0 : return ProxyStubType::DOMShadowed;
122 : }
123 :
124 6 : MOZ_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
125 6 : return ProxyStubType::DOMUnshadowed;
126 : }
127 :
128 : bool
129 4189 : GetPropIRGenerator::tryAttachStub()
130 : {
131 : // Idempotent ICs should call tryAttachIdempotentStub instead.
132 4189 : MOZ_ASSERT(!idempotent());
133 :
134 8378 : AutoAssertNoPendingException aanpe(cx_);
135 :
136 : // Non-object receivers are a degenerate case, so don't try to attach
137 : // stubs. The stubs we do emit will still perform runtime checks and
138 : // fallback as needed.
139 4189 : if (isSuper() && !receiver_.isObject())
140 0 : return false;
141 :
142 4189 : ValOperandId valId(writer.setInputOperandId(0));
143 4189 : if (cacheKind_ != CacheKind::GetProp) {
144 801 : MOZ_ASSERT_IF(cacheKind_ == CacheKind::GetPropSuper, getSuperReceiverValueId().id() == 1);
145 801 : MOZ_ASSERT_IF(cacheKind_ != CacheKind::GetPropSuper, getElemKeyValueId().id() == 1);
146 801 : writer.setInputOperandId(1);
147 : }
148 4189 : if (cacheKind_ == CacheKind::GetElemSuper) {
149 0 : MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
150 0 : writer.setInputOperandId(2);
151 : }
152 :
153 8378 : RootedId id(cx_);
154 : bool nameOrSymbol;
155 4189 : if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
156 0 : cx_->clearPendingException();
157 0 : return false;
158 : }
159 :
160 4189 : if (val_.isObject()) {
161 8158 : RootedObject obj(cx_, &val_.toObject());
162 4079 : ObjOperandId objId = writer.guardIsObject(valId);
163 4079 : if (nameOrSymbol) {
164 3898 : if (tryAttachObjectLength(obj, objId, id))
165 100 : return true;
166 3798 : if (tryAttachNative(obj, objId, id))
167 2798 : return true;
168 1000 : if (tryAttachUnboxed(obj, objId, id))
169 8 : return true;
170 992 : if (tryAttachUnboxedExpando(obj, objId, id))
171 0 : return true;
172 992 : if (tryAttachTypedObject(obj, objId, id))
173 0 : return true;
174 992 : if (tryAttachModuleNamespace(obj, objId, id))
175 0 : return true;
176 992 : if (tryAttachWindowProxy(obj, objId, id))
177 0 : return true;
178 992 : if (tryAttachCrossCompartmentWrapper(obj, objId, id))
179 544 : return true;
180 448 : if (tryAttachFunction(obj, objId, id))
181 0 : return true;
182 448 : if (tryAttachProxy(obj, objId, id))
183 117 : return true;
184 :
185 331 : trackNotAttached();
186 331 : return false;
187 : }
188 :
189 181 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
190 :
191 181 : if (tryAttachProxyElement(obj, objId))
192 12 : return true;
193 :
194 : uint32_t index;
195 169 : Int32OperandId indexId;
196 169 : if (maybeGuardInt32Index(idVal_, getElemKeyValueId(), &index, &indexId)) {
197 147 : if (tryAttachTypedElement(obj, objId, index, indexId))
198 0 : return true;
199 147 : if (tryAttachDenseElement(obj, objId, index, indexId))
200 145 : return true;
201 2 : if (tryAttachDenseElementHole(obj, objId, index, indexId))
202 2 : return true;
203 0 : if (tryAttachUnboxedArrayElement(obj, objId, index, indexId))
204 0 : return true;
205 0 : if (tryAttachArgumentsObjectArg(obj, objId, index, indexId))
206 0 : return true;
207 :
208 0 : trackNotAttached();
209 0 : return false;
210 : }
211 :
212 22 : trackNotAttached();
213 22 : return false;
214 : }
215 :
216 110 : if (nameOrSymbol) {
217 89 : if (tryAttachPrimitive(valId, id))
218 37 : return true;
219 52 : if (tryAttachStringLength(valId, id))
220 13 : return true;
221 39 : if (tryAttachMagicArgumentsName(valId, id))
222 38 : return true;
223 :
224 1 : trackNotAttached();
225 1 : return false;
226 : }
227 :
228 21 : if (idVal_.isInt32()) {
229 21 : ValOperandId indexId = getElemKeyValueId();
230 21 : if (tryAttachStringChar(valId, indexId))
231 3 : return true;
232 18 : if (tryAttachMagicArgument(valId, indexId))
233 16 : return true;
234 :
235 2 : trackNotAttached();
236 2 : return false;
237 : }
238 :
239 0 : trackNotAttached();
240 0 : return false;
241 : }
242 :
243 : bool
244 0 : GetPropIRGenerator::tryAttachIdempotentStub()
245 : {
246 : // For idempotent ICs, only attach stubs for plain data properties.
247 : // This ensures (1) the lookup has no side-effects and (2) Ion has complete
248 : // static type information and we don't have to monitor the result. Because
249 : // of (2), we don't support for instance missing properties or array
250 : // lengths, as TI does not account for these cases.
251 :
252 0 : MOZ_ASSERT(idempotent());
253 :
254 0 : RootedObject obj(cx_, &val_.toObject());
255 0 : RootedId id(cx_, NameToId(idVal_.toString()->asAtom().asPropertyName()));
256 :
257 0 : ValOperandId valId(writer.setInputOperandId(0));
258 0 : ObjOperandId objId = writer.guardIsObject(valId);
259 0 : if (tryAttachNative(obj, objId, id))
260 0 : return true;
261 :
262 : // Also support native data properties on DOMProxy prototypes.
263 0 : if (GetProxyStubType(cx_, obj, id) == ProxyStubType::DOMUnshadowed)
264 0 : return tryAttachDOMProxyUnshadowed(obj, objId, id);
265 :
266 0 : return false;
267 : }
268 :
269 : static bool
270 431 : IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
271 : jsbytecode* pc)
272 : {
273 431 : if (shape)
274 230 : return false;
275 :
276 201 : MOZ_ASSERT(!holder);
277 :
278 201 : if (!pc) {
279 : // This is an idempotent IC, don't attach a missing-property stub.
280 : // See tryAttachStub.
281 0 : return false;
282 : }
283 :
284 : // If we're doing a name lookup, we have to throw a ReferenceError. If
285 : // extra warnings are enabled, we may have to report a warning.
286 201 : if (*pc == JSOP_GETBOUNDNAME || cx->compartment()->behaviors().extraWarnings(cx))
287 0 : return false;
288 :
289 201 : return CheckHasNoSuchProperty(cx, obj, id);
290 : }
291 :
292 : enum NativeGetPropCacheability {
293 : CanAttachNone,
294 : CanAttachReadSlot,
295 : CanAttachCallGetter,
296 : };
297 :
298 : static NativeGetPropCacheability
299 4396 : CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id,
300 : MutableHandleNativeObject holder, MutableHandleShape shape,
301 : jsbytecode* pc, CanAttachGetter canAttachGetter,
302 : bool* isTemporarilyUnoptimizable)
303 : {
304 4396 : MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
305 :
306 : // The lookup needs to be universally pure, otherwise we risk calling hooks out
307 : // of turn. We don't mind doing this even when purity isn't required, because we
308 : // only miss out on shape hashification, which is only a temporary perf cost.
309 : // The limits were arbitrarily set, anyways.
310 4396 : JSObject* baseHolder = nullptr;
311 4396 : PropertyResult prop;
312 4396 : if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop))
313 947 : return CanAttachNone;
314 :
315 3449 : MOZ_ASSERT(!holder);
316 3449 : if (baseHolder) {
317 3248 : if (!baseHolder->isNative())
318 8 : return CanAttachNone;
319 3240 : holder.set(&baseHolder->as<NativeObject>());
320 : }
321 3441 : shape.set(prop.maybeShape());
322 :
323 3441 : if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop))
324 3010 : return CanAttachReadSlot;
325 :
326 : // Idempotent ICs only support plain data properties, see
327 : // tryAttachIdempotentStub.
328 431 : if (!pc)
329 0 : return CanAttachNone;
330 :
331 431 : if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc))
332 201 : return CanAttachReadSlot;
333 :
334 230 : if (canAttachGetter == CanAttachGetter::No)
335 0 : return CanAttachNone;
336 :
337 230 : if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable))
338 66 : return CanAttachCallGetter;
339 :
340 164 : if (IsCacheableGetPropCallNative(obj, holder, shape))
341 110 : return CanAttachCallGetter;
342 :
343 54 : return CanAttachNone;
344 : }
345 :
346 : static void
347 994 : GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId objId)
348 : {
349 : // The guards here protect against the effects of JSObject::swap(). If the
350 : // prototype chain is directly altered, then TI will toss the jitcode, so we
351 : // don't have to worry about it, and any other change to the holder, or
352 : // adding a shadowing property will result in reshaping the holder, and thus
353 : // the failure of the shape guard.
354 994 : MOZ_ASSERT(obj != holder);
355 :
356 994 : if (obj->hasUncacheableProto()) {
357 : // If the shape does not imply the proto, emit an explicit proto guard.
358 88 : writer.guardProto(objId, obj->staticPrototype());
359 : }
360 :
361 994 : JSObject* pobj = obj->staticPrototype();
362 994 : if (!pobj)
363 4 : return;
364 :
365 600 : while (pobj != holder) {
366 600 : if (pobj->hasUncacheableProto()) {
367 0 : ObjOperandId protoId = writer.loadObject(pobj);
368 0 : if (pobj->isSingleton()) {
369 : // Singletons can have their group's |proto| mutated directly.
370 0 : writer.guardProto(protoId, pobj->staticPrototype());
371 : } else {
372 0 : writer.guardGroup(protoId, pobj->group());
373 : }
374 : }
375 600 : pobj = pobj->staticPrototype();
376 : }
377 : }
378 :
379 : static void
380 2 : GeneratePrototypeHoleGuards(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
381 : {
382 2 : if (obj->hasUncacheableProto()) {
383 : // If the shape does not imply the proto, emit an explicit proto guard.
384 0 : writer.guardProto(objId, obj->staticPrototype());
385 : }
386 :
387 2 : JSObject* pobj = obj->staticPrototype();
388 6 : while (pobj) {
389 2 : ObjOperandId protoId = writer.loadObject(pobj);
390 :
391 : // Non-singletons with uncacheable protos can change their proto
392 : // without a shape change, so also guard on the group (which determines
393 : // the proto) in this case.
394 2 : if (pobj->hasUncacheableProto() && !pobj->isSingleton())
395 0 : writer.guardGroup(protoId, pobj->group());
396 :
397 : // Make sure the shape matches, to avoid non-dense elements or anything
398 : // else that is being checked by CanAttachDenseElementHole.
399 2 : writer.guardShape(protoId, pobj->as<NativeObject>().lastProperty());
400 :
401 : // Also make sure there are no dense elements.
402 2 : writer.guardNoDenseElements(protoId);
403 :
404 2 : pobj = pobj->staticPrototype();
405 : }
406 2 : }
407 :
408 : static void
409 3434 : TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId,
410 : Maybe<ObjOperandId>* expandoId)
411 : {
412 3434 : if (obj->is<UnboxedPlainObject>()) {
413 0 : writer.guardGroup(objId, obj->group());
414 :
415 0 : if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
416 0 : expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId));
417 0 : writer.guardShape(expandoId->ref(), expando->lastProperty());
418 : } else {
419 0 : writer.guardNoUnboxedExpando(objId);
420 : }
421 3434 : } else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
422 0 : writer.guardGroup(objId, obj->group());
423 : } else {
424 3434 : Shape* shape = obj->maybeShape();
425 3434 : MOZ_ASSERT(shape);
426 3434 : writer.guardShape(objId, shape);
427 : }
428 3434 : }
429 :
430 : static void
431 3273 : EmitReadSlotGuard(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
432 : ObjOperandId objId, Maybe<ObjOperandId>* holderId)
433 : {
434 6546 : Maybe<ObjOperandId> expandoId;
435 3273 : TestMatchingReceiver(writer, obj, objId, &expandoId);
436 :
437 3273 : if (obj != holder) {
438 897 : GeneratePrototypeGuards(writer, obj, holder, objId);
439 :
440 897 : if (holder) {
441 : // Guard on the holder's shape.
442 652 : holderId->emplace(writer.loadObject(holder));
443 652 : writer.guardShape(holderId->ref(), holder->as<NativeObject>().lastProperty());
444 : } else {
445 : // The property does not exist. Guard on everything in the prototype
446 : // chain. This is guaranteed to see only Native objects because of
447 : // CanAttachNativeGetProp().
448 245 : JSObject* proto = obj->taggedProto().toObjectOrNull();
449 245 : ObjOperandId lastObjId = objId;
450 927 : while (proto) {
451 341 : ObjOperandId protoId = writer.loadProto(lastObjId);
452 341 : writer.guardShape(protoId, proto->as<NativeObject>().lastProperty());
453 341 : proto = proto->staticPrototype();
454 341 : lastObjId = protoId;
455 : }
456 : }
457 2376 : } else if (obj->is<UnboxedPlainObject>() && expandoId.isSome()) {
458 0 : holderId->emplace(*expandoId);
459 : } else {
460 2376 : holderId->emplace(objId);
461 : }
462 3273 : }
463 :
464 : static void
465 3150 : EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
466 : Shape* shape, ObjOperandId objId)
467 : {
468 6300 : Maybe<ObjOperandId> holderId;
469 3150 : EmitReadSlotGuard(writer, obj, holder, objId, &holderId);
470 :
471 3150 : if (obj == holder && obj->is<UnboxedPlainObject>())
472 0 : holder = obj->as<UnboxedPlainObject>().maybeExpando();
473 :
474 : // Slot access.
475 3150 : if (holder) {
476 2957 : MOZ_ASSERT(holderId->valid());
477 2957 : EmitLoadSlotResult(writer, *holderId, &holder->as<NativeObject>(), shape);
478 : } else {
479 193 : MOZ_ASSERT(holderId.isNothing());
480 193 : writer.loadUndefinedResult();
481 : }
482 3150 : }
483 :
484 : static void
485 3150 : EmitReadSlotReturn(CacheIRWriter& writer, JSObject*, JSObject* holder, Shape* shape,
486 : bool wrapResult = false)
487 : {
488 : // Slot access.
489 3150 : if (holder) {
490 2957 : MOZ_ASSERT(shape);
491 2957 : if (wrapResult)
492 477 : writer.wrapResult();
493 2957 : writer.typeMonitorResult();
494 : } else {
495 : // Normally for this op, the result would have to be monitored by TI.
496 : // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure
497 : // that undefined is already registered with the type-set, this can be avoided.
498 193 : writer.returnFromIC();
499 : }
500 3150 : }
501 :
502 : static void
503 178 : EmitCallGetterResultNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
504 : Shape* shape, ObjOperandId receiverId)
505 : {
506 178 : if (IsCacheableGetPropCallNative(obj, holder, shape)) {
507 112 : JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
508 112 : MOZ_ASSERT(target->isNative());
509 112 : writer.callNativeGetterResult(receiverId, target);
510 112 : writer.typeMonitorResult();
511 112 : return;
512 : }
513 :
514 66 : MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
515 :
516 66 : JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
517 66 : MOZ_ASSERT(target->hasJITCode());
518 66 : writer.callScriptedGetterResult(receiverId, target);
519 66 : writer.typeMonitorResult();
520 : }
521 :
522 : static void
523 168 : EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape,
524 : ObjOperandId objId, ObjOperandId receiverId, ICState::Mode mode)
525 : {
526 : // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
527 : // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
528 : // require outerizing).
529 168 : if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
530 304 : Maybe<ObjOperandId> expandoId;
531 152 : TestMatchingReceiver(writer, obj, objId, &expandoId);
532 :
533 152 : if (obj != holder) {
534 82 : GeneratePrototypeGuards(writer, obj, holder, objId);
535 :
536 : // Guard on the holder's shape.
537 82 : ObjOperandId holderId = writer.loadObject(holder);
538 82 : writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
539 : }
540 : } else {
541 16 : writer.guardHasGetterSetter(objId, shape);
542 : }
543 :
544 168 : EmitCallGetterResultNoGuards(writer, obj, holder, shape, receiverId);
545 168 : }
546 :
547 : static void
548 0 : EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
549 : Shape* shape, ObjOperandId objId, ICState::Mode mode)
550 : {
551 0 : EmitCallGetterResult(writer, obj, holder, shape, objId, objId, mode);
552 0 : }
553 :
554 : void
555 61 : GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing)
556 : {
557 61 : MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
558 :
559 : // The stub handles the missing-properties case only if we're seeing one
560 : // now, to make sure Ion ICs correctly monitor the undefined type.
561 :
562 61 : if (cacheKind_ == CacheKind::GetProp || cacheKind_ == CacheKind::GetPropSuper) {
563 28 : writer.megamorphicLoadSlotResult(objId, JSID_TO_ATOM(id)->asPropertyName(),
564 28 : handleMissing);
565 : } else {
566 33 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
567 33 : writer.megamorphicLoadSlotByValueResult(objId, getElemKeyValueId(), handleMissing);
568 : }
569 61 : writer.typeMonitorResult();
570 :
571 61 : trackAttached(handleMissing ? "MegamorphicMissingNativeSlot" : "MegamorphicNativeSlot");
572 61 : }
573 :
574 : bool
575 3798 : GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id)
576 : {
577 7596 : RootedShape shape(cx_);
578 7596 : RootedNativeObject holder(cx_);
579 :
580 7596 : NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_,
581 : canAttachGetter_,
582 3798 : isTemporarilyUnoptimizable_);
583 3798 : MOZ_ASSERT_IF(idempotent(),
584 : type == CanAttachNone || (type == CanAttachReadSlot && holder));
585 3798 : switch (type) {
586 : case CanAttachNone:
587 1000 : return false;
588 : case CanAttachReadSlot:
589 2630 : if (mode_ == ICState::Mode::Megamorphic) {
590 61 : attachMegamorphicNativeSlot(objId, id, holder == nullptr);
591 61 : return true;
592 : }
593 :
594 2569 : maybeEmitIdGuard(id);
595 2569 : if (holder) {
596 2443 : EnsureTrackPropertyTypes(cx_, holder, id);
597 2443 : if (obj == holder) {
598 : // See the comment in StripPreliminaryObjectStubs.
599 1859 : if (IsPreliminaryObject(obj))
600 372 : preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
601 : else
602 1487 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
603 : }
604 : }
605 2569 : EmitReadSlotResult(writer, obj, holder, shape, objId);
606 2569 : EmitReadSlotReturn(writer, obj, holder, shape);
607 :
608 2569 : trackAttached("NativeSlot");
609 2569 : return true;
610 : case CanAttachCallGetter: {
611 : // |super.prop| accesses use a |this| value that differs from lookup object
612 168 : ObjOperandId receiverId = isSuper() ? writer.guardIsObject(getSuperReceiverValueId())
613 168 : : objId;
614 168 : maybeEmitIdGuard(id);
615 168 : EmitCallGetterResult(writer, obj, holder, shape, objId, receiverId, mode_);
616 :
617 168 : trackAttached("NativeGetter");
618 168 : return true;
619 : }
620 : }
621 :
622 0 : MOZ_CRASH("Bad NativeGetPropCacheability");
623 : }
624 :
625 : bool
626 992 : GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id)
627 : {
628 : // Attach a stub when the receiver is a WindowProxy and we can do the lookup
629 : // on the Window (the global object).
630 :
631 992 : if (!IsWindowProxy(obj))
632 992 : return false;
633 :
634 : // If we're megamorphic prefer a generic proxy stub that handles a lot more
635 : // cases.
636 0 : if (mode_ == ICState::Mode::Megamorphic)
637 0 : return false;
638 :
639 : // This must be a WindowProxy for the current Window/global. Else it would
640 : // be a cross-compartment wrapper and IsWindowProxy returns false for
641 : // those.
642 0 : MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
643 0 : MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
644 :
645 : // Now try to do the lookup on the Window (the current global).
646 0 : HandleObject windowObj = cx_->global();
647 0 : RootedShape shape(cx_);
648 0 : RootedNativeObject holder(cx_);
649 0 : NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_,
650 : canAttachGetter_,
651 0 : isTemporarilyUnoptimizable_);
652 0 : switch (type) {
653 : case CanAttachNone:
654 0 : return false;
655 :
656 : case CanAttachReadSlot: {
657 0 : maybeEmitIdGuard(id);
658 0 : writer.guardClass(objId, GuardClassKind::WindowProxy);
659 :
660 0 : ObjOperandId windowObjId = writer.loadObject(windowObj);
661 0 : EmitReadSlotResult(writer, windowObj, holder, shape, windowObjId);
662 0 : EmitReadSlotReturn(writer, windowObj, holder, shape);
663 :
664 0 : trackAttached("WindowProxySlot");
665 0 : return true;
666 : }
667 :
668 : case CanAttachCallGetter: {
669 0 : if (!IsCacheableGetPropCallNative(windowObj, holder, shape))
670 0 : return false;
671 :
672 : // Make sure the native getter is okay with the IC passing the Window
673 : // instead of the WindowProxy as |this| value.
674 0 : JSFunction* callee = &shape->getterObject()->as<JSFunction>();
675 0 : MOZ_ASSERT(callee->isNative());
676 0 : if (!callee->jitInfo() || callee->jitInfo()->needsOuterizedThisObject())
677 0 : return false;
678 :
679 : // If a |super| access, it is not worth the complexity to attach an IC.
680 0 : if (isSuper())
681 0 : return false;
682 :
683 : // Guard the incoming object is a WindowProxy and inline a getter call based
684 : // on the Window object.
685 0 : maybeEmitIdGuard(id);
686 0 : writer.guardClass(objId, GuardClassKind::WindowProxy);
687 0 : ObjOperandId windowObjId = writer.loadObject(windowObj);
688 0 : EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId, mode_);
689 :
690 0 : trackAttached("WindowProxyGetter");
691 0 : return true;
692 : }
693 : }
694 :
695 0 : MOZ_CRASH("Unreachable");
696 : }
697 :
698 : bool
699 992 : GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId,
700 : HandleId id)
701 : {
702 : // We can only optimize this very wrapper-handler, because others might
703 : // have a security policy.
704 992 : if (!IsWrapper(obj) || Wrapper::wrapperHandler(obj) != &CrossCompartmentWrapper::singleton)
705 339 : return false;
706 :
707 : // If we're megamorphic prefer a generic proxy stub that handles a lot more
708 : // cases.
709 653 : if (mode_ == ICState::Mode::Megamorphic)
710 31 : return false;
711 :
712 1244 : RootedObject unwrapped(cx_, Wrapper::wrappedObject(obj));
713 622 : MOZ_ASSERT(unwrapped == UnwrapOneChecked(obj));
714 :
715 : // If we allowed different zones we would have to wrap strings.
716 622 : if (unwrapped->compartment()->zone() != cx_->compartment()->zone())
717 66 : return false;
718 :
719 1112 : RootedObject wrappedGlobal(cx_, &obj->global());
720 556 : if (!cx_->compartment()->wrap(cx_, &wrappedGlobal))
721 0 : return false;
722 :
723 1112 : AutoCompartment ac(cx_, unwrapped);
724 :
725 : // The first CCW for iframes is almost always wrapping another WindowProxy
726 : // so we optimize for that case as well.
727 556 : bool isWindowProxy = IsWindowProxy(unwrapped);
728 556 : if (isWindowProxy) {
729 0 : MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == unwrapped->compartment()->maybeGlobal());
730 0 : unwrapped = cx_->global();
731 0 : MOZ_ASSERT(unwrapped);
732 : }
733 :
734 1112 : RootedShape shape(cx_);
735 1112 : RootedNativeObject holder(cx_);
736 : NativeGetPropCacheability canCache =
737 1112 : CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_, canAttachGetter_,
738 556 : isTemporarilyUnoptimizable_);
739 556 : if (canCache != CanAttachReadSlot)
740 12 : return false;
741 :
742 544 : if (holder) {
743 477 : EnsureTrackPropertyTypes(cx_, holder, id);
744 477 : if (unwrapped == holder) {
745 : // See the comment in StripPreliminaryObjectStubs.
746 410 : if (IsPreliminaryObject(unwrapped))
747 64 : preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
748 : else
749 346 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
750 : }
751 : }
752 :
753 544 : maybeEmitIdGuard(id);
754 544 : writer.guardIsProxy(objId);
755 544 : writer.guardIsCrossCompartmentWrapper(objId);
756 :
757 : // Load the object wrapped by the CCW
758 544 : ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
759 :
760 : // If the compartment of the wrapped object is different we should fail.
761 544 : writer.guardCompartment(wrapperTargetId, wrappedGlobal, unwrapped->compartment());
762 :
763 544 : ObjOperandId unwrappedId = wrapperTargetId;
764 544 : if (isWindowProxy) {
765 : // For the WindowProxy case also unwrap the inner window.
766 : // We avoid loadObject, because storing cross compartment objects in
767 : // stubs / JIT code is tricky.
768 0 : writer.guardClass(wrapperTargetId, GuardClassKind::WindowProxy);
769 0 : unwrappedId = writer.loadWrapperTarget(wrapperTargetId);
770 : }
771 :
772 544 : EmitReadSlotResult(writer, unwrapped, holder, shape, unwrappedId);
773 544 : EmitReadSlotReturn(writer, unwrapped, holder, shape, /* wrapResult = */ true);
774 :
775 544 : trackAttached("CCWSlot");
776 544 : return true;
777 : }
778 :
779 : bool
780 112 : GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
781 : bool handleDOMProxies)
782 : {
783 112 : MOZ_ASSERT(obj->is<ProxyObject>());
784 :
785 112 : writer.guardIsProxy(objId);
786 :
787 112 : if (!handleDOMProxies) {
788 : // Ensure that the incoming object is not a DOM proxy, so that we can get to
789 : // the specialized stubs
790 81 : writer.guardNotDOMProxy(objId);
791 : }
792 :
793 112 : if (cacheKind_ == CacheKind::GetProp || mode_ == ICState::Mode::Specialized) {
794 104 : MOZ_ASSERT(!isSuper());
795 104 : maybeEmitIdGuard(id);
796 104 : writer.callProxyGetResult(objId, id);
797 : } else {
798 : // Attach a stub that handles every id.
799 8 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
800 8 : MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
801 8 : MOZ_ASSERT(!isSuper());
802 8 : writer.callProxyGetByValueResult(objId, getElemKeyValueId());
803 : }
804 :
805 112 : writer.typeMonitorResult();
806 :
807 112 : trackAttached("GenericProxy");
808 112 : return true;
809 : }
810 :
811 : ObjOperandId
812 0 : IRGenerator::guardDOMProxyExpandoObjectAndShape(JSObject* obj, ObjOperandId objId,
813 : const Value& expandoVal, JSObject* expandoObj)
814 : {
815 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
816 :
817 0 : writer.guardShape(objId, obj->maybeShape());
818 :
819 : // Shape determines Class, so now it must be a DOM proxy.
820 0 : ValOperandId expandoValId;
821 0 : if (expandoVal.isObject())
822 0 : expandoValId = writer.loadDOMExpandoValue(objId);
823 : else
824 0 : expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
825 :
826 : // Guard the expando is an object and shape guard.
827 0 : ObjOperandId expandoObjId = writer.guardIsObject(expandoValId);
828 0 : writer.guardShape(expandoObjId, expandoObj->as<NativeObject>().shape());
829 0 : return expandoObjId;
830 : }
831 :
832 : bool
833 0 : GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id)
834 : {
835 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
836 :
837 0 : RootedValue expandoVal(cx_, GetProxyPrivate(obj));
838 0 : RootedObject expandoObj(cx_);
839 0 : if (expandoVal.isObject()) {
840 0 : expandoObj = &expandoVal.toObject();
841 : } else {
842 0 : MOZ_ASSERT(!expandoVal.isUndefined(),
843 : "How did a missing expando manage to shadow things?");
844 0 : auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
845 0 : MOZ_ASSERT(expandoAndGeneration);
846 0 : expandoObj = &expandoAndGeneration->expando.toObject();
847 : }
848 :
849 : // Try to do the lookup on the expando object.
850 0 : RootedNativeObject holder(cx_);
851 0 : RootedShape propShape(cx_);
852 : NativeGetPropCacheability canCache =
853 0 : CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &propShape, pc_,
854 0 : canAttachGetter_, isTemporarilyUnoptimizable_);
855 0 : if (canCache != CanAttachReadSlot && canCache != CanAttachCallGetter)
856 0 : return false;
857 0 : if (!holder)
858 0 : return false;
859 :
860 0 : MOZ_ASSERT(holder == expandoObj);
861 :
862 0 : maybeEmitIdGuard(id);
863 : ObjOperandId expandoObjId =
864 0 : guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
865 :
866 0 : if (canCache == CanAttachReadSlot) {
867 : // Load from the expando's slots.
868 0 : EmitLoadSlotResult(writer, expandoObjId, &expandoObj->as<NativeObject>(), propShape);
869 0 : writer.typeMonitorResult();
870 : } else {
871 : // Call the getter. Note that we pass objId, the DOM proxy, as |this|
872 : // and not the expando object.
873 0 : MOZ_ASSERT(canCache == CanAttachCallGetter);
874 0 : EmitCallGetterResultNoGuards(writer, expandoObj, expandoObj, propShape, objId);
875 : }
876 :
877 0 : trackAttached("DOMProxyExpando");
878 0 : return true;
879 : }
880 :
881 : bool
882 0 : GetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id)
883 : {
884 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
885 :
886 0 : maybeEmitIdGuard(id);
887 0 : writer.guardShape(objId, obj->maybeShape());
888 :
889 : // No need for more guards: we know this is a DOM proxy, since the shape
890 : // guard enforces a given JSClass, so just go ahead and emit the call to
891 : // ProxyGet.
892 0 : MOZ_ASSERT(!isSuper());
893 0 : writer.callProxyGetResult(objId, id);
894 0 : writer.typeMonitorResult();
895 :
896 0 : trackAttached("DOMProxyShadowed");
897 0 : return true;
898 : }
899 :
900 : // Callers are expected to have already guarded on the shape of the
901 : // object, which guarantees the object is a DOM proxy.
902 : static void
903 6 : CheckDOMProxyExpandoDoesNotShadow(CacheIRWriter& writer, JSObject* obj, jsid id,
904 : ObjOperandId objId)
905 : {
906 6 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
907 :
908 6 : Value expandoVal = GetProxyPrivate(obj);
909 :
910 6 : ValOperandId expandoId;
911 6 : if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
912 0 : auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
913 0 : expandoId = writer.loadDOMExpandoValueGuardGeneration(objId, expandoAndGeneration);
914 0 : expandoVal = expandoAndGeneration->expando;
915 : } else {
916 6 : expandoId = writer.loadDOMExpandoValue(objId);
917 : }
918 :
919 6 : if (expandoVal.isUndefined()) {
920 : // Guard there's no expando object.
921 6 : writer.guardType(expandoId, JSVAL_TYPE_UNDEFINED);
922 0 : } else if (expandoVal.isObject()) {
923 : // Guard the proxy either has no expando object or, if it has one, that
924 : // the shape matches the current expando object.
925 0 : NativeObject& expandoObj = expandoVal.toObject().as<NativeObject>();
926 0 : MOZ_ASSERT(!expandoObj.containsPure(id));
927 0 : writer.guardDOMExpandoMissingOrGuardShape(expandoId, expandoObj.lastProperty());
928 : } else {
929 0 : MOZ_CRASH("Invalid expando value");
930 : }
931 6 : }
932 :
933 : bool
934 5 : GetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id)
935 : {
936 5 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
937 :
938 10 : RootedObject checkObj(cx_, obj->staticPrototype());
939 5 : if (!checkObj)
940 0 : return false;
941 :
942 10 : RootedNativeObject holder(cx_);
943 10 : RootedShape shape(cx_);
944 10 : NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape,
945 : pc_, canAttachGetter_,
946 5 : isTemporarilyUnoptimizable_);
947 5 : MOZ_ASSERT_IF(idempotent(),
948 : canCache == CanAttachNone || (canCache == CanAttachReadSlot && holder));
949 5 : if (canCache == CanAttachNone)
950 0 : return false;
951 :
952 5 : maybeEmitIdGuard(id);
953 5 : writer.guardShape(objId, obj->maybeShape());
954 :
955 : // Guard that our expando object hasn't started shadowing this property.
956 5 : CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
957 :
958 5 : if (holder) {
959 : // Found the property on the prototype chain. Treat it like a native
960 : // getprop.
961 5 : GeneratePrototypeGuards(writer, obj, holder, objId);
962 :
963 : // Guard on the holder of the property.
964 5 : ObjOperandId holderId = writer.loadObject(holder);
965 5 : writer.guardShape(holderId, holder->lastProperty());
966 :
967 5 : if (canCache == CanAttachReadSlot) {
968 0 : EmitLoadSlotResult(writer, holderId, holder, shape);
969 0 : writer.typeMonitorResult();
970 : } else {
971 : // EmitCallGetterResultNoGuards expects |obj| to be the object the
972 : // property is on to do some checks. Since we actually looked at
973 : // checkObj, and no extra guards will be generated, we can just
974 : // pass that instead.
975 5 : MOZ_ASSERT(canCache == CanAttachCallGetter);
976 5 : MOZ_ASSERT(!isSuper());
977 5 : EmitCallGetterResultNoGuards(writer, checkObj, holder, shape, objId);
978 : }
979 : } else {
980 : // Property was not found on the prototype chain. Deoptimize down to
981 : // proxy get call.
982 0 : MOZ_ASSERT(!isSuper());
983 0 : writer.callProxyGetResult(objId, id);
984 0 : writer.typeMonitorResult();
985 : }
986 :
987 5 : trackAttached("DOMProxyUnshadowed");
988 5 : return true;
989 : }
990 :
991 : bool
992 448 : GetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id)
993 : {
994 448 : ProxyStubType type = GetProxyStubType(cx_, obj, id);
995 448 : if (type == ProxyStubType::None)
996 331 : return false;
997 :
998 : // The proxy stubs don't currently support |super| access.
999 117 : if (isSuper())
1000 0 : return false;
1001 :
1002 117 : if (mode_ == ICState::Mode::Megamorphic)
1003 31 : return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
1004 :
1005 86 : switch (type) {
1006 : case ProxyStubType::None:
1007 0 : break;
1008 : case ProxyStubType::DOMExpando:
1009 0 : if (tryAttachDOMProxyExpando(obj, objId, id))
1010 0 : return true;
1011 0 : if (*isTemporarilyUnoptimizable_) {
1012 : // Scripted getter without JIT code. Just wait.
1013 0 : return false;
1014 : }
1015 : MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
1016 : case ProxyStubType::DOMShadowed:
1017 0 : return tryAttachDOMProxyShadowed(obj, objId, id);
1018 : case ProxyStubType::DOMUnshadowed:
1019 5 : if (tryAttachDOMProxyUnshadowed(obj, objId, id))
1020 5 : return true;
1021 0 : if (*isTemporarilyUnoptimizable_) {
1022 : // Scripted getter without JIT code. Just wait.
1023 0 : return false;
1024 : }
1025 0 : return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
1026 : case ProxyStubType::Generic:
1027 81 : return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ false);
1028 : }
1029 :
1030 0 : MOZ_CRASH("Unexpected ProxyStubType");
1031 : }
1032 :
1033 : bool
1034 1000 : GetPropIRGenerator::tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id)
1035 : {
1036 1000 : if (!obj->is<UnboxedPlainObject>())
1037 992 : return false;
1038 :
1039 8 : const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
1040 8 : if (!property)
1041 0 : return false;
1042 :
1043 8 : if (!cx_->runtime()->jitSupportsFloatingPoint)
1044 0 : return false;
1045 :
1046 8 : maybeEmitIdGuard(id);
1047 8 : writer.guardGroup(objId, obj->group());
1048 8 : writer.loadUnboxedPropertyResult(objId, property->type,
1049 16 : UnboxedPlainObject::offsetOfData() + property->offset);
1050 8 : if (property->type == JSVAL_TYPE_OBJECT)
1051 3 : writer.typeMonitorResult();
1052 : else
1053 5 : writer.returnFromIC();
1054 :
1055 8 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
1056 :
1057 8 : trackAttached("Unboxed");
1058 8 : return true;
1059 : }
1060 :
1061 : bool
1062 992 : GetPropIRGenerator::tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id)
1063 : {
1064 992 : if (!obj->is<UnboxedPlainObject>())
1065 992 : return false;
1066 :
1067 0 : UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
1068 0 : if (!expando)
1069 0 : return false;
1070 :
1071 0 : Shape* shape = expando->lookup(cx_, id);
1072 0 : if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
1073 0 : return false;
1074 :
1075 0 : maybeEmitIdGuard(id);
1076 0 : EmitReadSlotResult(writer, obj, obj, shape, objId);
1077 0 : EmitReadSlotReturn(writer, obj, obj, shape);
1078 :
1079 0 : trackAttached("UnboxedExpando");
1080 0 : return true;
1081 : }
1082 :
1083 : bool
1084 992 : GetPropIRGenerator::tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id)
1085 : {
1086 1984 : if (!obj->is<TypedObject>() ||
1087 992 : !cx_->runtime()->jitSupportsFloatingPoint ||
1088 0 : cx_->compartment()->detachedTypedObjects)
1089 : {
1090 992 : return false;
1091 : }
1092 :
1093 0 : TypedObject* typedObj = &obj->as<TypedObject>();
1094 0 : if (!typedObj->typeDescr().is<StructTypeDescr>())
1095 0 : return false;
1096 :
1097 0 : StructTypeDescr* structDescr = &typedObj->typeDescr().as<StructTypeDescr>();
1098 : size_t fieldIndex;
1099 0 : if (!structDescr->fieldIndex(id, &fieldIndex))
1100 0 : return false;
1101 :
1102 0 : TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
1103 0 : if (!fieldDescr->is<SimpleTypeDescr>())
1104 0 : return false;
1105 :
1106 0 : Shape* shape = typedObj->maybeShape();
1107 0 : TypedThingLayout layout = GetTypedThingLayout(shape->getObjectClass());
1108 :
1109 0 : uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
1110 0 : uint32_t typeDescr = SimpleTypeDescrKey(&fieldDescr->as<SimpleTypeDescr>());
1111 :
1112 0 : maybeEmitIdGuard(id);
1113 0 : writer.guardNoDetachedTypedObjects();
1114 0 : writer.guardShape(objId, shape);
1115 0 : writer.loadTypedObjectResult(objId, fieldOffset, layout, typeDescr);
1116 :
1117 : // Only monitor the result if the type produced by this stub might vary.
1118 0 : bool monitorLoad = false;
1119 0 : if (SimpleTypeDescrKeyIsScalar(typeDescr)) {
1120 0 : Scalar::Type type = ScalarTypeFromSimpleTypeDescrKey(typeDescr);
1121 0 : monitorLoad = type == Scalar::Uint32;
1122 : } else {
1123 0 : ReferenceTypeDescr::Type type = ReferenceTypeFromSimpleTypeDescrKey(typeDescr);
1124 0 : monitorLoad = type != ReferenceTypeDescr::TYPE_STRING;
1125 : }
1126 :
1127 0 : if (monitorLoad)
1128 0 : writer.typeMonitorResult();
1129 : else
1130 0 : writer.returnFromIC();
1131 :
1132 0 : trackAttached("TypedObject");
1133 0 : return true;
1134 : }
1135 :
1136 : bool
1137 3898 : GetPropIRGenerator::tryAttachObjectLength(HandleObject obj, ObjOperandId objId, HandleId id)
1138 : {
1139 3898 : if (!JSID_IS_ATOM(id, cx_->names().length))
1140 3786 : return false;
1141 :
1142 112 : if (obj->is<ArrayObject>()) {
1143 : // Make sure int32 is added to the TypeSet before we attach a stub, so
1144 : // the stub can return int32 values without monitoring the result.
1145 99 : if (obj->as<ArrayObject>().length() > INT32_MAX)
1146 0 : return false;
1147 :
1148 99 : maybeEmitIdGuard(id);
1149 99 : writer.guardClass(objId, GuardClassKind::Array);
1150 99 : writer.loadInt32ArrayLengthResult(objId);
1151 99 : writer.returnFromIC();
1152 :
1153 99 : trackAttached("ArrayLength");
1154 99 : return true;
1155 : }
1156 :
1157 13 : if (obj->is<UnboxedArrayObject>()) {
1158 0 : maybeEmitIdGuard(id);
1159 0 : writer.guardClass(objId, GuardClassKind::UnboxedArray);
1160 0 : writer.loadUnboxedArrayLengthResult(objId);
1161 0 : writer.returnFromIC();
1162 :
1163 0 : trackAttached("UnboxedArrayLength");
1164 0 : return true;
1165 : }
1166 :
1167 13 : if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
1168 1 : maybeEmitIdGuard(id);
1169 1 : if (obj->is<MappedArgumentsObject>()) {
1170 0 : writer.guardClass(objId, GuardClassKind::MappedArguments);
1171 : } else {
1172 1 : MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
1173 1 : writer.guardClass(objId, GuardClassKind::UnmappedArguments);
1174 : }
1175 1 : writer.loadArgumentsObjectLengthResult(objId);
1176 1 : writer.returnFromIC();
1177 :
1178 1 : trackAttached("ArgumentsObjectLength");
1179 1 : return true;
1180 : }
1181 :
1182 12 : return false;
1183 : }
1184 :
1185 : bool
1186 448 : GetPropIRGenerator::tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id)
1187 : {
1188 : // Function properties are lazily resolved so they might not be defined yet.
1189 : // And we might end up in a situation where we always have a fresh function
1190 : // object during the IC generation.
1191 448 : if (!obj->is<JSFunction>())
1192 440 : return false;
1193 :
1194 8 : JSObject* holder = nullptr;
1195 8 : PropertyResult prop;
1196 : // This property exists already, don't attach the stub.
1197 8 : if (LookupPropertyPure(cx_, obj, id, &holder, &prop))
1198 7 : return false;
1199 :
1200 1 : JSFunction* fun = &obj->as<JSFunction>();
1201 :
1202 1 : if (JSID_IS_ATOM(id, cx_->names().length)) {
1203 : // length was probably deleted from the function.
1204 0 : if (fun->hasResolvedLength())
1205 0 : return false;
1206 :
1207 : // Lazy functions don't store the length.
1208 0 : if (fun->isInterpretedLazy())
1209 0 : return false;
1210 :
1211 0 : maybeEmitIdGuard(id);
1212 0 : writer.guardClass(objId, GuardClassKind::JSFunction);
1213 0 : writer.loadFunctionLengthResult(objId);
1214 0 : writer.returnFromIC();
1215 :
1216 0 : trackAttached("FunctionLength");
1217 0 : return true;
1218 : }
1219 :
1220 1 : return false;
1221 : }
1222 :
1223 : bool
1224 992 : GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id)
1225 : {
1226 992 : if (!obj->is<ModuleNamespaceObject>())
1227 992 : return false;
1228 :
1229 0 : Rooted<ModuleNamespaceObject*> ns(cx_, &obj->as<ModuleNamespaceObject>());
1230 0 : RootedModuleEnvironmentObject env(cx_);
1231 0 : RootedShape shape(cx_);
1232 0 : if (!ns->bindings().lookup(id, env.address(), shape.address()))
1233 0 : return false;
1234 :
1235 : // Don't emit a stub until the target binding has been initialized.
1236 0 : if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
1237 0 : return false;
1238 :
1239 0 : if (IsIonEnabled(cx_))
1240 0 : EnsureTrackPropertyTypes(cx_, env, shape->propid());
1241 :
1242 : // Check for the specific namespace object.
1243 0 : maybeEmitIdGuard(id);
1244 0 : writer.guardSpecificObject(objId, ns);
1245 :
1246 0 : ObjOperandId envId = writer.loadObject(env);
1247 0 : EmitLoadSlotResult(writer, envId, env, shape);
1248 0 : writer.typeMonitorResult();
1249 :
1250 0 : trackAttached("ModuleNamespace");
1251 0 : return true;
1252 : }
1253 :
1254 : bool
1255 89 : GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId, HandleId id)
1256 : {
1257 : JSValueType primitiveType;
1258 178 : RootedNativeObject proto(cx_);
1259 89 : if (val_.isString()) {
1260 51 : if (JSID_IS_ATOM(id, cx_->names().length)) {
1261 : // String length is special-cased, see js::GetProperty.
1262 13 : return false;
1263 : }
1264 38 : primitiveType = JSVAL_TYPE_STRING;
1265 38 : proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_String));
1266 38 : } else if (val_.isNumber()) {
1267 0 : primitiveType = JSVAL_TYPE_DOUBLE;
1268 0 : proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Number));
1269 38 : } else if (val_.isBoolean()) {
1270 0 : primitiveType = JSVAL_TYPE_BOOLEAN;
1271 0 : proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Boolean));
1272 38 : } else if (val_.isSymbol()) {
1273 0 : primitiveType = JSVAL_TYPE_SYMBOL;
1274 0 : proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Symbol));
1275 : } else {
1276 38 : MOZ_ASSERT(val_.isNullOrUndefined() || val_.isMagic());
1277 38 : return false;
1278 : }
1279 38 : if (!proto)
1280 1 : return false;
1281 :
1282 74 : RootedShape shape(cx_);
1283 74 : RootedNativeObject holder(cx_);
1284 74 : NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, proto, id, &holder, &shape, pc_,
1285 : canAttachGetter_,
1286 37 : isTemporarilyUnoptimizable_);
1287 37 : if (type != CanAttachReadSlot)
1288 0 : return false;
1289 :
1290 37 : if (holder) {
1291 : // Instantiate this property, for use during Ion compilation.
1292 37 : if (IsIonEnabled(cx_))
1293 37 : EnsureTrackPropertyTypes(cx_, holder, id);
1294 : }
1295 :
1296 37 : writer.guardType(valId, primitiveType);
1297 37 : maybeEmitIdGuard(id);
1298 :
1299 37 : ObjOperandId protoId = writer.loadObject(proto);
1300 37 : EmitReadSlotResult(writer, proto, holder, shape, protoId);
1301 37 : EmitReadSlotReturn(writer, proto, holder, shape);
1302 :
1303 37 : trackAttached("Primitive");
1304 37 : return true;
1305 : }
1306 :
1307 : bool
1308 52 : GetPropIRGenerator::tryAttachStringLength(ValOperandId valId, HandleId id)
1309 : {
1310 52 : if (!val_.isString() || !JSID_IS_ATOM(id, cx_->names().length))
1311 39 : return false;
1312 :
1313 13 : StringOperandId strId = writer.guardIsString(valId);
1314 13 : maybeEmitIdGuard(id);
1315 13 : writer.loadStringLengthResult(strId);
1316 13 : writer.returnFromIC();
1317 :
1318 13 : trackAttached("StringLength");
1319 13 : return true;
1320 : }
1321 :
1322 : bool
1323 21 : GetPropIRGenerator::tryAttachStringChar(ValOperandId valId, ValOperandId indexId)
1324 : {
1325 21 : MOZ_ASSERT(idVal_.isInt32());
1326 :
1327 21 : if (!val_.isString())
1328 16 : return false;
1329 :
1330 5 : int32_t index = idVal_.toInt32();
1331 5 : if (index < 0)
1332 0 : return false;
1333 :
1334 5 : JSString* str = val_.toString();
1335 5 : if (size_t(index) >= str->length())
1336 0 : return false;
1337 :
1338 : // This follows JSString::getChar, otherwise we fail to attach getChar in a lot of cases.
1339 5 : if (str->isRope()) {
1340 0 : JSRope* rope = &str->asRope();
1341 :
1342 : // Make sure the left side contains the index.
1343 0 : if (size_t(index) >= rope->leftChild()->length())
1344 0 : return false;
1345 :
1346 0 : str = rope->leftChild();
1347 : }
1348 :
1349 10 : if (!str->isLinear() ||
1350 5 : str->asLinear().latin1OrTwoByteChar(index) >= StaticStrings::UNIT_STATIC_LIMIT)
1351 : {
1352 2 : return false;
1353 : }
1354 :
1355 3 : StringOperandId strId = writer.guardIsString(valId);
1356 3 : Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
1357 3 : writer.loadStringCharResult(strId, int32IndexId);
1358 3 : writer.returnFromIC();
1359 :
1360 3 : trackAttached("StringChar");
1361 3 : return true;
1362 : }
1363 :
1364 : bool
1365 39 : GetPropIRGenerator::tryAttachMagicArgumentsName(ValOperandId valId, HandleId id)
1366 : {
1367 39 : if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
1368 1 : return false;
1369 :
1370 38 : if (!JSID_IS_ATOM(id, cx_->names().length) && !JSID_IS_ATOM(id, cx_->names().callee))
1371 0 : return false;
1372 :
1373 38 : maybeEmitIdGuard(id);
1374 38 : writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
1375 38 : writer.guardFrameHasNoArgumentsObject();
1376 :
1377 38 : if (JSID_IS_ATOM(id, cx_->names().length)) {
1378 38 : writer.loadFrameNumActualArgsResult();
1379 38 : writer.returnFromIC();
1380 : } else {
1381 0 : MOZ_ASSERT(JSID_IS_ATOM(id, cx_->names().callee));
1382 0 : writer.loadFrameCalleeResult();
1383 0 : writer.typeMonitorResult();
1384 : }
1385 :
1386 38 : trackAttached("MagicArgumentsName");
1387 38 : return true;
1388 : }
1389 :
1390 : bool
1391 18 : GetPropIRGenerator::tryAttachMagicArgument(ValOperandId valId, ValOperandId indexId)
1392 : {
1393 18 : MOZ_ASSERT(idVal_.isInt32());
1394 :
1395 18 : if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
1396 2 : return false;
1397 :
1398 16 : writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
1399 16 : writer.guardFrameHasNoArgumentsObject();
1400 :
1401 16 : Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
1402 16 : writer.loadFrameArgumentResult(int32IndexId);
1403 16 : writer.typeMonitorResult();
1404 :
1405 16 : trackAttached("MagicArgument");
1406 16 : return true;
1407 : }
1408 :
1409 : bool
1410 0 : GetPropIRGenerator::tryAttachArgumentsObjectArg(HandleObject obj, ObjOperandId objId,
1411 : uint32_t index, Int32OperandId indexId)
1412 : {
1413 0 : if (!obj->is<ArgumentsObject>() || obj->as<ArgumentsObject>().hasOverriddenElement())
1414 0 : return false;
1415 :
1416 0 : if (obj->is<MappedArgumentsObject>()) {
1417 0 : writer.guardClass(objId, GuardClassKind::MappedArguments);
1418 : } else {
1419 0 : MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
1420 0 : writer.guardClass(objId, GuardClassKind::UnmappedArguments);
1421 : }
1422 :
1423 0 : writer.loadArgumentsObjectArgResult(objId, indexId);
1424 0 : writer.typeMonitorResult();
1425 :
1426 0 : trackAttached("ArgumentsObjectArg");
1427 0 : return true;
1428 : }
1429 :
1430 : bool
1431 147 : GetPropIRGenerator::tryAttachDenseElement(HandleObject obj, ObjOperandId objId,
1432 : uint32_t index, Int32OperandId indexId)
1433 : {
1434 147 : if (!obj->isNative())
1435 0 : return false;
1436 :
1437 147 : if (!obj->as<NativeObject>().containsDenseElement(index))
1438 2 : return false;
1439 :
1440 145 : writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
1441 145 : writer.loadDenseElementResult(objId, indexId);
1442 145 : writer.typeMonitorResult();
1443 :
1444 145 : trackAttached("DenseElement");
1445 145 : return true;
1446 : }
1447 :
1448 : static bool
1449 4 : CanAttachDenseElementHole(JSObject* obj, bool ownProp)
1450 : {
1451 : // Make sure the objects on the prototype don't have any indexed properties
1452 : // or that such properties can't appear without a shape change.
1453 : // Otherwise returning undefined for holes would obviously be incorrect,
1454 : // because we would have to lookup a property on the prototype instead.
1455 : do {
1456 : // The first two checks are also relevant to the receiver object.
1457 4 : if (obj->isIndexed())
1458 0 : return false;
1459 :
1460 4 : if (ClassCanHaveExtraProperties(obj->getClass()))
1461 0 : return false;
1462 :
1463 : // Don't need to check prototype for OwnProperty checks
1464 4 : if (ownProp)
1465 0 : return true;
1466 :
1467 4 : JSObject* proto = obj->staticPrototype();
1468 4 : if (!proto)
1469 2 : break;
1470 :
1471 2 : if (!proto->isNative())
1472 0 : return false;
1473 :
1474 : // Make sure objects on the prototype don't have dense elements.
1475 2 : if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
1476 0 : return false;
1477 :
1478 2 : obj = proto;
1479 : } while (true);
1480 :
1481 2 : return true;
1482 : }
1483 :
1484 : bool
1485 2 : GetPropIRGenerator::tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId,
1486 : uint32_t index, Int32OperandId indexId)
1487 : {
1488 2 : if (!obj->isNative())
1489 0 : return false;
1490 :
1491 2 : if (obj->as<NativeObject>().containsDenseElement(index))
1492 0 : return false;
1493 :
1494 2 : if (!CanAttachDenseElementHole(obj, false))
1495 0 : return false;
1496 :
1497 : // Guard on the shape, to prevent non-dense elements from appearing.
1498 2 : writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
1499 :
1500 2 : GeneratePrototypeHoleGuards(writer, obj, objId);
1501 2 : writer.loadDenseElementHoleResult(objId, indexId);
1502 2 : writer.typeMonitorResult();
1503 :
1504 2 : trackAttached("DenseElementHole");
1505 2 : return true;
1506 : }
1507 :
1508 : bool
1509 0 : GetPropIRGenerator::tryAttachUnboxedArrayElement(HandleObject obj, ObjOperandId objId,
1510 : uint32_t index, Int32OperandId indexId)
1511 : {
1512 0 : if (!obj->is<UnboxedArrayObject>())
1513 0 : return false;
1514 :
1515 0 : if (index >= obj->as<UnboxedArrayObject>().initializedLength())
1516 0 : return false;
1517 :
1518 0 : writer.guardGroup(objId, obj->group());
1519 :
1520 0 : JSValueType elementType = obj->group()->unboxedLayoutDontCheckGeneration().elementType();
1521 0 : writer.loadUnboxedArrayElementResult(objId, indexId, elementType);
1522 :
1523 : // Only monitor the result if its type might change.
1524 0 : if (elementType == JSVAL_TYPE_OBJECT)
1525 0 : writer.typeMonitorResult();
1526 : else
1527 0 : writer.returnFromIC();
1528 :
1529 0 : trackAttached("UnboxedArrayElement");
1530 0 : return true;
1531 : }
1532 :
1533 : bool
1534 147 : GetPropIRGenerator::tryAttachTypedElement(HandleObject obj, ObjOperandId objId,
1535 : uint32_t index, Int32OperandId indexId)
1536 : {
1537 147 : if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
1538 147 : return false;
1539 :
1540 0 : if (!cx_->runtime()->jitSupportsFloatingPoint && TypedThingRequiresFloatingPoint(obj))
1541 0 : return false;
1542 :
1543 : // Ensure the index is in-bounds so the element type gets monitored.
1544 0 : if (obj->is<TypedArrayObject>() && index >= obj->as<TypedArrayObject>().length())
1545 0 : return false;
1546 :
1547 : // Don't attach typed object stubs if the underlying storage could be
1548 : // detached, as the stub will always bail out.
1549 0 : if (IsPrimitiveArrayTypedObject(obj) && cx_->compartment()->detachedTypedObjects)
1550 0 : return false;
1551 :
1552 0 : TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
1553 0 : if (layout != Layout_TypedArray)
1554 0 : writer.guardNoDetachedTypedObjects();
1555 :
1556 0 : writer.guardShape(objId, obj->as<ShapedObject>().shape());
1557 :
1558 0 : writer.loadTypedElementResult(objId, indexId, layout, TypedThingElementType(obj));
1559 :
1560 : // Reading from Uint32Array may produce an int32 now but a double value
1561 : // later, so ensure we monitor the result.
1562 0 : if (TypedThingElementType(obj) == Scalar::Type::Uint32)
1563 0 : writer.typeMonitorResult();
1564 : else
1565 0 : writer.returnFromIC();
1566 :
1567 0 : trackAttached("TypedElement");
1568 0 : return true;
1569 : }
1570 :
1571 : bool
1572 181 : GetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId)
1573 : {
1574 181 : if (!obj->is<ProxyObject>())
1575 169 : return false;
1576 :
1577 : // The proxy stubs don't currently support |super| access.
1578 12 : if (isSuper())
1579 0 : return false;
1580 :
1581 12 : writer.guardIsProxy(objId);
1582 :
1583 : // We are not guarding against DOM proxies here, because there is no other
1584 : // specialized DOM IC we could attach.
1585 : // We could call maybeEmitIdGuard here and then emit CallProxyGetResult,
1586 : // but for GetElem we prefer to attach a stub that can handle any Value
1587 : // so we don't attach a new stub for every id.
1588 12 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
1589 12 : MOZ_ASSERT(!isSuper());
1590 12 : writer.callProxyGetByValueResult(objId, getElemKeyValueId());
1591 12 : writer.typeMonitorResult();
1592 :
1593 12 : trackAttached("ProxyElement");
1594 12 : return true;
1595 : }
1596 :
1597 : void
1598 3833 : GetPropIRGenerator::trackAttached(const char* name)
1599 : {
1600 : #ifdef JS_CACHEIR_SPEW
1601 3833 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
1602 3833 : if (sp.enabled()) {
1603 0 : LockGuard<Mutex> guard(sp.lock());
1604 0 : sp.beginCache(guard, *this);
1605 0 : sp.valueProperty(guard, "base", val_);
1606 0 : sp.valueProperty(guard, "property", idVal_);
1607 0 : sp.attached(guard, name);
1608 0 : sp.endCache(guard);
1609 : }
1610 : #endif
1611 3833 : }
1612 :
1613 : void
1614 356 : GetPropIRGenerator::trackNotAttached()
1615 : {
1616 : #ifdef JS_CACHEIR_SPEW
1617 356 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
1618 356 : if (sp.enabled()) {
1619 0 : LockGuard<Mutex> guard(sp.lock());
1620 0 : sp.beginCache(guard, *this);
1621 0 : sp.valueProperty(guard, "base", val_);
1622 0 : sp.valueProperty(guard, "property", idVal_);
1623 0 : sp.endCache(guard);
1624 : }
1625 : #endif
1626 356 : }
1627 :
1628 : void
1629 911 : IRGenerator::emitIdGuard(ValOperandId valId, jsid id)
1630 : {
1631 911 : if (JSID_IS_SYMBOL(id)) {
1632 119 : SymbolOperandId symId = writer.guardIsSymbol(valId);
1633 119 : writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
1634 : } else {
1635 792 : MOZ_ASSERT(JSID_IS_ATOM(id));
1636 792 : StringOperandId strId = writer.guardIsString(valId);
1637 792 : writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
1638 : }
1639 911 : }
1640 :
1641 : void
1642 3586 : GetPropIRGenerator::maybeEmitIdGuard(jsid id)
1643 : {
1644 3586 : if (cacheKind_ == CacheKind::GetProp || cacheKind_ == CacheKind::GetPropSuper) {
1645 : // Constant PropertyName, no guards necessary.
1646 3106 : MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
1647 3106 : return;
1648 : }
1649 :
1650 480 : MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
1651 480 : emitIdGuard(getElemKeyValueId(), id);
1652 : }
1653 :
1654 : void
1655 947 : SetPropIRGenerator::maybeEmitIdGuard(jsid id)
1656 : {
1657 947 : if (cacheKind_ == CacheKind::SetProp) {
1658 : // Constant PropertyName, no guards necessary.
1659 639 : MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
1660 639 : return;
1661 : }
1662 :
1663 308 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
1664 308 : emitIdGuard(setElemKeyValueId(), id);
1665 : }
1666 :
1667 977 : GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
1668 : ICState::Mode mode, HandleObject env,
1669 977 : HandlePropertyName name)
1670 : : IRGenerator(cx, script, pc, CacheKind::GetName, mode),
1671 : env_(env),
1672 977 : name_(name)
1673 977 : {}
1674 :
1675 : bool
1676 977 : GetNameIRGenerator::tryAttachStub()
1677 : {
1678 977 : MOZ_ASSERT(cacheKind_ == CacheKind::GetName);
1679 :
1680 1954 : AutoAssertNoPendingException aanpe(cx_);
1681 :
1682 977 : ObjOperandId envId(writer.setInputOperandId(0));
1683 1954 : RootedId id(cx_, NameToId(name_));
1684 :
1685 977 : if (tryAttachGlobalNameValue(envId, id))
1686 837 : return true;
1687 140 : if (tryAttachGlobalNameGetter(envId, id))
1688 5 : return true;
1689 135 : if (tryAttachEnvironmentName(envId, id))
1690 11 : return true;
1691 :
1692 124 : trackNotAttached();
1693 124 : return false;
1694 : }
1695 :
1696 : bool
1697 855 : CanAttachGlobalName(JSContext* cx, Handle<LexicalEnvironmentObject*> globalLexical, HandleId id,
1698 : MutableHandleNativeObject holder, MutableHandleShape shape)
1699 : {
1700 : // The property must be found, and it must be found as a normal data property.
1701 1710 : RootedNativeObject current(cx, globalLexical);
1702 : while (true) {
1703 1589 : shape.set(current->lookup(cx, id));
1704 1589 : if (shape)
1705 849 : break;
1706 :
1707 740 : if (current == globalLexical) {
1708 732 : current = &globalLexical->global();
1709 : } else {
1710 : // In the browser the global prototype chain should be immutable.
1711 8 : if (!current->staticPrototypeIsImmutable())
1712 4 : return false;
1713 :
1714 4 : JSObject* proto = current->staticPrototype();
1715 4 : if (!proto || !proto->is<NativeObject>())
1716 2 : return false;
1717 :
1718 2 : current = &proto->as<NativeObject>();
1719 : }
1720 734 : }
1721 :
1722 849 : holder.set(current);
1723 849 : return true;
1724 : }
1725 :
1726 : bool
1727 977 : GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
1728 : {
1729 977 : if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
1730 131 : return false;
1731 :
1732 846 : Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
1733 846 : MOZ_ASSERT(globalLexical->isGlobal());
1734 :
1735 1692 : RootedNativeObject holder(cx_);
1736 1692 : RootedShape shape(cx_);
1737 846 : if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
1738 3 : return false;
1739 :
1740 : // The property must be found, and it must be found as a normal data property.
1741 843 : if (!shape->hasDefaultGetter() || !shape->hasSlot())
1742 6 : return false;
1743 :
1744 : // This might still be an uninitialized lexical.
1745 837 : if (holder->getSlot(shape->slot()).isMagic())
1746 0 : return false;
1747 :
1748 : // Instantiate this global property, for use during Ion compilation.
1749 837 : if (IsIonEnabled(cx_))
1750 837 : EnsureTrackPropertyTypes(cx_, holder, id);
1751 :
1752 837 : if (holder == globalLexical) {
1753 : // There is no need to guard on the shape. Lexical bindings are
1754 : // non-configurable, and this stub cannot be shared across globals.
1755 123 : size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
1756 123 : writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
1757 : } else {
1758 : // Check the prototype chain from the global to the holder
1759 : // prototype. Ignore the global lexical scope as it doesn't figure
1760 : // into the prototype chain. We guard on the global lexical
1761 : // scope's shape independently.
1762 1428 : if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), holder,
1763 1428 : PropertyResult(shape)))
1764 0 : return false;
1765 :
1766 : // Shape guard for global lexical.
1767 714 : writer.guardShape(objId, globalLexical->lastProperty());
1768 :
1769 : // Guard on the shape of the GlobalObject.
1770 714 : ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
1771 714 : writer.guardShape(globalId, globalLexical->global().lastProperty());
1772 :
1773 714 : ObjOperandId holderId = globalId;
1774 714 : if (holder != &globalLexical->global()) {
1775 : // Shape guard holder.
1776 0 : holderId = writer.loadObject(holder);
1777 0 : writer.guardShape(holderId, holder->lastProperty());
1778 : }
1779 :
1780 714 : EmitLoadSlotResult(writer, holderId, holder, shape);
1781 : }
1782 :
1783 837 : writer.typeMonitorResult();
1784 :
1785 837 : trackAttached("GlobalNameValue");
1786 837 : return true;
1787 : }
1788 :
1789 : bool
1790 140 : GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id)
1791 : {
1792 140 : if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
1793 131 : return false;
1794 :
1795 9 : Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
1796 9 : MOZ_ASSERT(globalLexical->isGlobal());
1797 :
1798 18 : RootedNativeObject holder(cx_);
1799 18 : RootedShape shape(cx_);
1800 9 : if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
1801 3 : return false;
1802 :
1803 6 : if (holder == globalLexical)
1804 0 : return false;
1805 :
1806 6 : if (!IsCacheableGetPropCallNative(&globalLexical->global(), holder, shape))
1807 1 : return false;
1808 :
1809 5 : if (IsIonEnabled(cx_))
1810 5 : EnsureTrackPropertyTypes(cx_, holder, id);
1811 :
1812 : // Shape guard for global lexical.
1813 5 : writer.guardShape(objId, globalLexical->lastProperty());
1814 :
1815 : // Guard on the shape of the GlobalObject.
1816 5 : ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
1817 5 : writer.guardShape(globalId, globalLexical->global().lastProperty());
1818 :
1819 5 : if (holder != &globalLexical->global()) {
1820 : // Shape guard holder.
1821 0 : ObjOperandId holderId = writer.loadObject(holder);
1822 0 : writer.guardShape(holderId, holder->lastProperty());
1823 : }
1824 :
1825 5 : EmitCallGetterResultNoGuards(writer, &globalLexical->global(), holder, shape, globalId);
1826 :
1827 5 : trackAttached("GlobalNameGetter");
1828 5 : return true;
1829 : }
1830 :
1831 : static bool
1832 23 : NeedEnvironmentShapeGuard(JSObject* envObj)
1833 : {
1834 23 : if (!envObj->is<CallObject>())
1835 19 : return true;
1836 :
1837 : // We can skip a guard on the call object if the script's bindings are
1838 : // guaranteed to be immutable (and thus cannot introduce shadowing
1839 : // variables). The function might have been relazified under rare
1840 : // conditions. In that case, we pessimistically create the guard.
1841 4 : CallObject* callObj = &envObj->as<CallObject>();
1842 4 : JSFunction* fun = &callObj->callee();
1843 4 : if (!fun->hasScript() || fun->nonLazyScript()->funHasExtensibleScope())
1844 4 : return true;
1845 :
1846 0 : return false;
1847 : }
1848 :
1849 : bool
1850 135 : GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
1851 : {
1852 135 : if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
1853 124 : return false;
1854 :
1855 22 : RootedObject env(cx_, env_);
1856 22 : RootedShape shape(cx_);
1857 22 : RootedNativeObject holder(cx_);
1858 :
1859 35 : while (env) {
1860 23 : if (env->is<GlobalObject>()) {
1861 4 : shape = env->as<GlobalObject>().lookup(cx_, id);
1862 4 : if (shape)
1863 4 : break;
1864 0 : return false;
1865 : }
1866 :
1867 19 : if (!env->is<EnvironmentObject>() || env->is<WithEnvironmentObject>())
1868 0 : return false;
1869 :
1870 19 : MOZ_ASSERT(!env->hasUncacheableProto());
1871 :
1872 : // Check for an 'own' property on the env. There is no need to
1873 : // check the prototype as non-with scopes do not inherit properties
1874 : // from any prototype.
1875 19 : shape = env->as<NativeObject>().lookup(cx_, id);
1876 19 : if (shape)
1877 7 : break;
1878 :
1879 12 : env = env->enclosingEnvironment();
1880 : }
1881 :
1882 11 : holder = &env->as<NativeObject>();
1883 11 : if (!IsCacheableGetPropReadSlotForIonOrCacheIR(holder, holder, PropertyResult(shape)))
1884 0 : return false;
1885 11 : if (holder->getSlot(shape->slot()).isMagic())
1886 0 : return false;
1887 :
1888 11 : ObjOperandId lastObjId = objId;
1889 11 : env = env_;
1890 35 : while (env) {
1891 23 : if (NeedEnvironmentShapeGuard(env))
1892 23 : writer.guardShape(lastObjId, env->maybeShape());
1893 :
1894 23 : if (env == holder)
1895 11 : break;
1896 :
1897 12 : lastObjId = writer.loadEnclosingEnvironment(lastObjId);
1898 12 : env = env->enclosingEnvironment();
1899 : }
1900 :
1901 11 : if (holder->isFixedSlot(shape->slot())) {
1902 4 : writer.loadEnvironmentFixedSlotResult(lastObjId, NativeObject::getFixedSlotOffset(shape->slot()));
1903 : } else {
1904 7 : size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
1905 7 : writer.loadEnvironmentDynamicSlotResult(lastObjId, dynamicSlotOffset);
1906 : }
1907 11 : writer.typeMonitorResult();
1908 :
1909 11 : trackAttached("EnvironmentName");
1910 11 : return true;
1911 : }
1912 :
1913 : void
1914 853 : GetNameIRGenerator::trackAttached(const char* name)
1915 : {
1916 : #ifdef JS_CACHEIR_SPEW
1917 853 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
1918 853 : if (sp.enabled()) {
1919 0 : LockGuard<Mutex> guard(sp.lock());
1920 0 : sp.beginCache(guard, *this);
1921 0 : sp.valueProperty(guard, "base", ObjectValue(*env_));
1922 0 : sp.valueProperty(guard, "property", StringValue(name_));
1923 0 : sp.attached(guard, name);
1924 0 : sp.endCache(guard);
1925 : }
1926 : #endif
1927 853 : }
1928 :
1929 : void
1930 124 : GetNameIRGenerator::trackNotAttached()
1931 : {
1932 : #ifdef JS_CACHEIR_SPEW
1933 124 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
1934 124 : if (sp.enabled()) {
1935 0 : LockGuard<Mutex> guard(sp.lock());
1936 0 : sp.beginCache(guard, *this);
1937 0 : sp.valueProperty(guard, "base", ObjectValue(*env_));
1938 0 : sp.valueProperty(guard, "property", StringValue(name_));
1939 0 : sp.endCache(guard);
1940 : }
1941 : #endif
1942 124 : }
1943 :
1944 0 : BindNameIRGenerator::BindNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
1945 : ICState::Mode mode, HandleObject env,
1946 0 : HandlePropertyName name)
1947 : : IRGenerator(cx, script, pc, CacheKind::BindName, mode),
1948 : env_(env),
1949 0 : name_(name)
1950 0 : {}
1951 :
1952 : bool
1953 0 : BindNameIRGenerator::tryAttachStub()
1954 : {
1955 0 : MOZ_ASSERT(cacheKind_ == CacheKind::BindName);
1956 :
1957 0 : AutoAssertNoPendingException aanpe(cx_);
1958 :
1959 0 : ObjOperandId envId(writer.setInputOperandId(0));
1960 0 : RootedId id(cx_, NameToId(name_));
1961 :
1962 0 : if (tryAttachGlobalName(envId, id))
1963 0 : return true;
1964 0 : if (tryAttachEnvironmentName(envId, id))
1965 0 : return true;
1966 :
1967 0 : trackNotAttached();
1968 0 : return false;
1969 : }
1970 :
1971 : bool
1972 0 : BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId, HandleId id)
1973 : {
1974 0 : if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
1975 0 : return false;
1976 :
1977 0 : Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
1978 0 : MOZ_ASSERT(globalLexical->isGlobal());
1979 :
1980 0 : JSObject* result = nullptr;
1981 0 : if (Shape* shape = globalLexical->lookup(cx_, id)) {
1982 : // If this is an uninitialized lexical or a const, we need to return a
1983 : // RuntimeLexicalErrorObject.
1984 0 : if (globalLexical->getSlot(shape->slot()).isMagic() || !shape->writable())
1985 0 : return false;
1986 0 : result = globalLexical;
1987 : } else {
1988 0 : result = &globalLexical->global();
1989 : }
1990 :
1991 0 : if (result == globalLexical) {
1992 : // Lexical bindings are non-configurable so we can just return the
1993 : // global lexical.
1994 0 : writer.loadObjectResult(objId);
1995 : } else {
1996 : // If the property exists on the global and is non-configurable, it cannot be
1997 : // shadowed by the lexical scope so we can just return the global without a
1998 : // shape guard.
1999 0 : Shape* shape = result->as<GlobalObject>().lookup(cx_, id);
2000 0 : if (!shape || shape->configurable())
2001 0 : writer.guardShape(objId, globalLexical->lastProperty());
2002 0 : ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
2003 0 : writer.loadObjectResult(globalId);
2004 : }
2005 0 : writer.returnFromIC();
2006 :
2007 0 : trackAttached("GlobalName");
2008 0 : return true;
2009 : }
2010 :
2011 : bool
2012 0 : BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
2013 : {
2014 0 : if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
2015 0 : return false;
2016 :
2017 0 : RootedObject env(cx_, env_);
2018 0 : RootedShape shape(cx_);
2019 : while (true) {
2020 0 : if (!env->is<GlobalObject>() && !env->is<EnvironmentObject>())
2021 0 : return false;
2022 0 : if (env->is<WithEnvironmentObject>())
2023 0 : return false;
2024 :
2025 0 : MOZ_ASSERT(!env->hasUncacheableProto());
2026 :
2027 : // When we reach an unqualified variables object (like the global) we
2028 : // have to stop looking and return that object.
2029 0 : if (env->isUnqualifiedVarObj())
2030 0 : break;
2031 :
2032 : // Check for an 'own' property on the env. There is no need to
2033 : // check the prototype as non-with scopes do not inherit properties
2034 : // from any prototype.
2035 0 : shape = env->as<NativeObject>().lookup(cx_, id);
2036 0 : if (shape)
2037 0 : break;
2038 :
2039 0 : env = env->enclosingEnvironment();
2040 : }
2041 :
2042 : // If this is an uninitialized lexical or a const, we need to return a
2043 : // RuntimeLexicalErrorObject.
2044 0 : RootedNativeObject holder(cx_, &env->as<NativeObject>());
2045 0 : if (shape &&
2046 0 : holder->is<EnvironmentObject>() &&
2047 0 : (holder->getSlot(shape->slot()).isMagic() || !shape->writable()))
2048 : {
2049 0 : return false;
2050 : }
2051 :
2052 0 : ObjOperandId lastObjId = objId;
2053 0 : env = env_;
2054 0 : while (env) {
2055 0 : if (NeedEnvironmentShapeGuard(env) && !env->is<GlobalObject>())
2056 0 : writer.guardShape(lastObjId, env->maybeShape());
2057 :
2058 0 : if (env == holder)
2059 0 : break;
2060 :
2061 0 : lastObjId = writer.loadEnclosingEnvironment(lastObjId);
2062 0 : env = env->enclosingEnvironment();
2063 : }
2064 0 : writer.loadObjectResult(lastObjId);
2065 0 : writer.returnFromIC();
2066 :
2067 0 : trackAttached("EnvironmentName");
2068 0 : return true;
2069 : }
2070 :
2071 : void
2072 0 : BindNameIRGenerator::trackAttached(const char* name)
2073 : {
2074 : #ifdef JS_CACHEIR_SPEW
2075 0 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
2076 0 : if (sp.enabled()) {
2077 0 : LockGuard<Mutex> guard(sp.lock());
2078 0 : sp.beginCache(guard, *this);
2079 0 : sp.valueProperty(guard, "base", ObjectValue(*env_));
2080 0 : sp.valueProperty(guard, "property", StringValue(name_));
2081 0 : sp.attached(guard, name);
2082 0 : sp.endCache(guard);
2083 : }
2084 : #endif
2085 0 : }
2086 :
2087 : void
2088 0 : BindNameIRGenerator::trackNotAttached()
2089 : {
2090 : #ifdef JS_CACHEIR_SPEW
2091 0 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
2092 0 : if (sp.enabled()) {
2093 0 : LockGuard<Mutex> guard(sp.lock());
2094 0 : sp.beginCache(guard, *this);
2095 0 : sp.valueProperty(guard, "base", ObjectValue(*env_));
2096 0 : sp.valueProperty(guard, "property", StringValue(name_));
2097 0 : sp.endCache(guard);
2098 : }
2099 : #endif
2100 0 : }
2101 :
2102 184 : HasPropIRGenerator::HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
2103 : CacheKind cacheKind, ICState::Mode mode,
2104 184 : HandleValue idVal, HandleValue val)
2105 : : IRGenerator(cx, script, pc, cacheKind, mode),
2106 : val_(val),
2107 184 : idVal_(idVal)
2108 184 : { }
2109 :
2110 : bool
2111 39 : HasPropIRGenerator::tryAttachDense(HandleObject obj, ObjOperandId objId,
2112 : uint32_t index, Int32OperandId indexId)
2113 : {
2114 39 : if (!obj->isNative())
2115 2 : return false;
2116 37 : if (!obj->as<NativeObject>().containsDenseElement(index))
2117 0 : return false;
2118 :
2119 : // Guard shape to ensure object class is NativeObject.
2120 37 : writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
2121 :
2122 37 : writer.loadDenseElementExistsResult(objId, indexId);
2123 37 : writer.returnFromIC();
2124 :
2125 37 : trackAttached("DenseHasProp");
2126 37 : return true;
2127 : }
2128 :
2129 : bool
2130 2 : HasPropIRGenerator::tryAttachDenseHole(HandleObject obj, ObjOperandId objId,
2131 : uint32_t index, Int32OperandId indexId)
2132 : {
2133 2 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2134 :
2135 2 : if (!obj->isNative())
2136 2 : return false;
2137 0 : if (obj->as<NativeObject>().containsDenseElement(index))
2138 0 : return false;
2139 0 : if (!CanAttachDenseElementHole(obj, hasOwn))
2140 0 : return false;
2141 :
2142 : // Guard shape to ensure class is NativeObject and to prevent non-dense
2143 : // elements being added. Also ensures prototype doesn't change if dynamic
2144 : // checks aren't emitted.
2145 0 : writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
2146 :
2147 : // Generate prototype guards if needed. This includes monitoring that
2148 : // properties were not added in the chain.
2149 0 : if (!hasOwn)
2150 0 : GeneratePrototypeHoleGuards(writer, obj, objId);
2151 :
2152 0 : writer.loadDenseElementHoleExistsResult(objId, indexId);
2153 0 : writer.returnFromIC();
2154 :
2155 0 : trackAttached("DenseHasPropHole");
2156 0 : return true;
2157 : }
2158 :
2159 : bool
2160 145 : HasPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId,
2161 : HandleId key, ValOperandId keyId)
2162 : {
2163 145 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2164 :
2165 145 : JSObject* holder = nullptr;
2166 145 : PropertyResult prop;
2167 :
2168 145 : if (hasOwn) {
2169 0 : if (!LookupOwnPropertyPure(cx_, obj, key, &prop))
2170 0 : return false;
2171 :
2172 0 : holder = obj;
2173 : } else {
2174 145 : if (!LookupPropertyPure(cx_, obj, key, &holder, &prop))
2175 22 : return false;
2176 : }
2177 123 : if (!prop.isFound())
2178 52 : return false;
2179 :
2180 : // Use MegamorphicHasOwnResult if applicable
2181 71 : if (hasOwn && mode_ == ICState::Mode::Megamorphic) {
2182 0 : writer.megamorphicHasOwnResult(objId, keyId);
2183 0 : writer.returnFromIC();
2184 0 : trackAttached("MegamorphicHasProp");
2185 0 : return true;
2186 : }
2187 :
2188 142 : Maybe<ObjOperandId> tempId;
2189 71 : emitIdGuard(keyId, key);
2190 71 : EmitReadSlotGuard(writer, obj, holder, objId, &tempId);
2191 71 : writer.loadBooleanResult(true);
2192 71 : writer.returnFromIC();
2193 :
2194 71 : trackAttached("NativeHasProp");
2195 71 : return true;
2196 : }
2197 :
2198 : bool
2199 74 : HasPropIRGenerator::tryAttachNativeDoesNotExist(HandleObject obj, ObjOperandId objId,
2200 : HandleId key, ValOperandId keyId)
2201 : {
2202 74 : bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
2203 :
2204 74 : if (hasOwn) {
2205 0 : if (!CheckHasNoSuchOwnProperty(cx_, obj, key))
2206 0 : return false;
2207 : } else {
2208 74 : if (!CheckHasNoSuchProperty(cx_, obj, key))
2209 22 : return false;
2210 : }
2211 :
2212 : // Use MegamorphicHasOwnResult if applicable
2213 52 : if (hasOwn && mode_ == ICState::Mode::Megamorphic) {
2214 0 : writer.megamorphicHasOwnResult(objId, keyId);
2215 0 : writer.returnFromIC();
2216 0 : trackAttached("MegamorphicHasOwn");
2217 0 : return true;
2218 : }
2219 :
2220 104 : Maybe<ObjOperandId> tempId;
2221 52 : emitIdGuard(keyId, key);
2222 52 : if (hasOwn) {
2223 0 : TestMatchingReceiver(writer, obj, objId, &tempId);
2224 : } else {
2225 52 : EmitReadSlotGuard(writer, obj, nullptr, objId, &tempId);
2226 : }
2227 52 : writer.loadBooleanResult(false);
2228 52 : writer.returnFromIC();
2229 :
2230 52 : trackAttached("NativeDoesNotExist");
2231 52 : return true;
2232 : }
2233 :
2234 : bool
2235 0 : HasPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId,
2236 : ValOperandId keyId)
2237 : {
2238 0 : MOZ_ASSERT(cacheKind_ == CacheKind::HasOwn);
2239 :
2240 0 : if (!obj->is<ProxyObject>())
2241 0 : return false;
2242 :
2243 0 : writer.guardIsProxy(objId);
2244 0 : writer.callProxyHasOwnResult(objId, keyId);
2245 0 : writer.returnFromIC();
2246 :
2247 0 : trackAttached("ProxyHasProp");
2248 0 : return true;
2249 : }
2250 :
2251 : bool
2252 184 : HasPropIRGenerator::tryAttachStub()
2253 : {
2254 184 : MOZ_ASSERT(cacheKind_ == CacheKind::In ||
2255 : cacheKind_ == CacheKind::HasOwn);
2256 :
2257 368 : AutoAssertNoPendingException aanpe(cx_);
2258 :
2259 : // NOTE: Argument order is PROPERTY, OBJECT
2260 184 : ValOperandId keyId(writer.setInputOperandId(0));
2261 184 : ValOperandId valId(writer.setInputOperandId(1));
2262 :
2263 184 : if (!val_.isObject()) {
2264 0 : trackNotAttached();
2265 0 : return false;
2266 : }
2267 368 : RootedObject obj(cx_, &val_.toObject());
2268 184 : ObjOperandId objId = writer.guardIsObject(valId);
2269 :
2270 : // Optimize DOM Proxies for JSOP_HASOWN
2271 184 : if (cacheKind_ == CacheKind::HasOwn) {
2272 0 : if (tryAttachProxyElement(obj, objId, keyId))
2273 0 : return true;
2274 : }
2275 :
2276 368 : RootedId id(cx_);
2277 : bool nameOrSymbol;
2278 184 : if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
2279 0 : cx_->clearPendingException();
2280 0 : return false;
2281 : }
2282 :
2283 184 : if (nameOrSymbol) {
2284 145 : if (tryAttachNative(obj, objId, id, keyId))
2285 71 : return true;
2286 74 : if (tryAttachNativeDoesNotExist(obj, objId, id, keyId))
2287 52 : return true;
2288 :
2289 22 : trackNotAttached();
2290 22 : return false;
2291 : }
2292 :
2293 : uint32_t index;
2294 39 : Int32OperandId indexId;
2295 39 : if (maybeGuardInt32Index(idVal_, keyId, &index, &indexId)) {
2296 39 : if (tryAttachDense(obj, objId, index, indexId))
2297 37 : return true;
2298 2 : if (tryAttachDenseHole(obj, objId, index, indexId))
2299 0 : return true;
2300 :
2301 2 : trackNotAttached();
2302 2 : return false;
2303 : }
2304 :
2305 0 : trackNotAttached();
2306 0 : return false;
2307 : }
2308 :
2309 : void
2310 160 : HasPropIRGenerator::trackAttached(const char* name)
2311 : {
2312 : #ifdef JS_CACHEIR_SPEW
2313 160 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
2314 160 : if (sp.enabled()) {
2315 0 : LockGuard<Mutex> guard(sp.lock());
2316 0 : sp.beginCache(guard, *this);
2317 0 : sp.valueProperty(guard, "base", val_);
2318 0 : sp.valueProperty(guard, "property", idVal_);
2319 0 : sp.attached(guard, name);
2320 0 : sp.endCache(guard);
2321 : }
2322 : #endif
2323 160 : }
2324 :
2325 : void
2326 24 : HasPropIRGenerator::trackNotAttached()
2327 : {
2328 : #ifdef JS_CACHEIR_SPEW
2329 24 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
2330 24 : if (sp.enabled()) {
2331 0 : LockGuard<Mutex> guard(sp.lock());
2332 0 : sp.beginCache(guard, *this);
2333 0 : sp.valueProperty(guard, "base", val_);
2334 0 : sp.valueProperty(guard, "property", idVal_);
2335 0 : sp.endCache(guard);
2336 : }
2337 : #endif
2338 24 : }
2339 :
2340 : bool
2341 353 : IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
2342 : uint32_t* int32Index, Int32OperandId* int32IndexId)
2343 : {
2344 353 : if (index.isNumber()) {
2345 : int32_t indexSigned;
2346 322 : if (index.isInt32()) {
2347 322 : indexSigned = index.toInt32();
2348 : } else {
2349 : // We allow negative zero here.
2350 0 : if (!mozilla::NumberEqualsInt32(index.toDouble(), &indexSigned))
2351 0 : return false;
2352 0 : if (!cx_->runtime()->jitSupportsFloatingPoint)
2353 0 : return false;
2354 : }
2355 :
2356 322 : if (indexSigned < 0)
2357 0 : return false;
2358 :
2359 322 : *int32Index = uint32_t(indexSigned);
2360 322 : *int32IndexId = writer.guardIsInt32Index(indexId);
2361 322 : return true;
2362 : }
2363 :
2364 31 : if (index.isString()) {
2365 2 : int32_t indexSigned = GetIndexFromString(index.toString());
2366 2 : if (indexSigned < 0)
2367 0 : return false;
2368 :
2369 2 : StringOperandId strId = writer.guardIsString(indexId);
2370 2 : *int32Index = uint32_t(indexSigned);
2371 2 : *int32IndexId = writer.guardAndGetIndexFromString(strId);
2372 2 : return true;
2373 : }
2374 :
2375 29 : return false;
2376 : }
2377 :
2378 1705 : SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
2379 : CacheKind cacheKind, ICState::Mode mode,
2380 : bool* isTemporarilyUnoptimizable,
2381 : HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal,
2382 1705 : bool needsTypeBarrier, bool maybeHasExtraIndexedProps)
2383 : : IRGenerator(cx, script, pc, cacheKind, mode),
2384 : lhsVal_(lhsVal),
2385 : idVal_(idVal),
2386 : rhsVal_(rhsVal),
2387 : isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
2388 : typeCheckInfo_(cx, needsTypeBarrier),
2389 : preliminaryObjectAction_(PreliminaryObjectAction::None),
2390 : attachedTypedArrayOOBStub_(false),
2391 1705 : maybeHasExtraIndexedProps_(maybeHasExtraIndexedProps)
2392 1705 : {}
2393 :
2394 : bool
2395 1165 : SetPropIRGenerator::tryAttachStub()
2396 : {
2397 2330 : AutoAssertNoPendingException aanpe(cx_);
2398 :
2399 1165 : ValOperandId objValId(writer.setInputOperandId(0));
2400 1165 : ValOperandId rhsValId;
2401 1165 : if (cacheKind_ == CacheKind::SetProp) {
2402 690 : rhsValId = ValOperandId(writer.setInputOperandId(1));
2403 : } else {
2404 475 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
2405 475 : MOZ_ASSERT(setElemKeyValueId().id() == 1);
2406 475 : writer.setInputOperandId(1);
2407 475 : rhsValId = ValOperandId(writer.setInputOperandId(2));
2408 : }
2409 :
2410 2330 : RootedId id(cx_);
2411 : bool nameOrSymbol;
2412 1165 : if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
2413 0 : cx_->clearPendingException();
2414 0 : return false;
2415 : }
2416 :
2417 1165 : if (lhsVal_.isObject()) {
2418 2330 : RootedObject obj(cx_, &lhsVal_.toObject());
2419 1165 : if (obj->watched())
2420 0 : return false;
2421 :
2422 1165 : ObjOperandId objId = writer.guardIsObject(objValId);
2423 1165 : if (nameOrSymbol) {
2424 1020 : if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
2425 422 : return true;
2426 598 : if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
2427 0 : return true;
2428 598 : if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
2429 47 : return true;
2430 551 : if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId))
2431 0 : return true;
2432 551 : if (tryAttachSetArrayLength(obj, objId, id, rhsValId))
2433 10 : return true;
2434 541 : if (IsPropertySetOp(JSOp(*pc_))) {
2435 504 : if (tryAttachSetter(obj, objId, id, rhsValId))
2436 9 : return true;
2437 495 : if (tryAttachWindowProxy(obj, objId, id, rhsValId))
2438 0 : return true;
2439 495 : if (tryAttachProxy(obj, objId, id, rhsValId))
2440 16 : return true;
2441 : }
2442 516 : return false;
2443 : }
2444 :
2445 145 : if (IsPropertySetOp(JSOp(*pc_))) {
2446 76 : if (tryAttachProxyElement(obj, objId, rhsValId))
2447 0 : return true;
2448 : }
2449 :
2450 : uint32_t index;
2451 145 : Int32OperandId indexId;
2452 145 : if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) {
2453 138 : if (tryAttachSetDenseElement(obj, objId, index, indexId, rhsValId))
2454 26 : return true;
2455 112 : if (tryAttachSetDenseElementHole(obj, objId, index, indexId, rhsValId))
2456 96 : return true;
2457 16 : if (tryAttachSetUnboxedArrayElement(obj, objId, index, indexId, rhsValId))
2458 0 : return true;
2459 16 : if (tryAttachSetUnboxedArrayElementHole(obj, objId, index, indexId, rhsValId))
2460 0 : return true;
2461 16 : if (tryAttachSetTypedElement(obj, objId, index, indexId, rhsValId))
2462 0 : return true;
2463 16 : return false;
2464 : }
2465 7 : return false;
2466 : }
2467 0 : return false;
2468 : }
2469 :
2470 : static void
2471 422 : EmitStoreSlotAndReturn(CacheIRWriter& writer, ObjOperandId objId, NativeObject* nobj, Shape* shape,
2472 : ValOperandId rhsId)
2473 : {
2474 422 : if (nobj->isFixedSlot(shape->slot())) {
2475 381 : size_t offset = NativeObject::getFixedSlotOffset(shape->slot());
2476 381 : writer.storeFixedSlot(objId, offset, rhsId);
2477 : } else {
2478 41 : size_t offset = nobj->dynamicSlotIndex(shape->slot()) * sizeof(Value);
2479 41 : writer.storeDynamicSlot(objId, offset, rhsId);
2480 : }
2481 422 : writer.returnFromIC();
2482 422 : }
2483 :
2484 : static Shape*
2485 957 : LookupShapeForSetSlot(NativeObject* obj, jsid id)
2486 : {
2487 957 : Shape* shape = obj->lookupPure(id);
2488 957 : if (shape && shape->hasSlot() && shape->hasDefaultSetter() && shape->writable())
2489 446 : return shape;
2490 511 : return nullptr;
2491 : }
2492 :
2493 : static bool
2494 1020 : CanAttachNativeSetSlot(JSContext* cx, HandleObject obj, HandleId id,
2495 : bool* isTemporarilyUnoptimizable, MutableHandleShape propShape)
2496 : {
2497 1020 : if (!obj->isNative())
2498 63 : return false;
2499 :
2500 957 : propShape.set(LookupShapeForSetSlot(&obj->as<NativeObject>(), id));
2501 957 : if (!propShape)
2502 511 : return false;
2503 :
2504 446 : ObjectGroup* group = JSObject::getGroup(cx, obj);
2505 446 : if (!group) {
2506 0 : cx->recoverFromOutOfMemory();
2507 0 : return false;
2508 : }
2509 :
2510 : // For some property writes, such as the initial overwrite of global
2511 : // properties, TI will not mark the property as having been
2512 : // overwritten. Don't attach a stub in this case, so that we don't
2513 : // execute another write to the property without TI seeing that write.
2514 446 : EnsureTrackPropertyTypes(cx, obj, id);
2515 446 : if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
2516 24 : *isTemporarilyUnoptimizable = true;
2517 24 : return false;
2518 : }
2519 :
2520 422 : return true;
2521 : }
2522 :
2523 : bool
2524 1020 : SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
2525 : ValOperandId rhsId)
2526 : {
2527 2040 : RootedShape propShape(cx_);
2528 1020 : if (!CanAttachNativeSetSlot(cx_, obj, id, isTemporarilyUnoptimizable_, &propShape))
2529 598 : return false;
2530 :
2531 422 : if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp) {
2532 0 : writer.megamorphicStoreSlot(objId, JSID_TO_ATOM(id)->asPropertyName(), rhsId,
2533 0 : typeCheckInfo_.needsTypeBarrier());
2534 0 : writer.returnFromIC();
2535 0 : trackAttached("MegamorphicNativeSlot");
2536 0 : return true;
2537 : }
2538 :
2539 422 : maybeEmitIdGuard(id);
2540 :
2541 : // If we need a property type barrier (always in Baseline, sometimes in
2542 : // Ion), guard on both the shape and the group. If Ion knows the property
2543 : // types match, we don't need the group guard.
2544 422 : NativeObject* nobj = &obj->as<NativeObject>();
2545 422 : if (typeCheckInfo_.needsTypeBarrier())
2546 422 : writer.guardGroup(objId, nobj->group());
2547 422 : writer.guardShape(objId, nobj->lastProperty());
2548 :
2549 422 : if (IsPreliminaryObject(obj))
2550 353 : preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
2551 : else
2552 69 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
2553 :
2554 422 : typeCheckInfo_.set(nobj->group(), id);
2555 422 : EmitStoreSlotAndReturn(writer, objId, nobj, propShape, rhsId);
2556 :
2557 422 : trackAttached("NativeSlot");
2558 422 : return true;
2559 : }
2560 :
2561 : bool
2562 598 : SetPropIRGenerator::tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId,
2563 : HandleId id, ValOperandId rhsId)
2564 : {
2565 598 : if (!obj->is<UnboxedPlainObject>())
2566 551 : return false;
2567 :
2568 47 : UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
2569 47 : if (!expando)
2570 47 : return false;
2571 :
2572 0 : Shape* propShape = LookupShapeForSetSlot(expando, id);
2573 0 : if (!propShape)
2574 0 : return false;
2575 :
2576 0 : maybeEmitIdGuard(id);
2577 0 : writer.guardGroup(objId, obj->group());
2578 0 : ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId);
2579 0 : writer.guardShape(expandoId, expando->lastProperty());
2580 :
2581 : // Property types must be added to the unboxed object's group, not the
2582 : // expando's group (it has unknown properties).
2583 0 : typeCheckInfo_.set(obj->group(), id);
2584 0 : EmitStoreSlotAndReturn(writer, expandoId, expando, propShape, rhsId);
2585 :
2586 0 : trackAttached("UnboxedExpando");
2587 0 : return true;
2588 : }
2589 :
2590 : static void
2591 47 : EmitGuardUnboxedPropertyType(CacheIRWriter& writer, JSValueType propType, ValOperandId valId)
2592 : {
2593 47 : if (propType == JSVAL_TYPE_OBJECT) {
2594 : // Unboxed objects store NullValue as nullptr object.
2595 21 : writer.guardIsObjectOrNull(valId);
2596 : } else {
2597 26 : writer.guardType(valId, propType);
2598 : }
2599 47 : }
2600 :
2601 : bool
2602 598 : SetPropIRGenerator::tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
2603 : ValOperandId rhsId)
2604 : {
2605 598 : if (!obj->is<UnboxedPlainObject>() || !cx_->runtime()->jitSupportsFloatingPoint)
2606 551 : return false;
2607 :
2608 47 : const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
2609 47 : if (!property)
2610 0 : return false;
2611 :
2612 47 : maybeEmitIdGuard(id);
2613 47 : writer.guardGroup(objId, obj->group());
2614 47 : EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
2615 47 : writer.storeUnboxedProperty(objId, property->type,
2616 47 : UnboxedPlainObject::offsetOfData() + property->offset,
2617 47 : rhsId);
2618 47 : writer.returnFromIC();
2619 :
2620 47 : typeCheckInfo_.set(obj->group(), id);
2621 47 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
2622 :
2623 47 : trackAttached("Unboxed");
2624 47 : return true;
2625 : }
2626 :
2627 : bool
2628 551 : SetPropIRGenerator::tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id,
2629 : ValOperandId rhsId)
2630 : {
2631 551 : if (!obj->is<TypedObject>() || !cx_->runtime()->jitSupportsFloatingPoint)
2632 551 : return false;
2633 :
2634 0 : if (cx_->compartment()->detachedTypedObjects)
2635 0 : return false;
2636 :
2637 0 : if (!obj->as<TypedObject>().typeDescr().is<StructTypeDescr>())
2638 0 : return false;
2639 :
2640 0 : StructTypeDescr* structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>();
2641 : size_t fieldIndex;
2642 0 : if (!structDescr->fieldIndex(id, &fieldIndex))
2643 0 : return false;
2644 :
2645 0 : TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
2646 0 : if (!fieldDescr->is<SimpleTypeDescr>())
2647 0 : return false;
2648 :
2649 0 : uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
2650 0 : TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
2651 :
2652 0 : maybeEmitIdGuard(id);
2653 0 : writer.guardNoDetachedTypedObjects();
2654 0 : writer.guardShape(objId, obj->as<TypedObject>().shape());
2655 0 : writer.guardGroup(objId, obj->group());
2656 :
2657 0 : typeCheckInfo_.set(obj->group(), id);
2658 :
2659 : // Scalar types can always be stored without a type update stub.
2660 0 : if (fieldDescr->is<ScalarTypeDescr>()) {
2661 0 : Scalar::Type type = fieldDescr->as<ScalarTypeDescr>().type();
2662 0 : writer.storeTypedObjectScalarProperty(objId, fieldOffset, layout, type, rhsId);
2663 0 : writer.returnFromIC();
2664 :
2665 0 : trackAttached("TypedObject");
2666 0 : return true;
2667 : }
2668 :
2669 : // For reference types, guard on the RHS type first, so that
2670 : // StoreTypedObjectReferenceProperty is infallible.
2671 0 : ReferenceTypeDescr::Type type = fieldDescr->as<ReferenceTypeDescr>().type();
2672 0 : switch (type) {
2673 : case ReferenceTypeDescr::TYPE_ANY:
2674 0 : break;
2675 : case ReferenceTypeDescr::TYPE_OBJECT:
2676 0 : writer.guardIsObjectOrNull(rhsId);
2677 0 : break;
2678 : case ReferenceTypeDescr::TYPE_STRING:
2679 0 : writer.guardType(rhsId, JSVAL_TYPE_STRING);
2680 0 : break;
2681 : }
2682 :
2683 0 : writer.storeTypedObjectReferenceProperty(objId, fieldOffset, layout, type, rhsId);
2684 0 : writer.returnFromIC();
2685 :
2686 0 : trackAttached("TypedObject");
2687 0 : return true;
2688 : }
2689 :
2690 : void
2691 1070 : SetPropIRGenerator::trackAttached(const char* name)
2692 : {
2693 : #ifdef JS_CACHEIR_SPEW
2694 1070 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
2695 1070 : if (sp.enabled()) {
2696 0 : LockGuard<Mutex> guard(sp.lock());
2697 0 : sp.beginCache(guard, *this);
2698 0 : sp.valueProperty(guard, "base", lhsVal_);
2699 0 : sp.valueProperty(guard, "property", idVal_);
2700 0 : sp.valueProperty(guard, "value", rhsVal_);
2701 0 : sp.attached(guard, name);
2702 0 : sp.endCache(guard);
2703 : }
2704 : #endif
2705 1070 : }
2706 :
2707 : void
2708 96 : SetPropIRGenerator::trackNotAttached()
2709 : {
2710 : #ifdef JS_CACHEIR_SPEW
2711 96 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
2712 96 : if (sp.enabled()) {
2713 0 : LockGuard<Mutex> guard(sp.lock());
2714 0 : sp.beginCache(guard, *this);
2715 0 : sp.valueProperty(guard, "base", lhsVal_);
2716 0 : sp.valueProperty(guard, "property", idVal_);
2717 0 : sp.valueProperty(guard, "value", rhsVal_);
2718 0 : sp.endCache(guard);
2719 : }
2720 : #endif
2721 96 : }
2722 :
2723 : static bool
2724 505 : CanAttachSetter(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id,
2725 : MutableHandleObject holder, MutableHandleShape propShape,
2726 : bool* isTemporarilyUnoptimizable)
2727 : {
2728 : // Don't attach a setter stub for ops like JSOP_INITELEM.
2729 505 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
2730 :
2731 505 : PropertyResult prop;
2732 505 : if (!LookupPropertyPure(cx, obj, id, holder.address(), &prop))
2733 31 : return false;
2734 :
2735 474 : if (prop.isNonNativeProperty())
2736 0 : return false;
2737 :
2738 474 : propShape.set(prop.maybeShape());
2739 944 : if (!IsCacheableSetPropCallScripted(obj, holder, propShape, isTemporarilyUnoptimizable) &&
2740 470 : !IsCacheableSetPropCallNative(obj, holder, propShape))
2741 : {
2742 464 : return false;
2743 : }
2744 :
2745 10 : return true;
2746 : }
2747 :
2748 : static void
2749 10 : EmitCallSetterNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
2750 : Shape* shape, ObjOperandId objId, ValOperandId rhsId)
2751 : {
2752 10 : if (IsCacheableSetPropCallNative(obj, holder, shape)) {
2753 6 : JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
2754 6 : MOZ_ASSERT(target->isNative());
2755 6 : writer.callNativeSetter(objId, target, rhsId);
2756 6 : writer.returnFromIC();
2757 6 : return;
2758 : }
2759 :
2760 4 : MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
2761 :
2762 4 : JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
2763 4 : MOZ_ASSERT(target->hasJITCode());
2764 4 : writer.callScriptedSetter(objId, target, rhsId);
2765 4 : writer.returnFromIC();
2766 : }
2767 :
2768 : bool
2769 504 : SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
2770 : ValOperandId rhsId)
2771 : {
2772 1008 : RootedObject holder(cx_);
2773 1008 : RootedShape propShape(cx_);
2774 504 : if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &propShape, isTemporarilyUnoptimizable_))
2775 495 : return false;
2776 :
2777 9 : maybeEmitIdGuard(id);
2778 :
2779 : // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
2780 : // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
2781 : // require outerizing).
2782 9 : if (mode_ == ICState::Mode::Specialized || IsWindow(obj)) {
2783 18 : Maybe<ObjOperandId> expandoId;
2784 9 : TestMatchingReceiver(writer, obj, objId, &expandoId);
2785 :
2786 9 : if (obj != holder) {
2787 9 : GeneratePrototypeGuards(writer, obj, holder, objId);
2788 :
2789 : // Guard on the holder's shape.
2790 9 : ObjOperandId holderId = writer.loadObject(holder);
2791 9 : writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
2792 : }
2793 : } else {
2794 0 : writer.guardHasGetterSetter(objId, propShape);
2795 : }
2796 :
2797 9 : EmitCallSetterNoGuards(writer, obj, holder, propShape, objId, rhsId);
2798 :
2799 9 : trackAttached("Setter");
2800 9 : return true;
2801 : }
2802 :
2803 : bool
2804 551 : SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id,
2805 : ValOperandId rhsId)
2806 : {
2807 1112 : if (!obj->is<ArrayObject>() ||
2808 561 : !JSID_IS_ATOM(id, cx_->names().length) ||
2809 10 : !obj->as<ArrayObject>().lengthIsWritable())
2810 : {
2811 541 : return false;
2812 : }
2813 :
2814 10 : maybeEmitIdGuard(id);
2815 10 : writer.guardClass(objId, GuardClassKind::Array);
2816 10 : writer.callSetArrayLength(objId, IsStrictSetPC(pc_), rhsId);
2817 10 : writer.returnFromIC();
2818 :
2819 10 : trackAttached("SetArrayLength");
2820 10 : return true;
2821 : }
2822 :
2823 : bool
2824 138 : SetPropIRGenerator::tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId, uint32_t index,
2825 : Int32OperandId indexId, ValOperandId rhsId)
2826 : {
2827 138 : if (!obj->isNative())
2828 0 : return false;
2829 :
2830 138 : NativeObject* nobj = &obj->as<NativeObject>();
2831 138 : if (!nobj->containsDenseElement(index) || nobj->getElementsHeader()->isFrozen())
2832 112 : return false;
2833 :
2834 26 : if (typeCheckInfo_.needsTypeBarrier())
2835 26 : writer.guardGroup(objId, nobj->group());
2836 26 : writer.guardShape(objId, nobj->shape());
2837 :
2838 26 : writer.storeDenseElement(objId, indexId, rhsId);
2839 26 : writer.returnFromIC();
2840 :
2841 : // Type inference uses JSID_VOID for the element types.
2842 26 : typeCheckInfo_.set(nobj->group(), JSID_VOID);
2843 :
2844 26 : trackAttached("SetDenseElement");
2845 26 : return true;
2846 : }
2847 :
2848 : static bool
2849 123 : CanAttachAddElement(JSObject* obj, bool isInit)
2850 : {
2851 : // Make sure the objects on the prototype don't have any indexed properties
2852 : // or that such properties can't appear without a shape change.
2853 : do {
2854 : // The first two checks are also relevant to the receiver object.
2855 123 : if (obj->isIndexed())
2856 0 : return false;
2857 :
2858 123 : const Class* clasp = obj->getClass();
2859 177 : if ((clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) &&
2860 108 : (clasp->getAddProperty() ||
2861 108 : clasp->getResolve() ||
2862 108 : clasp->getOpsLookupProperty() ||
2863 108 : clasp->getSetProperty() ||
2864 54 : clasp->getOpsSetProperty()))
2865 : {
2866 0 : return false;
2867 : }
2868 :
2869 : // If we're initializing a property instead of setting one, the objects
2870 : // on the prototype are not relevant.
2871 123 : if (isInit)
2872 69 : break;
2873 :
2874 54 : JSObject* proto = obj->staticPrototype();
2875 54 : if (!proto)
2876 27 : break;
2877 :
2878 27 : if (!proto->isNative())
2879 0 : return false;
2880 :
2881 27 : obj = proto;
2882 : } while (true);
2883 :
2884 96 : return true;
2885 : }
2886 :
2887 : static void
2888 1078 : ShapeGuardProtoChain(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
2889 : {
2890 : while (true) {
2891 : // Guard on the proto if the shape does not imply the proto. Singleton
2892 : // objects always trigger a shape change when the proto changes, so we
2893 : // don't need a guard in that case.
2894 1078 : bool guardProto = obj->hasUncacheableProto() && !obj->isSingleton();
2895 :
2896 1078 : obj = obj->staticPrototype();
2897 1078 : if (!obj)
2898 942 : return;
2899 :
2900 607 : objId = writer.loadProto(objId);
2901 607 : if (guardProto)
2902 0 : writer.guardSpecificObject(objId, obj);
2903 607 : writer.guardShape(objId, obj->as<NativeObject>().shape());
2904 607 : }
2905 : }
2906 :
2907 : bool
2908 112 : SetPropIRGenerator::tryAttachSetDenseElementHole(HandleObject obj, ObjOperandId objId,
2909 : uint32_t index, Int32OperandId indexId,
2910 : ValOperandId rhsId)
2911 : {
2912 112 : if (!obj->isNative() || rhsVal_.isMagic(JS_ELEMENTS_HOLE))
2913 0 : return false;
2914 :
2915 112 : JSOp op = JSOp(*pc_);
2916 112 : MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
2917 :
2918 112 : if (op == JSOP_INITHIDDENELEM)
2919 0 : return false;
2920 :
2921 112 : NativeObject* nobj = &obj->as<NativeObject>();
2922 112 : if (!nobj->nonProxyIsExtensible())
2923 0 : return false;
2924 :
2925 112 : MOZ_ASSERT(!nobj->getElementsHeader()->isFrozen(),
2926 : "Extensible objects should not have frozen elements");
2927 :
2928 112 : uint32_t initLength = nobj->getDenseInitializedLength();
2929 :
2930 : // Optimize if we're adding an element at initLength or writing to a hole.
2931 : // Don't handle the adding case if the current accesss is in bounds, to
2932 : // ensure we always call noteArrayWriteHole.
2933 112 : bool isAdd = index == initLength;
2934 112 : bool isHoleInBounds = index < initLength && !nobj->containsDenseElement(index);
2935 112 : if (!isAdd && !isHoleInBounds)
2936 16 : return false;
2937 :
2938 : // Can't add new elements to arrays with non-writable length.
2939 96 : if (isAdd && nobj->is<ArrayObject>() && !nobj->as<ArrayObject>().lengthIsWritable())
2940 0 : return false;
2941 :
2942 : // Typed arrays don't have dense elements.
2943 96 : if (nobj->is<TypedArrayObject>())
2944 0 : return false;
2945 :
2946 : // Check for other indexed properties or class hooks.
2947 96 : if (!CanAttachAddElement(nobj, IsPropertyInitOp(op)))
2948 0 : return false;
2949 :
2950 96 : if (typeCheckInfo_.needsTypeBarrier())
2951 96 : writer.guardGroup(objId, nobj->group());
2952 96 : writer.guardShape(objId, nobj->shape());
2953 :
2954 : // Also shape guard the proto chain, unless this is an INITELEM or we know
2955 : // the proto chain has no indexed props.
2956 96 : if (IsPropertySetOp(op) && maybeHasExtraIndexedProps_)
2957 27 : ShapeGuardProtoChain(writer, obj, objId);
2958 :
2959 96 : writer.storeDenseElementHole(objId, indexId, rhsId, isAdd);
2960 96 : writer.returnFromIC();
2961 :
2962 : // Type inference uses JSID_VOID for the element types.
2963 96 : typeCheckInfo_.set(nobj->group(), JSID_VOID);
2964 :
2965 96 : trackAttached(isAdd ? "AddDenseElement" : "StoreDenseElementHole");
2966 96 : return true;
2967 : }
2968 :
2969 : bool
2970 16 : SetPropIRGenerator::tryAttachSetUnboxedArrayElement(HandleObject obj, ObjOperandId objId,
2971 : uint32_t index, Int32OperandId indexId,
2972 : ValOperandId rhsId)
2973 : {
2974 16 : if (!obj->is<UnboxedArrayObject>())
2975 16 : return false;
2976 :
2977 0 : if (!cx_->runtime()->jitSupportsFloatingPoint)
2978 0 : return false;
2979 :
2980 0 : if (index >= obj->as<UnboxedArrayObject>().initializedLength())
2981 0 : return false;
2982 :
2983 0 : writer.guardGroup(objId, obj->group());
2984 :
2985 0 : JSValueType elementType = obj->group()->unboxedLayoutDontCheckGeneration().elementType();
2986 0 : EmitGuardUnboxedPropertyType(writer, elementType, rhsId);
2987 :
2988 0 : writer.storeUnboxedArrayElement(objId, indexId, rhsId, elementType);
2989 0 : writer.returnFromIC();
2990 :
2991 : // Type inference uses JSID_VOID for the element types.
2992 0 : typeCheckInfo_.set(obj->group(), JSID_VOID);
2993 :
2994 0 : trackAttached("SetUnboxedArrayElement");
2995 0 : return true;
2996 : }
2997 :
2998 : bool
2999 16 : SetPropIRGenerator::tryAttachSetTypedElement(HandleObject obj, ObjOperandId objId,
3000 : uint32_t index, Int32OperandId indexId,
3001 : ValOperandId rhsId)
3002 : {
3003 16 : if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
3004 16 : return false;
3005 :
3006 0 : if (!rhsVal_.isNumber())
3007 0 : return false;
3008 :
3009 0 : if (!cx_->runtime()->jitSupportsFloatingPoint && TypedThingRequiresFloatingPoint(obj))
3010 0 : return false;
3011 :
3012 0 : bool handleOutOfBounds = false;
3013 0 : if (obj->is<TypedArrayObject>()) {
3014 0 : handleOutOfBounds = (index >= obj->as<TypedArrayObject>().length());
3015 : } else {
3016 : // Typed objects throw on out of bounds accesses. Don't attach
3017 : // a stub in this case.
3018 0 : if (index >= obj->as<TypedObject>().length())
3019 0 : return false;
3020 :
3021 : // Don't attach stubs if the underlying storage for typed objects
3022 : // in the compartment could be detached, as the stub will always
3023 : // bail out.
3024 0 : if (cx_->compartment()->detachedTypedObjects)
3025 0 : return false;
3026 : }
3027 :
3028 0 : Scalar::Type elementType = TypedThingElementType(obj);
3029 0 : TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
3030 :
3031 0 : if (!obj->is<TypedArrayObject>())
3032 0 : writer.guardNoDetachedTypedObjects();
3033 :
3034 0 : writer.guardShape(objId, obj->as<ShapedObject>().shape());
3035 0 : writer.storeTypedElement(objId, indexId, rhsId, layout, elementType, handleOutOfBounds);
3036 0 : writer.returnFromIC();
3037 :
3038 0 : if (handleOutOfBounds)
3039 0 : attachedTypedArrayOOBStub_ = true;
3040 :
3041 0 : trackAttached(handleOutOfBounds ? "SetTypedElementOOB" : "SetTypedElement");
3042 0 : return true;
3043 : }
3044 :
3045 : bool
3046 16 : SetPropIRGenerator::tryAttachSetUnboxedArrayElementHole(HandleObject obj, ObjOperandId objId,
3047 : uint32_t index, Int32OperandId indexId,
3048 : ValOperandId rhsId)
3049 : {
3050 16 : if (!obj->is<UnboxedArrayObject>() || rhsVal_.isMagic(JS_ELEMENTS_HOLE))
3051 16 : return false;
3052 :
3053 0 : if (!cx_->runtime()->jitSupportsFloatingPoint)
3054 0 : return false;
3055 :
3056 0 : JSOp op = JSOp(*pc_);
3057 0 : MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
3058 :
3059 0 : if (op == JSOP_INITHIDDENELEM)
3060 0 : return false;
3061 :
3062 : // Optimize if we're adding an element at initLength. Unboxed arrays don't
3063 : // have holes at indexes < initLength.
3064 0 : UnboxedArrayObject* aobj = &obj->as<UnboxedArrayObject>();
3065 0 : if (index != aobj->initializedLength() || index >= aobj->capacity())
3066 0 : return false;
3067 :
3068 : // Check for other indexed properties or class hooks.
3069 0 : if (!CanAttachAddElement(aobj, IsPropertyInitOp(op)))
3070 0 : return false;
3071 :
3072 0 : writer.guardGroup(objId, aobj->group());
3073 :
3074 0 : JSValueType elementType = aobj->group()->unboxedLayoutDontCheckGeneration().elementType();
3075 0 : EmitGuardUnboxedPropertyType(writer, elementType, rhsId);
3076 :
3077 : // Also shape guard the proto chain, unless this is an INITELEM.
3078 0 : if (IsPropertySetOp(op))
3079 0 : ShapeGuardProtoChain(writer, aobj, objId);
3080 :
3081 0 : writer.storeUnboxedArrayElementHole(objId, indexId, rhsId, elementType);
3082 0 : writer.returnFromIC();
3083 :
3084 : // Type inference uses JSID_VOID for the element types.
3085 0 : typeCheckInfo_.set(aobj->group(), JSID_VOID);
3086 :
3087 0 : trackAttached("StoreUnboxedArrayElementHole");
3088 0 : return true;
3089 : }
3090 :
3091 : bool
3092 15 : SetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
3093 : ValOperandId rhsId, bool handleDOMProxies)
3094 : {
3095 15 : MOZ_ASSERT(obj->is<ProxyObject>());
3096 :
3097 15 : writer.guardIsProxy(objId);
3098 :
3099 15 : if (!handleDOMProxies) {
3100 : // Ensure that the incoming object is not a DOM proxy, so that we can
3101 : // get to the specialized stubs. If handleDOMProxies is true, we were
3102 : // unable to attach a specialized DOM stub, so we just handle all
3103 : // proxies here.
3104 14 : writer.guardNotDOMProxy(objId);
3105 : }
3106 :
3107 15 : if (cacheKind_ == CacheKind::SetProp || mode_ == ICState::Mode::Specialized) {
3108 14 : maybeEmitIdGuard(id);
3109 14 : writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
3110 : } else {
3111 : // Attach a stub that handles every id.
3112 1 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
3113 1 : MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
3114 1 : writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
3115 : }
3116 :
3117 15 : writer.returnFromIC();
3118 :
3119 15 : trackAttached("GenericProxy");
3120 15 : return true;
3121 : }
3122 :
3123 : bool
3124 0 : SetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id,
3125 : ValOperandId rhsId)
3126 : {
3127 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
3128 :
3129 0 : maybeEmitIdGuard(id);
3130 0 : writer.guardShape(objId, obj->maybeShape());
3131 :
3132 : // No need for more guards: we know this is a DOM proxy, since the shape
3133 : // guard enforces a given JSClass, so just go ahead and emit the call to
3134 : // ProxySet.
3135 0 : writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
3136 0 : writer.returnFromIC();
3137 :
3138 0 : trackAttached("DOMProxyShadowed");
3139 0 : return true;
3140 : }
3141 :
3142 : bool
3143 1 : SetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id,
3144 : ValOperandId rhsId)
3145 : {
3146 1 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
3147 :
3148 2 : RootedObject proto(cx_, obj->staticPrototype());
3149 1 : if (!proto)
3150 0 : return false;
3151 :
3152 2 : RootedObject holder(cx_);
3153 2 : RootedShape propShape(cx_);
3154 1 : if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &propShape, isTemporarilyUnoptimizable_))
3155 0 : return false;
3156 :
3157 1 : maybeEmitIdGuard(id);
3158 1 : writer.guardShape(objId, obj->maybeShape());
3159 :
3160 : // Guard that our expando object hasn't started shadowing this property.
3161 1 : CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
3162 :
3163 1 : GeneratePrototypeGuards(writer, obj, holder, objId);
3164 :
3165 : // Guard on the holder of the property.
3166 1 : ObjOperandId holderId = writer.loadObject(holder);
3167 1 : writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
3168 :
3169 : // EmitCallSetterNoGuards expects |obj| to be the object the property is
3170 : // on to do some checks. Since we actually looked at proto, and no extra
3171 : // guards will be generated, we can just pass that instead.
3172 1 : EmitCallSetterNoGuards(writer, proto, holder, propShape, objId, rhsId);
3173 :
3174 1 : trackAttached("DOMProxyUnshadowed");
3175 1 : return true;
3176 : }
3177 :
3178 : bool
3179 0 : SetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id,
3180 : ValOperandId rhsId)
3181 : {
3182 0 : MOZ_ASSERT(IsCacheableDOMProxy(obj));
3183 :
3184 0 : RootedValue expandoVal(cx_, GetProxyPrivate(obj));
3185 0 : RootedObject expandoObj(cx_);
3186 0 : if (expandoVal.isObject()) {
3187 0 : expandoObj = &expandoVal.toObject();
3188 : } else {
3189 0 : MOZ_ASSERT(!expandoVal.isUndefined(),
3190 : "How did a missing expando manage to shadow things?");
3191 0 : auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
3192 0 : MOZ_ASSERT(expandoAndGeneration);
3193 0 : expandoObj = &expandoAndGeneration->expando.toObject();
3194 : }
3195 :
3196 0 : RootedShape propShape(cx_);
3197 0 : if (CanAttachNativeSetSlot(cx_, expandoObj, id, isTemporarilyUnoptimizable_, &propShape)) {
3198 0 : maybeEmitIdGuard(id);
3199 : ObjOperandId expandoObjId =
3200 0 : guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
3201 :
3202 0 : NativeObject* nativeExpandoObj = &expandoObj->as<NativeObject>();
3203 0 : writer.guardGroup(expandoObjId, nativeExpandoObj->group());
3204 0 : typeCheckInfo_.set(nativeExpandoObj->group(), id);
3205 :
3206 0 : EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, propShape, rhsId);
3207 0 : trackAttached("DOMProxyExpandoSlot");
3208 0 : return true;
3209 : }
3210 :
3211 0 : RootedObject holder(cx_);
3212 0 : if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &propShape,
3213 : isTemporarilyUnoptimizable_))
3214 : {
3215 : // Note that we don't actually use the expandoObjId here after the
3216 : // shape guard. The DOM proxy (objId) is passed to the setter as
3217 : // |this|.
3218 0 : maybeEmitIdGuard(id);
3219 0 : guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
3220 :
3221 0 : MOZ_ASSERT(holder == expandoObj);
3222 0 : EmitCallSetterNoGuards(writer, expandoObj, expandoObj, propShape, objId, rhsId);
3223 0 : trackAttached("DOMProxyExpandoSetter");
3224 0 : return true;
3225 : }
3226 :
3227 0 : return false;
3228 : }
3229 :
3230 : bool
3231 495 : SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id,
3232 : ValOperandId rhsId)
3233 : {
3234 : // Don't attach a proxy stub for ops like JSOP_INITELEM.
3235 495 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
3236 :
3237 495 : ProxyStubType type = GetProxyStubType(cx_, obj, id);
3238 495 : if (type == ProxyStubType::None)
3239 479 : return false;
3240 :
3241 16 : if (mode_ == ICState::Mode::Megamorphic)
3242 1 : return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
3243 :
3244 15 : switch (type) {
3245 : case ProxyStubType::None:
3246 0 : break;
3247 : case ProxyStubType::DOMExpando:
3248 0 : if (tryAttachDOMProxyExpando(obj, objId, id, rhsId))
3249 0 : return true;
3250 0 : if (*isTemporarilyUnoptimizable_) {
3251 : // Scripted setter without JIT code. Just wait.
3252 0 : return false;
3253 : }
3254 : MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
3255 : case ProxyStubType::DOMShadowed:
3256 0 : return tryAttachDOMProxyShadowed(obj, objId, id, rhsId);
3257 : case ProxyStubType::DOMUnshadowed:
3258 1 : if (tryAttachDOMProxyUnshadowed(obj, objId, id, rhsId))
3259 1 : return true;
3260 0 : if (*isTemporarilyUnoptimizable_) {
3261 : // Scripted setter without JIT code. Just wait.
3262 0 : return false;
3263 : }
3264 0 : return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
3265 : case ProxyStubType::Generic:
3266 14 : return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ false);
3267 : }
3268 :
3269 0 : MOZ_CRASH("Unexpected ProxyStubType");
3270 : }
3271 :
3272 : bool
3273 76 : SetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId)
3274 : {
3275 : // Don't attach a proxy stub for ops like JSOP_INITELEM.
3276 76 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
3277 :
3278 76 : if (!obj->is<ProxyObject>())
3279 76 : return false;
3280 :
3281 0 : writer.guardIsProxy(objId);
3282 :
3283 : // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
3284 : // proxies here as we don't have specialized DOM stubs for this.
3285 0 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
3286 0 : writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
3287 0 : writer.returnFromIC();
3288 :
3289 0 : trackAttached("ProxyElement");
3290 0 : return true;
3291 : }
3292 :
3293 : bool
3294 495 : SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id,
3295 : ValOperandId rhsId)
3296 : {
3297 : // Attach a stub when the receiver is a WindowProxy and we can do the set
3298 : // on the Window (the global object).
3299 :
3300 495 : if (!IsWindowProxy(obj))
3301 495 : return false;
3302 :
3303 : // If we're megamorphic prefer a generic proxy stub that handles a lot more
3304 : // cases.
3305 0 : if (mode_ == ICState::Mode::Megamorphic)
3306 0 : return false;
3307 :
3308 : // This must be a WindowProxy for the current Window/global. Else it would
3309 : // be a cross-compartment wrapper and IsWindowProxy returns false for
3310 : // those.
3311 0 : MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
3312 0 : MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
3313 :
3314 : // Now try to do the set on the Window (the current global).
3315 0 : Handle<GlobalObject*> windowObj = cx_->global();
3316 :
3317 0 : RootedShape propShape(cx_);
3318 0 : if (!CanAttachNativeSetSlot(cx_, windowObj, id, isTemporarilyUnoptimizable_, &propShape))
3319 0 : return false;
3320 :
3321 0 : maybeEmitIdGuard(id);
3322 :
3323 0 : writer.guardClass(objId, GuardClassKind::WindowProxy);
3324 0 : ObjOperandId windowObjId = writer.loadObject(windowObj);
3325 :
3326 0 : writer.guardShape(windowObjId, windowObj->lastProperty());
3327 0 : writer.guardGroup(windowObjId, windowObj->group());
3328 0 : typeCheckInfo_.set(windowObj->group(), id);
3329 :
3330 0 : EmitStoreSlotAndReturn(writer, windowObjId, windowObj, propShape, rhsId);
3331 :
3332 0 : trackAttached("WindowProxySlot");
3333 0 : return true;
3334 : }
3335 :
3336 : bool
3337 540 : SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape)
3338 : {
3339 1080 : AutoAssertNoPendingException aanpe(cx_);
3340 :
3341 540 : ValOperandId objValId(writer.setInputOperandId(0));
3342 540 : ValOperandId rhsValId;
3343 540 : if (cacheKind_ == CacheKind::SetProp) {
3344 211 : rhsValId = ValOperandId(writer.setInputOperandId(1));
3345 : } else {
3346 329 : MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
3347 329 : MOZ_ASSERT(setElemKeyValueId().id() == 1);
3348 329 : writer.setInputOperandId(1);
3349 329 : rhsValId = ValOperandId(writer.setInputOperandId(2));
3350 : }
3351 :
3352 1080 : RootedId id(cx_);
3353 : bool nameOrSymbol;
3354 540 : if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
3355 0 : cx_->clearPendingException();
3356 0 : return false;
3357 : }
3358 :
3359 540 : if (!lhsVal_.isObject() || !nameOrSymbol)
3360 23 : return false;
3361 :
3362 1034 : RootedObject obj(cx_, &lhsVal_.toObject());
3363 517 : if (obj->watched())
3364 0 : return false;
3365 :
3366 517 : PropertyResult prop;
3367 : JSObject* holder;
3368 517 : if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
3369 0 : return false;
3370 517 : if (obj != holder)
3371 26 : return false;
3372 :
3373 491 : Shape* propShape = nullptr;
3374 491 : NativeObject* holderOrExpando = nullptr;
3375 :
3376 491 : if (obj->isNative()) {
3377 491 : propShape = prop.shape();
3378 491 : holderOrExpando = &obj->as<NativeObject>();
3379 : } else {
3380 0 : if (!obj->is<UnboxedPlainObject>())
3381 0 : return false;
3382 0 : UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
3383 0 : if (!expando)
3384 0 : return false;
3385 0 : propShape = expando->lookupPure(id);
3386 0 : if (!propShape)
3387 0 : return false;
3388 0 : holderOrExpando = expando;
3389 : }
3390 :
3391 491 : MOZ_ASSERT(propShape);
3392 :
3393 : // The property must be the last added property of the object.
3394 491 : if (holderOrExpando->lastProperty() != propShape)
3395 22 : return false;
3396 :
3397 : // Object must be extensible, oldShape must be immediate parent of
3398 : // current shape.
3399 469 : if (!obj->nonProxyIsExtensible() || propShape->previous() != oldShape)
3400 5 : return false;
3401 :
3402 : // Basic shape checks.
3403 1384 : if (propShape->inDictionary() ||
3404 909 : !propShape->hasSlot() ||
3405 1370 : !propShape->hasDefaultSetter() ||
3406 453 : !propShape->writable())
3407 : {
3408 11 : return false;
3409 : }
3410 :
3411 : // Watch out for resolve hooks.
3412 453 : if (ClassMayResolveId(cx_->names(), obj->getClass(), id, obj)) {
3413 : // The JSFunction resolve hook defines a (non-configurable and
3414 : // non-enumerable) |prototype| property on certain functions. Scripts
3415 : // often assign a custom |prototype| object and we want to optimize
3416 : // this |prototype| set and eliminate the default object allocation.
3417 : //
3418 : // We check group->maybeInterpretedFunction() here and guard on the
3419 : // group. The group is unique for a particular function so this ensures
3420 : // we don't add the default prototype property to functions that don't
3421 : // have it.
3422 27 : if (!obj->is<JSFunction>() ||
3423 10 : !JSID_IS_ATOM(id, cx_->names().prototype) ||
3424 21 : !oldGroup->maybeInterpretedFunction() ||
3425 5 : !obj->as<JSFunction>().needsPrototypeProperty())
3426 : {
3427 6 : return false;
3428 : }
3429 5 : MOZ_ASSERT(!propShape->configurable());
3430 5 : MOZ_ASSERT(!propShape->enumerable());
3431 : }
3432 :
3433 : // Also watch out for addProperty hooks. Ignore the Array addProperty hook,
3434 : // because it doesn't do anything for non-index properties.
3435 894 : DebugOnly<uint32_t> index;
3436 447 : MOZ_ASSERT_IF(obj->is<ArrayObject>(), !IdIsIndex(id, &index));
3437 447 : if (!obj->is<ArrayObject>() && obj->getClass()->getAddProperty())
3438 3 : return false;
3439 :
3440 : // Walk up the object prototype chain and ensure that all prototypes are
3441 : // native, and that all prototypes have no setter defined on the property.
3442 1024 : for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) {
3443 580 : if (!proto->isNative())
3444 0 : return false;
3445 :
3446 : // If prototype defines this property in a non-plain way, don't optimize.
3447 580 : Shape* protoShape = proto->as<NativeObject>().lookup(cx_, id);
3448 580 : if (protoShape && !protoShape->hasDefaultSetter())
3449 0 : return false;
3450 :
3451 : // Otherwise, if there's no such property, watch out for a resolve hook
3452 : // that would need to be invoked and thus prevent inlining of property
3453 : // addition. Allow the JSFunction resolve hook as it only defines plain
3454 : // data properties and we don't need to invoke it for objects on the
3455 : // proto chain.
3456 585 : if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto) &&
3457 5 : !proto->is<JSFunction>())
3458 : {
3459 0 : return false;
3460 : }
3461 : }
3462 :
3463 444 : ObjOperandId objId = writer.guardIsObject(objValId);
3464 444 : maybeEmitIdGuard(id);
3465 :
3466 444 : writer.guardGroup(objId, oldGroup);
3467 :
3468 : // If we are adding a property to an object for which the new script
3469 : // properties analysis hasn't been performed yet, make sure the stub fails
3470 : // after we run the analysis as a group change may be required here. The
3471 : // group change is not required for correctness but improves type
3472 : // information elsewhere.
3473 444 : if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
3474 111 : writer.guardGroupHasUnanalyzedNewScript(oldGroup);
3475 111 : MOZ_ASSERT(IsPreliminaryObject(obj));
3476 111 : preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
3477 : } else {
3478 333 : preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
3479 : }
3480 :
3481 : // Shape guard the holder.
3482 444 : ObjOperandId holderId = objId;
3483 444 : if (!obj->isNative()) {
3484 0 : MOZ_ASSERT(obj->as<UnboxedPlainObject>().maybeExpando());
3485 0 : holderId = writer.guardAndLoadUnboxedExpando(objId);
3486 : }
3487 444 : writer.guardShape(holderId, oldShape);
3488 :
3489 444 : ShapeGuardProtoChain(writer, obj, objId);
3490 :
3491 444 : ObjectGroup* newGroup = obj->group();
3492 :
3493 : // Check if we have to change the object's group. If we're adding an
3494 : // unboxed expando property, we pass the expando object to AddAndStore*Slot.
3495 : // That's okay because we only have to do a group change if the object is a
3496 : // PlainObject.
3497 444 : bool changeGroup = oldGroup != newGroup;
3498 444 : MOZ_ASSERT_IF(changeGroup, obj->is<PlainObject>());
3499 :
3500 444 : if (holderOrExpando->isFixedSlot(propShape->slot())) {
3501 110 : size_t offset = NativeObject::getFixedSlotOffset(propShape->slot());
3502 110 : writer.addAndStoreFixedSlot(holderId, offset, rhsValId, propShape,
3503 220 : changeGroup, newGroup);
3504 110 : trackAttached("AddSlot");
3505 : } else {
3506 334 : size_t offset = holderOrExpando->dynamicSlotIndex(propShape->slot()) * sizeof(Value);
3507 334 : uint32_t numOldSlots = NativeObject::dynamicSlotsCount(oldShape);
3508 334 : uint32_t numNewSlots = NativeObject::dynamicSlotsCount(propShape);
3509 334 : if (numOldSlots == numNewSlots) {
3510 274 : writer.addAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
3511 548 : changeGroup, newGroup);
3512 274 : trackAttached("AddSlot");
3513 : } else {
3514 60 : MOZ_ASSERT(numNewSlots > numOldSlots);
3515 60 : writer.allocateAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
3516 120 : changeGroup, newGroup, numNewSlots);
3517 60 : trackAttached("AllocateSlot");
3518 : }
3519 : }
3520 444 : writer.returnFromIC();
3521 :
3522 444 : typeCheckInfo_.set(oldGroup, id);
3523 444 : return true;
3524 : }
3525 :
3526 80 : TypeOfIRGenerator::TypeOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
3527 80 : ICState::Mode mode, HandleValue value)
3528 : : IRGenerator(cx, script, pc, CacheKind::TypeOf, mode),
3529 80 : val_(value)
3530 80 : { }
3531 :
3532 : bool
3533 80 : TypeOfIRGenerator::tryAttachStub()
3534 : {
3535 80 : MOZ_ASSERT(cacheKind_ == CacheKind::TypeOf);
3536 :
3537 160 : AutoAssertNoPendingException aanpe(cx_);
3538 :
3539 80 : ValOperandId valId(writer.setInputOperandId(0));
3540 :
3541 80 : if (tryAttachPrimitive(valId))
3542 61 : return true;
3543 :
3544 19 : MOZ_ALWAYS_TRUE(tryAttachObject(valId));
3545 19 : return true;
3546 : }
3547 :
3548 : bool
3549 80 : TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId)
3550 : {
3551 80 : if (!val_.isPrimitive())
3552 19 : return false;
3553 :
3554 61 : writer.guardType(valId, val_.isNumber() ? JSVAL_TYPE_DOUBLE : val_.extractNonDoubleType());
3555 61 : writer.loadStringResult(TypeName(js::TypeOfValue(val_), cx_->names()));
3556 61 : writer.returnFromIC();
3557 :
3558 61 : return true;
3559 : }
3560 :
3561 : bool
3562 19 : TypeOfIRGenerator::tryAttachObject(ValOperandId valId)
3563 : {
3564 19 : if (!val_.isObject())
3565 0 : return false;
3566 :
3567 19 : ObjOperandId objId = writer.guardIsObject(valId);
3568 19 : writer.loadTypeOfObjectResult(objId);
3569 19 : writer.returnFromIC();
3570 :
3571 19 : return true;
3572 : }
3573 :
3574 :
3575 3981 : CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
3576 : ICState::Mode mode, uint32_t argc,
3577 3981 : HandleValue callee, HandleValue thisval, HandleValueArray args)
3578 : : IRGenerator(cx, script, pc, CacheKind::Call, mode),
3579 : argc_(argc),
3580 : callee_(callee),
3581 : thisval_(thisval),
3582 : args_(args),
3583 3981 : cachedStrategy_()
3584 3981 : { }
3585 :
3586 : CallIRGenerator::OptStrategy
3587 3981 : CallIRGenerator::canOptimize()
3588 : {
3589 : // Ensure callee is a function.
3590 3981 : if (!callee_.isObject() || !callee_.toObject().is<JSFunction>())
3591 815 : return OptStrategy::None;
3592 :
3593 6332 : RootedFunction calleeFunc(cx_, &callee_.toObject().as<JSFunction>());
3594 :
3595 : OptStrategy strategy;
3596 3166 : if ((strategy = canOptimizeStringSplit(calleeFunc)) != OptStrategy::None) {
3597 1 : return strategy;
3598 : }
3599 :
3600 3165 : return OptStrategy::None;
3601 : }
3602 :
3603 : CallIRGenerator::OptStrategy
3604 3166 : CallIRGenerator::canOptimizeStringSplit(HandleFunction calleeFunc)
3605 : {
3606 3166 : if (argc_ != 2 || !args_[0].isString() || !args_[1].isString())
3607 3062 : return OptStrategy::None;
3608 :
3609 : // Just for now: if they're both atoms, then do not optimize using
3610 : // CacheIR and allow the legacy "ConstStringSplit" BaselineIC optimization
3611 : // to proceed.
3612 104 : if (args_[0].toString()->isAtom() && args_[1].toString()->isAtom())
3613 43 : return OptStrategy::None;
3614 :
3615 61 : if (!calleeFunc->isNative())
3616 53 : return OptStrategy::None;
3617 :
3618 8 : if (calleeFunc->native() != js::intrinsic_StringSplitString)
3619 7 : return OptStrategy::None;
3620 :
3621 1 : return OptStrategy::StringSplit;
3622 : }
3623 :
3624 : bool
3625 1 : CallIRGenerator::tryAttachStringSplit()
3626 : {
3627 : // Get the object group to use for this location.
3628 2 : RootedObjectGroup group(cx_, ObjectGroupCompartment::getStringSplitStringGroup(cx_));
3629 1 : if (!group) {
3630 0 : return false;
3631 : }
3632 :
3633 2 : AutoAssertNoPendingException aanpe(cx_);
3634 1 : Int32OperandId argcId(writer.setInputOperandId(0));
3635 :
3636 : // Ensure argc == 1.
3637 1 : writer.guardSpecificInt32Immediate(argcId, 2);
3638 :
3639 : // 1 argument only. Stack-layout here is (bottom to top):
3640 : //
3641 : // 3: Callee
3642 : // 2: ThisValue
3643 : // 1: Arg0
3644 : // 0: Arg1 <-- Top of stack
3645 :
3646 : // Ensure callee is an object and is the function that matches the callee optimized
3647 : // against during stub generation (i.e. the String_split function object).
3648 1 : ValOperandId calleeValId = writer.loadStackValue(3);
3649 1 : ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
3650 1 : writer.guardIsNativeFunction(calleeObjId, js::intrinsic_StringSplitString);
3651 :
3652 : // Ensure arg0 is a string.
3653 1 : ValOperandId arg0ValId = writer.loadStackValue(1);
3654 1 : StringOperandId arg0StrId = writer.guardIsString(arg0ValId);
3655 :
3656 : // Ensure arg1 is a string.
3657 1 : ValOperandId arg1ValId = writer.loadStackValue(0);
3658 1 : StringOperandId arg1StrId = writer.guardIsString(arg1ValId);
3659 :
3660 : // Call custom string splitter VM-function.
3661 1 : writer.callStringSplitResult(arg0StrId, arg1StrId, group);
3662 1 : writer.typeMonitorResult();
3663 :
3664 1 : return true;
3665 : }
3666 :
3667 : CallIRGenerator::OptStrategy
3668 3982 : CallIRGenerator::getOptStrategy(bool* optimizeAfterCall)
3669 : {
3670 3982 : if (!cachedStrategy_) {
3671 3981 : cachedStrategy_ = mozilla::Some(canOptimize());
3672 : }
3673 3982 : if (optimizeAfterCall != nullptr) {
3674 3981 : MOZ_ASSERT(cachedStrategy_.isSome());
3675 3981 : switch (cachedStrategy_.value()) {
3676 : case OptStrategy::StringSplit:
3677 1 : *optimizeAfterCall = true;
3678 1 : break;
3679 :
3680 : default:
3681 3980 : *optimizeAfterCall = false;
3682 : }
3683 : }
3684 3982 : return cachedStrategy_.value();
3685 : }
3686 :
3687 : bool
3688 1 : CallIRGenerator::tryAttachStub()
3689 : {
3690 1 : OptStrategy strategy = getOptStrategy();
3691 :
3692 1 : if (strategy == OptStrategy::StringSplit) {
3693 1 : return tryAttachStringSplit();
3694 : }
3695 :
3696 0 : MOZ_ASSERT(strategy == OptStrategy::None);
3697 0 : return false;
3698 : }
3699 :
3700 998 : CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
3701 : ICState::Mode mode, JSOp op,
3702 998 : HandleValue lhsVal, HandleValue rhsVal)
3703 : : IRGenerator(cx, script, pc, CacheKind::Compare, mode),
3704 998 : op_(op), lhsVal_(lhsVal), rhsVal_(rhsVal)
3705 998 : { }
3706 :
3707 : bool
3708 747 : CompareIRGenerator::tryAttachString(ValOperandId lhsId, ValOperandId rhsId)
3709 : {
3710 747 : MOZ_ASSERT(IsEqualityOp(op_));
3711 :
3712 747 : if (!lhsVal_.isString() || !rhsVal_.isString())
3713 381 : return false;
3714 :
3715 366 : StringOperandId lhsStrId = writer.guardIsString(lhsId);
3716 366 : StringOperandId rhsStrId = writer.guardIsString(rhsId);
3717 366 : writer.compareStringResult(op_, lhsStrId, rhsStrId);
3718 366 : writer.returnFromIC();
3719 :
3720 366 : trackAttached("String");
3721 366 : return true;
3722 : }
3723 :
3724 : bool
3725 381 : CompareIRGenerator::tryAttachObject(ValOperandId lhsId, ValOperandId rhsId)
3726 : {
3727 381 : MOZ_ASSERT(IsEqualityOp(op_));
3728 :
3729 381 : if (!lhsVal_.isObject() || !rhsVal_.isObject())
3730 362 : return false;
3731 :
3732 19 : ObjOperandId lhsObjId = writer.guardIsObject(lhsId);
3733 19 : ObjOperandId rhsObjId = writer.guardIsObject(rhsId);
3734 19 : writer.compareObjectResult(op_, lhsObjId, rhsObjId);
3735 19 : writer.returnFromIC();
3736 :
3737 19 : trackAttached("Object");
3738 19 : return true;
3739 : }
3740 :
3741 : bool
3742 362 : CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId)
3743 : {
3744 362 : MOZ_ASSERT(IsEqualityOp(op_));
3745 :
3746 362 : if (!lhsVal_.isSymbol() || !rhsVal_.isSymbol())
3747 362 : return false;
3748 :
3749 0 : SymbolOperandId lhsSymId = writer.guardIsSymbol(lhsId);
3750 0 : SymbolOperandId rhsSymId = writer.guardIsSymbol(rhsId);
3751 0 : writer.compareSymbolResult(op_, lhsSymId, rhsSymId);
3752 0 : writer.returnFromIC();
3753 :
3754 0 : trackAttached("Symbol");
3755 0 : return true;
3756 : }
3757 :
3758 : bool
3759 998 : CompareIRGenerator::tryAttachStub()
3760 : {
3761 998 : MOZ_ASSERT(cacheKind_ == CacheKind::Compare);
3762 998 : MOZ_ASSERT(IsEqualityOp(op_) ||
3763 : op_ == JSOP_LE || op_ == JSOP_LT ||
3764 : op_ == JSOP_GE || op_ == JSOP_GT);
3765 :
3766 1996 : AutoAssertNoPendingException aanpe(cx_);
3767 :
3768 998 : ValOperandId lhsId(writer.setInputOperandId(0));
3769 998 : ValOperandId rhsId(writer.setInputOperandId(1));
3770 :
3771 998 : if (IsEqualityOp(op_)) {
3772 747 : if (tryAttachString(lhsId, rhsId))
3773 366 : return true;
3774 381 : if (tryAttachObject(lhsId, rhsId))
3775 19 : return true;
3776 362 : if (tryAttachSymbol(lhsId, rhsId))
3777 0 : return true;
3778 :
3779 362 : trackNotAttached();
3780 362 : return false;
3781 : }
3782 :
3783 251 : trackNotAttached();
3784 251 : return false;
3785 : }
3786 :
3787 : void
3788 385 : CompareIRGenerator::trackAttached(const char* name)
3789 : {
3790 : #ifdef JS_CACHEIR_SPEW
3791 385 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
3792 385 : if (sp.enabled()) {
3793 0 : LockGuard<Mutex> guard(sp.lock());
3794 0 : sp.beginCache(guard, *this);
3795 0 : sp.valueProperty(guard, "lhs", lhsVal_);
3796 0 : sp.valueProperty(guard, "rhs", rhsVal_);
3797 0 : sp.attached(guard, name);
3798 0 : sp.endCache(guard);
3799 : }
3800 : #endif
3801 385 : }
3802 :
3803 : void
3804 613 : CompareIRGenerator::trackNotAttached()
3805 : {
3806 : #ifdef JS_CACHEIR_SPEW
3807 613 : CacheIRSpewer& sp = CacheIRSpewer::singleton();
3808 613 : if (sp.enabled()) {
3809 0 : LockGuard<Mutex> guard(sp.lock());
3810 0 : sp.beginCache(guard, *this);
3811 0 : sp.valueProperty(guard, "lhs", lhsVal_);
3812 0 : sp.valueProperty(guard, "rhs", rhsVal_);
3813 0 : sp.endCache(guard);
3814 : }
3815 : #endif
3816 613 : }
|