diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index 5c774bb357ae..8c0bafdd6902 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -1505,6 +1505,79 @@ BaselineCacheIRCompiler::emitCallSetArrayLength() return true; } +typedef bool (*ProxySetPropertyFn)(JSContext*, HandleObject, HandleId, HandleValue, bool); +static const VMFunction ProxySetPropertyInfo = + FunctionInfo(ProxySetProperty, "ProxySetProperty"); + +bool +BaselineCacheIRCompiler::emitCallProxySet() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId()); + Address idAddr(stubAddress(reader.stubOffset())); + bool strict = reader.readBool(); + + AutoScratchRegister scratch(allocator, masm); + + allocator.discardStack(masm); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch); + + // Load the jsid in the scratch register. + masm.loadPtr(idAddr, scratch); + + masm.Push(Imm32(strict)); + masm.Push(val); + masm.Push(scratch); + masm.Push(obj); + + if (!callVM(masm, ProxySetPropertyInfo)) + return false; + + stubFrame.leave(masm); + return true; +} + +typedef bool (*ProxySetPropertyByValueFn)(JSContext*, HandleObject, HandleValue, HandleValue, bool); +static const VMFunction ProxySetPropertyByValueInfo = + FunctionInfo(ProxySetPropertyByValue, "ProxySetPropertyByValue"); + +bool +BaselineCacheIRCompiler::emitCallProxySetByValue() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId()); + ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId()); + bool strict = reader.readBool(); + + allocator.discardStack(masm); + + // We need a scratch register but we don't have any registers available on + // x86, so temporarily store |obj| in the frame's scratch slot. + int scratchOffset = BaselineFrame::reverseOffsetOfScratchValue(); + masm.storePtr(obj, Address(BaselineFrameReg, scratchOffset)); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, obj); + + // Restore |obj|. Because we entered a stub frame we first have to load + // the original frame pointer. + masm.loadPtr(Address(BaselineFrameReg, 0), obj); + masm.loadPtr(Address(obj, scratchOffset), obj); + + masm.Push(Imm32(strict)); + masm.Push(val); + masm.Push(idVal); + masm.Push(obj); + + if (!callVM(masm, ProxySetPropertyByValueInfo)) + return false; + + stubFrame.leave(masm); + return true; +} + bool BaselineCacheIRCompiler::emitTypeMonitorResult() { diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 00b9eb097698..a8afbb3e873e 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -1447,7 +1447,6 @@ SetPropIRGenerator::maybeEmitIdGuard(jsid id) emitIdGuard(setElemKeyValueId(), id); } - GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, HandleObject env, HandlePropertyName name) : IRGenerator(cx, script, pc, CacheKind::GetName), @@ -1904,9 +1903,14 @@ SetPropIRGenerator::tryAttachStub() return true; if (tryAttachSetArrayLength(obj, objId, id, rhsValId)) return true; + if (tryAttachProxy(obj, objId, id, rhsValId)) + return true; return false; } + if (tryAttachProxyElement(obj, objId, rhsValId)) + return true; + uint32_t index; Int32OperandId indexId; if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) { @@ -2506,6 +2510,69 @@ SetPropIRGenerator::tryAttachSetUnboxedArrayElementHole(HandleObject obj, ObjOpe return true; } +bool +SetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id, + ValOperandId rhsId) +{ + MOZ_ASSERT(obj->is()); + + writer.guardIsProxy(objId); + + // Ensure that the incoming object is not a DOM proxy, so that we can get to + // the specialized stubs + writer.guardNotDOMProxy(objId); + + if (cacheKind_ == CacheKind::SetProp) { + writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_)); + } else { + // We could call maybeEmitIdGuard here and then emit CallProxySet, but + // for SetElem we prefer to attach a stub that can handle any Value + // so we don't attach a new stub for every id. + MOZ_ASSERT(cacheKind_ == CacheKind::SetElem); + writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_)); + } + + writer.returnFromIC(); + + trackAttached("GenericProxy"); + return true; +} + +bool +SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id, + ValOperandId rhsId) +{ + switch (GetProxyStubType(cx_, obj, id)) { + case ProxyStubType::None: + return false; + case ProxyStubType::DOMExpando: + case ProxyStubType::DOMShadowed: + case ProxyStubType::DOMUnshadowed: + case ProxyStubType::Generic: + return tryAttachGenericProxy(obj, objId, id, rhsId); + } + + MOZ_CRASH("Unexpected ProxyStubType"); +} + +bool +SetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId) +{ + if (!obj->is()) + return false; + + writer.guardIsProxy(objId); + + // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM + // proxies here as we don't have specialized DOM stubs for this. + MOZ_ASSERT(cacheKind_ == CacheKind::SetElem); + writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_)); + writer.returnFromIC(); + + trackAttached("ProxyElement"); + return true; +} + bool SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape) { diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index abc707b69692..ea901b700fac 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -195,6 +195,8 @@ extern const char* CacheKindNames[]; _(CallNativeSetter) \ _(CallScriptedSetter) \ _(CallSetArrayLength) \ + _(CallProxySet) \ + _(CallProxySetByValue) \ \ /* The *Result ops load a value into the cache's result register. */ \ _(LoadFixedSlotResult) \ @@ -707,6 +709,18 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter buffer_.writeByte(uint32_t(strict)); writeOperandId(rhs); } + void callProxySet(ObjOperandId obj, jsid id, ValOperandId rhs, bool strict) { + writeOpWithOperandId(CacheOp::CallProxySet, obj); + writeOperandId(rhs); + addStubField(uintptr_t(JSID_BITS(id)), StubField::Type::Id); + buffer_.writeByte(uint32_t(strict)); + } + void callProxySetByValue(ObjOperandId obj, ValOperandId id, ValOperandId rhs, bool strict) { + writeOpWithOperandId(CacheOp::CallProxySetByValue, obj); + writeOperandId(id); + writeOperandId(rhs); + buffer_.writeByte(uint32_t(strict)); + } void loadBooleanResult(bool val) { writeOp(CacheOp::LoadBooleanResult); @@ -1077,6 +1091,11 @@ class MOZ_RAII SetPropIRGenerator : public IRGenerator bool tryAttachSetUnboxedArrayElementHole(HandleObject obj, ObjOperandId objId, uint32_t index, Int32OperandId indexId, ValOperandId rhsId); + bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id, + ValOperandId rhsId); + bool tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId); + bool tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId); + void trackAttached(const char* name); public: diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index 5370574c1bc4..cdb700a2ef22 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -933,6 +933,18 @@ IonCacheIRCompiler::emitCallSetArrayLength() MOZ_CRASH("Baseline-specific op"); } +bool +IonCacheIRCompiler::emitCallProxySet() +{ + MOZ_CRASH("Baseline-specific op"); +} + +bool +IonCacheIRCompiler::emitCallProxySetByValue() +{ + MOZ_CRASH("Baseline-specific op"); +} + bool IonCacheIRCompiler::emitLoadTypedObjectResult() { diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 5b7a7d3886a2..20139e529a6c 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -1440,15 +1440,6 @@ EmitObjectOpResultCheck(MacroAssembler& masm, Label* failure, bool strict, masm.bind(&noStrictError); } -static bool -ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, bool strict) -{ - RootedValue receiver(cx, ObjectValue(*proxy)); - ObjectOpResult result; - return Proxy::set(cx, proxy, id, v, receiver, result) - && result.checkStrictErrorOrWarning(cx, proxy, id, strict); -} - static bool EmitCallProxySet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, HandleId propId, LiveRegisterSet liveRegs, Register object, diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index 16b504bd71bf..e035e3c0161b 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -354,6 +354,31 @@ Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, Handle return handler->set(cx, proxy, id, v, receiver, result); } +bool +js::ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, HandleValue val, bool strict) +{ + ObjectOpResult result; + RootedValue receiver(cx, ObjectValue(*proxy)); + if (!Proxy::set(cx, proxy, id, val, receiver, result)) + return false; + return result.checkStrictErrorOrWarning(cx, proxy, id, strict); +} + +bool +js::ProxySetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal, HandleValue val, + bool strict) +{ + RootedId id(cx); + if (!ValueToId(cx, idVal, &id)) + return false; + + ObjectOpResult result; + RootedValue receiver(cx, ObjectValue(*proxy)); + if (!Proxy::set(cx, proxy, id, val, receiver, result)) + return false; + return result.checkStrictErrorOrWarning(cx, proxy, id, strict); +} + bool Proxy::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) { diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index 4e10ee0e0e2c..eef9d0c6f12c 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -79,7 +79,7 @@ proxy_Call(JSContext* cx, unsigned argc, Value* vp); bool proxy_Construct(JSContext* cx, unsigned argc, Value* vp); -// These two functions are used by JIT code +// These functions are used by JIT code bool ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp); @@ -88,6 +88,13 @@ bool ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal, MutableHandleValue vp); +bool +ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, HandleValue val, bool strict); + +bool +ProxySetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal, HandleValue val, + bool strict); + } /* namespace js */ #endif /* proxy_Proxy_h */