static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId objId) { // The guards here protect against the effects of JSObject::swap(). If the // prototype chain is directly altered, then TI will toss the jitcode, so we // don't have to worry about it, and any other change to the holder, or // adding a shadowing property will result in reshaping the holder, and thus // the failure of the shape guard. MOZ_ASSERT(obj != holder); if (obj->hasUncacheableProto()) { // If the shape does not imply the proto, emit an explicit proto guard. writer.guardProto(objId, obj->getProto()); } JSObject* pobj = IsCacheableDOMProxy(obj) ? obj->getTaggedProto().toObjectOrNull() : obj->getProto(); if (!pobj) return; while (pobj != holder) { if (pobj->hasUncacheableProto()) { ObjOperandId protoId = writer.loadObject(pobj); if (pobj->isSingleton()) { // Singletons can have their group's |proto| mutated directly. writer.guardProto(protoId, pobj->getProto()); } else { writer.guardGroup(protoId, pobj->group()); } } pobj = pobj->getProto(); } }
bool GetPropIRGenerator::tryAttachModuleNamespace(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) { MOZ_ASSERT(!emitted_); if (!obj->is<ModuleNamespaceObject>()) return true; Rooted<ModuleNamespaceObject*> ns(cx_, &obj->as<ModuleNamespaceObject>()); RootedModuleEnvironmentObject env(cx_); RootedShape shape(cx_); if (!ns->bindings().lookup(NameToId(name_), env.address(), shape.address())) return true; // Don't emit a stub until the target binding has been initialized. if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) return true; if (IsIonEnabled(cx_)) EnsureTrackPropertyTypes(cx_, env, shape->propid()); emitted_ = true; // Check for the specific namespace object. writer.guardSpecificObject(objId, ns); ObjOperandId envId = writer.loadObject(env); EmitLoadSlotResult(writer, envId, env, shape); return true; }
// Callers are expected to have already guarded on the shape of the // object, which guarantees the object is a DOM proxy. static void CheckDOMProxyExpandoDoesNotShadow(CacheIRWriter& writer, JSObject* obj, jsid id, ObjOperandId objId) { MOZ_ASSERT(IsCacheableDOMProxy(obj)); Value expandoVal = GetProxyExtra(obj, GetDOMProxyExpandoSlot()); ValOperandId expandoId; if (!expandoVal.isObject() && !expandoVal.isUndefined()) { ExpandoAndGeneration* expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate(); expandoId = writer.guardDOMExpandoGeneration(objId, expandoAndGeneration, expandoAndGeneration->generation); expandoVal = expandoAndGeneration->expando; } else { expandoId = writer.loadDOMExpandoValue(objId); } if (expandoVal.isUndefined()) { // Guard there's no expando object. writer.guardType(expandoId, JSVAL_TYPE_UNDEFINED); } else if (expandoVal.isObject()) { // Guard the proxy either has no expando object or, if it has one, that // the shape matches the current expando object. NativeObject& expandoObj = expandoVal.toObject().as<NativeObject>(); MOZ_ASSERT(!expandoObj.containsPure(id)); writer.guardDOMExpandoObject(expandoId, expandoObj.lastProperty()); } else { MOZ_CRASH("Invalid expando value"); } }
static void EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder, Shape* shape) { if (holder->isFixedSlot(shape->slot())) { writer.loadFixedSlotResult(holderOp, NativeObject::getFixedSlotOffset(shape->slot())); } else { size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value); writer.loadDynamicSlotResult(holderOp, dynamicSlotOffset); } }
static void EmitReadSlotReturn(CacheIRWriter& writer, JSObject*, JSObject* holder, Shape* shape) { // Slot access. if (holder) { MOZ_ASSERT(shape); writer.typeMonitorResult(); } else { // Normally for this op, the result would have to be monitored by TI. // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure // that undefined is already registered with the type-set, this can be avoided. writer.returnFromIC(); } }
static void EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape, ObjOperandId objId) { Maybe<ObjOperandId> expandoId; TestMatchingReceiver(writer, obj, shape, objId, &expandoId); if (obj != holder) { GeneratePrototypeGuards(writer, obj, holder, objId); // Guard on the holder's shape. ObjOperandId holderId = writer.loadObject(holder); writer.guardShape(holderId, holder->as<NativeObject>().lastProperty()); } EmitCallGetterResultNoGuards(writer, obj, holder, shape, objId); }
static void EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape, ObjOperandId objId) { Maybe<ObjOperandId> expandoId; TestMatchingReceiver(writer, obj, shape, objId, &expandoId); ObjOperandId holderId; if (obj != holder) { GeneratePrototypeGuards(writer, obj, holder, objId); if (holder) { // Guard on the holder's shape. holderId = writer.loadObject(holder); writer.guardShape(holderId, holder->as<NativeObject>().lastProperty()); } else { // The property does not exist. Guard on everything in the prototype // chain. This is guaranteed to see only Native objects because of // CanAttachNativeGetProp(). JSObject* proto = obj->taggedProto().toObjectOrNull(); ObjOperandId lastObjId = objId; while (proto) { ObjOperandId protoId = writer.loadProto(lastObjId); writer.guardShape(protoId, proto->as<NativeObject>().lastProperty()); proto = proto->staticPrototype(); lastObjId = protoId; } } } else if (obj->is<UnboxedPlainObject>()) { holder = obj->as<UnboxedPlainObject>().maybeExpando(); holderId = *expandoId; } else { holderId = objId; } // Slot access. if (holder) { MOZ_ASSERT(holderId.valid()); EmitLoadSlotResult(writer, holderId, &holder->as<NativeObject>(), shape); } else { MOZ_ASSERT(!holderId.valid()); writer.loadUndefinedResult(); } }
static void EmitCallGetterResultNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape, ObjOperandId objId) { if (IsCacheableGetPropCallNative(obj, holder, shape)) { JSFunction* target = &shape->getterValue().toObject().as<JSFunction>(); MOZ_ASSERT(target->isNative()); writer.callNativeGetterResult(objId, target); writer.typeMonitorResult(); return; } MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape)); JSFunction* target = &shape->getterValue().toObject().as<JSFunction>(); MOZ_ASSERT(target->hasJITCode()); writer.callScriptedGetterResult(objId, target); writer.typeMonitorResult(); }
bool GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) { MOZ_ASSERT(!emitted_); if (name_ != cx_->names().length) return true; if (obj->is<ArrayObject>()) { // Make sure int32 is added to the TypeSet before we attach a stub, so // the stub can return int32 values without monitoring the result. if (obj->as<ArrayObject>().length() > INT32_MAX) return true; writer.guardClass(objId, GuardClassKind::Array); writer.loadInt32ArrayLengthResult(objId); emitted_ = true; return true; } if (obj->is<UnboxedArrayObject>()) { writer.guardClass(objId, GuardClassKind::UnboxedArray); writer.loadUnboxedArrayLengthResult(objId); emitted_ = true; return true; } if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) { if (obj->is<MappedArgumentsObject>()) { writer.guardClass(objId, GuardClassKind::MappedArguments); } else { MOZ_ASSERT(obj->is<UnmappedArgumentsObject>()); writer.guardClass(objId, GuardClassKind::UnmappedArguments); } writer.loadArgumentsObjectLengthResult(objId); emitted_ = true; return true; } return true; }
static void TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId) { if (obj->is<UnboxedPlainObject>()) { writer.guardGroup(objId, obj->group()); if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) { ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId); writer.guardShape(expandoId, expando->lastProperty()); } else { writer.guardNoUnboxedExpando(objId); } } else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) { writer.guardGroup(objId, obj->group()); } else { Shape* shape = obj->maybeShape(); MOZ_ASSERT(shape); writer.guardShape(objId, shape); } }