blob: 371c19c79cb3ab1ae7ef2f428343d3e7adeee4a7 [file] [log] [blame]
/*
* Copyright (C) 2017-2021 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.
*/
#pragma once
#include <wtf/Platform.h>
#if ENABLE(JIT)
#include "CacheableIdentifier.h"
#include "GCAwareJITStubRoutine.h"
#include "JITStubRoutine.h"
#include "JSFunctionInlines.h"
#include "ObjectPropertyConditionSet.h"
#include "PolyProtoAccessChain.h"
#include <wtf/CommaPrinter.h>
namespace JSC {
class GetterSetterAccessCase;
class InstanceOfAccessCase;
class IntrinsicGetterAccessCase;
class ModuleNamespaceAccessCase;
class ProxyableAccessCase;
class InlineCacheCompiler;
class InlineCacheHandler;
DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(AccessCase);
// An AccessCase describes one of the cases of a PolymorphicAccess. A PolymorphicAccess represents a
// planned (to generate in future) or generated stub for some inline cache. That stub contains fast
// path code for some finite number of fast cases, each described by an AccessCase object.
#define JSC_FOR_EACH_ACCESS_TYPE(macro) \
macro(Load) \
macro(LoadMegamorphic) \
macro(Transition) \
macro(StoreMegamorphic) \
macro(Delete) \
macro(DeleteNonConfigurable) \
macro(DeleteMiss) \
macro(Replace) \
macro(Miss) \
macro(GetGetter) \
macro(Getter) \
macro(Setter) \
macro(CustomValueGetter) \
macro(CustomAccessorGetter) \
macro(CustomValueSetter) \
macro(CustomAccessorSetter) \
macro(IntrinsicGetter) \
macro(InHit) \
macro(InMiss) \
macro(InMegamorphic) \
macro(ArrayLength) \
macro(StringLength) \
macro(DirectArgumentsLength) \
macro(ScopedArgumentsLength) \
macro(ModuleNamespaceLoad) \
macro(ProxyObjectIn) \
macro(ProxyObjectLoad) \
macro(ProxyObjectStore) \
macro(InstanceOfHit) \
macro(InstanceOfMiss) \
macro(InstanceOfMegamorphic) \
macro(CheckPrivateBrand) \
macro(SetPrivateBrand) \
macro(IndexedProxyObjectLoad) \
macro(IndexedMegamorphicLoad) \
macro(IndexedInt32Load) \
macro(IndexedDoubleLoad) \
macro(IndexedContiguousLoad) \
macro(IndexedArrayStorageLoad) \
macro(IndexedScopedArgumentsLoad) \
macro(IndexedDirectArgumentsLoad) \
macro(IndexedTypedArrayInt8Load) \
macro(IndexedTypedArrayUint8Load) \
macro(IndexedTypedArrayUint8ClampedLoad) \
macro(IndexedTypedArrayInt16Load) \
macro(IndexedTypedArrayUint16Load) \
macro(IndexedTypedArrayInt32Load) \
macro(IndexedTypedArrayUint32Load) \
macro(IndexedTypedArrayFloat16Load) \
macro(IndexedTypedArrayFloat32Load) \
macro(IndexedTypedArrayFloat64Load) \
macro(IndexedResizableTypedArrayInt8Load) \
macro(IndexedResizableTypedArrayUint8Load) \
macro(IndexedResizableTypedArrayUint8ClampedLoad) \
macro(IndexedResizableTypedArrayInt16Load) \
macro(IndexedResizableTypedArrayUint16Load) \
macro(IndexedResizableTypedArrayInt32Load) \
macro(IndexedResizableTypedArrayUint32Load) \
macro(IndexedResizableTypedArrayFloat16Load) \
macro(IndexedResizableTypedArrayFloat32Load) \
macro(IndexedResizableTypedArrayFloat64Load) \
macro(IndexedStringLoad) \
macro(IndexedNoIndexingMiss) \
macro(IndexedProxyObjectStore) \
macro(IndexedMegamorphicStore) \
macro(IndexedInt32Store) \
macro(IndexedDoubleStore) \
macro(IndexedContiguousStore) \
macro(IndexedArrayStorageStore) \
macro(IndexedTypedArrayInt8Store) \
macro(IndexedTypedArrayUint8Store) \
macro(IndexedTypedArrayUint8ClampedStore) \
macro(IndexedTypedArrayInt16Store) \
macro(IndexedTypedArrayUint16Store) \
macro(IndexedTypedArrayInt32Store) \
macro(IndexedTypedArrayUint32Store) \
macro(IndexedTypedArrayFloat16Store) \
macro(IndexedTypedArrayFloat32Store) \
macro(IndexedTypedArrayFloat64Store) \
macro(IndexedResizableTypedArrayInt8Store) \
macro(IndexedResizableTypedArrayUint8Store) \
macro(IndexedResizableTypedArrayUint8ClampedStore) \
macro(IndexedResizableTypedArrayInt16Store) \
macro(IndexedResizableTypedArrayUint16Store) \
macro(IndexedResizableTypedArrayInt32Store) \
macro(IndexedResizableTypedArrayUint32Store) \
macro(IndexedResizableTypedArrayFloat16Store) \
macro(IndexedResizableTypedArrayFloat32Store) \
macro(IndexedResizableTypedArrayFloat64Store) \
macro(IndexedInt32InHit) \
macro(IndexedDoubleInHit) \
macro(IndexedContiguousInHit) \
macro(IndexedArrayStorageInHit) \
macro(IndexedScopedArgumentsInHit) \
macro(IndexedDirectArgumentsInHit) \
macro(IndexedTypedArrayInt8In) \
macro(IndexedTypedArrayUint8In) \
macro(IndexedTypedArrayUint8ClampedIn) \
macro(IndexedTypedArrayInt16In) \
macro(IndexedTypedArrayUint16In) \
macro(IndexedTypedArrayInt32In) \
macro(IndexedTypedArrayUint32In) \
macro(IndexedTypedArrayFloat16In) \
macro(IndexedTypedArrayFloat32In) \
macro(IndexedTypedArrayFloat64In) \
macro(IndexedResizableTypedArrayInt8In) \
macro(IndexedResizableTypedArrayUint8In) \
macro(IndexedResizableTypedArrayUint8ClampedIn) \
macro(IndexedResizableTypedArrayInt16In) \
macro(IndexedResizableTypedArrayUint16In) \
macro(IndexedResizableTypedArrayInt32In) \
macro(IndexedResizableTypedArrayUint32In) \
macro(IndexedResizableTypedArrayFloat16In) \
macro(IndexedResizableTypedArrayFloat32In) \
macro(IndexedResizableTypedArrayFloat64In) \
macro(IndexedStringInHit) \
macro(IndexedNoIndexingInMiss) \
macro(IndexedProxyObjectIn) \
macro(IndexedMegamorphicIn) \
class AccessCase : public ThreadSafeRefCounted<AccessCase> {
WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(AccessCase, AccessCase);
public:
friend class InlineCacheCompiler;
enum AccessType : uint8_t {
#define JSC_DEFINE_ACCESS_TYPE(name) name,
JSC_FOR_EACH_ACCESS_TYPE(JSC_DEFINE_ACCESS_TYPE)
#undef JSC_DEFINE_ACCESS_TYPE
};
template<typename T>
T& as() { return *static_cast<T*>(this); }
template<typename T>
const T& as() const { return *static_cast<const T*>(this); }
static Ref<AccessCase> create(VM&, JSCell* owner, AccessType, CacheableIdentifier, PropertyOffset = invalidOffset,
Structure* = nullptr, const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(), RefPtr<PolyProtoAccessChain>&& = nullptr);
static RefPtr<AccessCase> createTransition(VM&, JSCell* owner, CacheableIdentifier, PropertyOffset, Structure* oldStructure,
Structure* newStructure, const ObjectPropertyConditionSet&, RefPtr<PolyProtoAccessChain>&&, const StructureStubInfo&);
static Ref<AccessCase> createDelete(VM&, JSCell* owner, CacheableIdentifier, PropertyOffset, Structure* oldStructure, Structure* newStructure);
static Ref<AccessCase> createCheckPrivateBrand(VM&, JSCell* owner, CacheableIdentifier, Structure*);
static Ref<AccessCase> createSetPrivateBrand(VM&, JSCell* owner, CacheableIdentifier, Structure* oldStructure, Structure* newStructure);
static Ref<AccessCase> createReplace(VM&, JSCell* owner, CacheableIdentifier, PropertyOffset, Structure* oldStructure, bool viaGlobalProxy);
static RefPtr<AccessCase> fromStructureStubInfo(VM&, JSCell* owner, CacheableIdentifier, StructureStubInfo&);
AccessType type() const { return m_type; }
PropertyOffset offset() const { return m_offset; }
Structure* structure() const
{
if (m_type == Transition || m_type == Delete || m_type == SetPrivateBrand)
return m_structureID->previousID();
return m_structureID.get();
}
StructureID structureID() const
{
if (auto* result = structure())
return result->id();
return StructureID();
}
Structure* newStructure() const
{
ASSERT(m_type == Transition || m_type == Delete || m_type == SetPrivateBrand);
return m_structureID.get();
}
StructureID newStructureID() const
{
ASSERT(m_type == Transition || m_type == Delete || m_type == SetPrivateBrand);
return m_structureID.value();
}
const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
JSObject* tryGetAlternateBase() const;
WatchpointSet* additionalSet() const;
bool viaGlobalProxy() const { return m_viaGlobalProxy; }
bool doesCalls(VM&) const;
bool isCustom() const
{
switch (type()) {
case CustomValueGetter:
case CustomAccessorGetter:
case CustomValueSetter:
case CustomAccessorSetter:
return true;
default:
return false;
}
}
bool isGetter() const
{
switch (type()) {
case Getter:
case CustomValueGetter:
case CustomAccessorGetter:
return true;
default:
return false;
}
}
bool isAccessor() const { return isGetter() || type() == Setter; }
// Is it still possible for this case to ever be taken? Must call this as a prerequisite for
// calling generate() and friends. If this returns true, then you can call generate(). If
// this returns false, then generate() will crash. You must call generate() in the same epoch
// as when you called couldStillSucceed().
bool couldStillSucceed() const;
// If this method returns true, then it's a good idea to remove 'other' from the access once 'this'
// is added. This method assumes that in case of contradictions, 'this' represents a newer, and so
// more useful, truth. This method can be conservative; it will return false when it doubt.
bool canReplace(const AccessCase& other) const;
void dump(PrintStream& out) const;
PolyProtoAccessChain* polyProtoAccessChain() const { return m_polyProtoAccessChain.get(); }
bool usesPolyProto() const
{
return !!m_polyProtoAccessChain;
}
bool requiresIdentifierNameMatch() const;
bool requiresInt32PropertyCheck() const;
UniquedStringImpl* uid() const { return m_identifier.uid(); }
CacheableIdentifier identifier() const { return m_identifier; }
#if ASSERT_ENABLED
void checkConsistency(StructureStubInfo&);
#else
ALWAYS_INLINE void checkConsistency(StructureStubInfo&) { }
#endif
unsigned hash() const
{
return computeHash(m_conditionSet.hash(), static_cast<unsigned>(m_type), m_viaGlobalProxy, m_structureID.unvalidatedGet(), m_offset);
}
static bool canBeShared(const AccessCase&, const AccessCase&);
void collectDependentCells(VM&, Vector<JSCell*>&) const;
template<typename Func>
void runWithDowncast(const Func&);
void operator delete(AccessCase*, std::destroying_delete_t);
protected:
AccessCase(VM&, JSCell* owner, AccessType, CacheableIdentifier, PropertyOffset, Structure*, const ObjectPropertyConditionSet&, RefPtr<PolyProtoAccessChain>&&);
AccessCase(AccessCase&& other)
: m_type(WTFMove(other.m_type))
, m_viaGlobalProxy(WTFMove(other.m_viaGlobalProxy))
, m_offset(WTFMove(other.m_offset))
, m_structureID(WTFMove(other.m_structureID))
, m_conditionSet(WTFMove(other.m_conditionSet))
, m_polyProtoAccessChain(WTFMove(other.m_polyProtoAccessChain))
, m_identifier(WTFMove(other.m_identifier))
{ }
AccessCase(const AccessCase& other)
: m_type(other.m_type)
, m_viaGlobalProxy(other.m_viaGlobalProxy)
, m_offset(other.m_offset)
, m_structureID(other.m_structureID)
, m_conditionSet(other.m_conditionSet)
, m_polyProtoAccessChain(other.m_polyProtoAccessChain)
, m_identifier(other.m_identifier)
{ }
AccessCase& operator=(const AccessCase&) = delete;
WatchpointSet* additionalSetImpl() const { return nullptr; }
JSObject* tryGetAlternateBaseImpl() const;
void dumpImpl(PrintStream&, CommaPrinter&, Indenter&) const { }
bool guardedByStructureCheckSkippingConstantIdentifierCheck() const;
private:
friend class CodeBlock;
friend class PolymorphicAccess;
friend class ProxyableAccessCase;
friend class GetterSetterAccessCase;
friend class IntrinsicGetterAccessCase;
friend class ModuleNamespaceAccessCase;
friend class InstanceOfAccessCase;
template<typename Functor>
void forEachDependentCell(VM&, const Functor&) const;
DECLARE_VISIT_AGGREGATE_WITH_MODIFIER(const);
bool visitWeak(VM&) const;
template<typename Visitor> void propagateTransitions(Visitor&) const;
AccessType m_type;
protected:
// m_viaGlobalProxy is true only if the instance inherits (or it is) ProxyableAccessCase.
// We put this value here instead of ProxyableAccessCase to reduce the size of ProxyableAccessCase and its
// derived classes, which are super frequently allocated.
bool m_viaGlobalProxy { false };
private:
PropertyOffset m_offset;
// Usually this is the structure that we expect the base object to have. But, this is the *new*
// structure for a transition and we rely on the fact that it has a strong reference to the old
// structure. For proxies, this is the structure of the object behind the proxy.
WriteBarrierStructureID m_structureID;
ObjectPropertyConditionSet m_conditionSet;
RefPtr<PolyProtoAccessChain> m_polyProtoAccessChain;
CacheableIdentifier m_identifier;
};
} // namespace JSC
namespace WTF {
template<typename T> struct DefaultHash;
template<> struct DefaultHash<JSC::AccessCase::AccessType> : public IntHash<JSC::AccessCase::AccessType> { };
template<typename T> struct HashTraits;
template<> struct HashTraits<JSC::AccessCase::AccessType> : public StrongEnumHashTraits<JSC::AccessCase::AccessType> { };
} // namespace WTF
#endif