mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-09 10:20:59 +00:00
243 lines
10 KiB
C++
243 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2017-2020 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "GetterSetterAccessCase.h"
|
|
|
|
#if ENABLE(JIT)
|
|
|
|
#include "AccessCaseSnippetParams.h"
|
|
#include "DOMJITCallDOMGetterSnippet.h"
|
|
#include "DOMJITGetterSetter.h"
|
|
#include "JSCJSValueInlines.h"
|
|
#include "PolymorphicAccess.h"
|
|
#include "StructureStubInfo.h"
|
|
|
|
namespace JSC {
|
|
|
|
namespace GetterSetterAccessCaseInternal {
|
|
static constexpr bool verbose = false;
|
|
}
|
|
|
|
GetterSetterAccessCase::GetterSetterAccessCase(VM& vm, JSCell* owner, AccessType accessType, CacheableIdentifier identifier, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet, bool viaProxy, WatchpointSet* additionalSet, JSObject* customSlotBase, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
|
|
: Base(vm, owner, accessType, identifier, offset, structure, conditionSet, viaProxy, additionalSet, WTFMove(prototypeAccessChain))
|
|
{
|
|
m_customSlotBase.setMayBeNull(vm, owner, customSlotBase);
|
|
}
|
|
|
|
std::unique_ptr<AccessCase> GetterSetterAccessCase::create(
|
|
VM& vm, JSCell* owner, AccessType type, CacheableIdentifier identifier, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet,
|
|
bool viaProxy, WatchpointSet* additionalSet, FunctionPtr<CustomAccessorPtrTag> customGetter, JSObject* customSlotBase,
|
|
Optional<DOMAttributeAnnotation> domAttribute, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
|
|
{
|
|
switch (type) {
|
|
case Getter:
|
|
case CustomAccessorGetter:
|
|
case CustomValueGetter:
|
|
break;
|
|
default:
|
|
ASSERT_NOT_REACHED();
|
|
};
|
|
|
|
std::unique_ptr<GetterSetterAccessCase> result(new GetterSetterAccessCase(vm, owner, type, identifier, offset, structure, conditionSet, viaProxy, additionalSet, customSlotBase, WTFMove(prototypeAccessChain)));
|
|
result->m_domAttribute = domAttribute;
|
|
result->m_customAccessor = customGetter ? FunctionPtr<CustomAccessorPtrTag>(customGetter) : nullptr;
|
|
return result;
|
|
}
|
|
|
|
std::unique_ptr<AccessCase> GetterSetterAccessCase::create(VM& vm, JSCell* owner, AccessType type, Structure* structure, CacheableIdentifier identifier, PropertyOffset offset,
|
|
const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain, bool viaProxy,
|
|
FunctionPtr<CustomAccessorPtrTag> customSetter, JSObject* customSlotBase)
|
|
{
|
|
ASSERT(type == Setter || type == CustomValueSetter || type == CustomAccessorSetter);
|
|
std::unique_ptr<GetterSetterAccessCase> result(new GetterSetterAccessCase(vm, owner, type, identifier, offset, structure, conditionSet, viaProxy, nullptr, customSlotBase, WTFMove(prototypeAccessChain)));
|
|
result->m_customAccessor = customSetter ? FunctionPtr<CustomAccessorPtrTag>(customSetter) : nullptr;
|
|
return result;
|
|
}
|
|
|
|
|
|
GetterSetterAccessCase::~GetterSetterAccessCase()
|
|
{
|
|
}
|
|
|
|
|
|
GetterSetterAccessCase::GetterSetterAccessCase(const GetterSetterAccessCase& other)
|
|
: Base(other)
|
|
, m_customSlotBase(other.m_customSlotBase)
|
|
{
|
|
m_customAccessor = other.m_customAccessor;
|
|
m_domAttribute = other.m_domAttribute;
|
|
}
|
|
|
|
std::unique_ptr<AccessCase> GetterSetterAccessCase::clone() const
|
|
{
|
|
std::unique_ptr<GetterSetterAccessCase> result(new GetterSetterAccessCase(*this));
|
|
result->resetState();
|
|
return result;
|
|
}
|
|
|
|
bool GetterSetterAccessCase::hasAlternateBase() const
|
|
{
|
|
if (customSlotBase())
|
|
return true;
|
|
return Base::hasAlternateBase();
|
|
}
|
|
|
|
JSObject* GetterSetterAccessCase::alternateBase() const
|
|
{
|
|
if (customSlotBase())
|
|
return customSlotBase();
|
|
return Base::alternateBase();
|
|
}
|
|
|
|
void GetterSetterAccessCase::dumpImpl(PrintStream& out, CommaPrinter& comma) const
|
|
{
|
|
Base::dumpImpl(out, comma);
|
|
out.print(comma, "customSlotBase = ", RawPointer(customSlotBase()));
|
|
if (callLinkInfo())
|
|
out.print(comma, "callLinkInfo = ", RawPointer(callLinkInfo()));
|
|
out.print(comma, "customAccessor = ", RawPointer(m_customAccessor.executableAddress()));
|
|
}
|
|
|
|
void GetterSetterAccessCase::emitDOMJITGetter(AccessGenerationState& state, const DOMJIT::GetterSetter* domJIT, GPRReg baseForGetGPR)
|
|
{
|
|
CCallHelpers& jit = *state.jit;
|
|
StructureStubInfo& stubInfo = *state.stubInfo;
|
|
JSValueRegs valueRegs = state.valueRegs;
|
|
GPRReg baseGPR = state.baseGPR;
|
|
GPRReg scratchGPR = state.scratchGPR;
|
|
|
|
// We construct the environment that can execute the DOMJIT::Snippet here.
|
|
Ref<DOMJIT::CallDOMGetterSnippet> snippet = domJIT->compiler()();
|
|
|
|
Vector<GPRReg> gpScratch;
|
|
Vector<FPRReg> fpScratch;
|
|
Vector<SnippetParams::Value> regs;
|
|
|
|
ScratchRegisterAllocator allocator(stubInfo.usedRegisters);
|
|
allocator.lock(stubInfo.baseRegs());
|
|
allocator.lock(valueRegs);
|
|
allocator.lock(scratchGPR);
|
|
|
|
GPRReg paramBaseGPR = InvalidGPRReg;
|
|
GPRReg paramGlobalObjectGPR = InvalidGPRReg;
|
|
JSValueRegs paramValueRegs = valueRegs;
|
|
GPRReg remainingScratchGPR = InvalidGPRReg;
|
|
|
|
// valueRegs and baseForGetGPR may be the same. For example, in Baseline JIT, we pass the same regT0 for baseGPR and valueRegs.
|
|
// In FTL, there is no constraint that the baseForGetGPR interferes with the result. To make implementation simple in
|
|
// Snippet, Snippet assumes that result registers always early interfere with input registers, in this case,
|
|
// baseForGetGPR. So we move baseForGetGPR to the other register if baseForGetGPR == valueRegs.
|
|
if (baseForGetGPR != valueRegs.payloadGPR()) {
|
|
paramBaseGPR = baseForGetGPR;
|
|
if (!snippet->requireGlobalObject)
|
|
remainingScratchGPR = scratchGPR;
|
|
else
|
|
paramGlobalObjectGPR = scratchGPR;
|
|
} else {
|
|
jit.move(valueRegs.payloadGPR(), scratchGPR);
|
|
paramBaseGPR = scratchGPR;
|
|
if (snippet->requireGlobalObject)
|
|
paramGlobalObjectGPR = allocator.allocateScratchGPR();
|
|
}
|
|
|
|
JSGlobalObject* globalObjectForDOMJIT = structure()->globalObject();
|
|
|
|
regs.append(paramValueRegs);
|
|
regs.append(paramBaseGPR);
|
|
if (snippet->requireGlobalObject) {
|
|
ASSERT(paramGlobalObjectGPR != InvalidGPRReg);
|
|
regs.append(SnippetParams::Value(paramGlobalObjectGPR, globalObjectForDOMJIT));
|
|
}
|
|
|
|
if (snippet->numGPScratchRegisters) {
|
|
unsigned i = 0;
|
|
if (remainingScratchGPR != InvalidGPRReg) {
|
|
gpScratch.append(remainingScratchGPR);
|
|
++i;
|
|
}
|
|
for (; i < snippet->numGPScratchRegisters; ++i)
|
|
gpScratch.append(allocator.allocateScratchGPR());
|
|
}
|
|
|
|
for (unsigned i = 0; i < snippet->numFPScratchRegisters; ++i)
|
|
fpScratch.append(allocator.allocateScratchFPR());
|
|
|
|
// Let's store the reused registers to the stack. After that, we can use allocated scratch registers.
|
|
ScratchRegisterAllocator::PreservedState preservedState =
|
|
allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall);
|
|
|
|
if (GetterSetterAccessCaseInternal::verbose) {
|
|
dataLog("baseGPR = ", baseGPR, "\n");
|
|
dataLog("valueRegs = ", valueRegs, "\n");
|
|
dataLog("scratchGPR = ", scratchGPR, "\n");
|
|
dataLog("paramBaseGPR = ", paramBaseGPR, "\n");
|
|
if (paramGlobalObjectGPR != InvalidGPRReg)
|
|
dataLog("paramGlobalObjectGPR = ", paramGlobalObjectGPR, "\n");
|
|
dataLog("paramValueRegs = ", paramValueRegs, "\n");
|
|
for (unsigned i = 0; i < snippet->numGPScratchRegisters; ++i)
|
|
dataLog("gpScratch[", i, "] = ", gpScratch[i], "\n");
|
|
}
|
|
|
|
if (snippet->requireGlobalObject)
|
|
jit.move(CCallHelpers::TrustedImmPtr(globalObjectForDOMJIT), paramGlobalObjectGPR);
|
|
|
|
// We just spill the registers used in Snippet here. For not spilled registers here explicitly,
|
|
// they must be in the used register set passed by the callers (Baseline, DFG, and FTL) if they need to be kept.
|
|
// Some registers can be locked, but not in the used register set. For example, the caller could make baseGPR
|
|
// same to valueRegs, and not include it in the used registers since it will be changed.
|
|
RegisterSet registersToSpillForCCall;
|
|
for (auto& value : regs) {
|
|
SnippetReg reg = value.reg();
|
|
if (reg.isJSValueRegs())
|
|
registersToSpillForCCall.set(reg.jsValueRegs());
|
|
else if (reg.isGPR())
|
|
registersToSpillForCCall.set(reg.gpr());
|
|
else
|
|
registersToSpillForCCall.set(reg.fpr());
|
|
}
|
|
for (GPRReg reg : gpScratch)
|
|
registersToSpillForCCall.set(reg);
|
|
for (FPRReg reg : fpScratch)
|
|
registersToSpillForCCall.set(reg);
|
|
registersToSpillForCCall.exclude(RegisterSet::registersToNotSaveForCCall());
|
|
|
|
AccessCaseSnippetParams params(state.m_vm, WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
|
|
snippet->generator()->run(jit, params);
|
|
allocator.restoreReusedRegistersByPopping(jit, preservedState);
|
|
state.succeed();
|
|
|
|
CCallHelpers::JumpList exceptions = params.emitSlowPathCalls(state, registersToSpillForCCall, jit);
|
|
if (!exceptions.empty()) {
|
|
exceptions.link(&jit);
|
|
allocator.restoreReusedRegistersByPopping(jit, preservedState);
|
|
state.emitExplicitExceptionHandler();
|
|
}
|
|
}
|
|
|
|
} // namespace JSC
|
|
|
|
#endif // ENABLE(JIT)
|