mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2024-11-23 04:09:40 +00:00
370 lines
14 KiB
C++
370 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2015-2019 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 "JSObject.h"
|
|
#include <wtf/CompactPointerTuple.h>
|
|
#include <wtf/HashMap.h>
|
|
|
|
namespace JSC {
|
|
|
|
class TrackedReferences;
|
|
|
|
class PropertyCondition {
|
|
public:
|
|
enum Kind : uint8_t {
|
|
Presence,
|
|
Absence,
|
|
AbsenceOfSetEffect,
|
|
Equivalence, // An adaptive watchpoint on this will be a pair of watchpoints, and when the structure transitions, we will set the replacement watchpoint on the new structure.
|
|
HasStaticProperty, // Custom value or accessor.
|
|
HasPrototype
|
|
};
|
|
|
|
using Header = CompactPointerTuple<UniquedStringImpl*, Kind>;
|
|
|
|
PropertyCondition()
|
|
: m_header(nullptr, Presence)
|
|
{
|
|
memset(&u, 0, sizeof(u));
|
|
}
|
|
|
|
PropertyCondition(WTF::HashTableDeletedValueType)
|
|
: m_header(nullptr, Absence)
|
|
{
|
|
memset(&u, 0, sizeof(u));
|
|
}
|
|
|
|
static PropertyCondition presenceWithoutBarrier(UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
|
|
{
|
|
PropertyCondition result;
|
|
result.m_header = Header(uid, Presence);
|
|
result.u.presence.offset = offset;
|
|
result.u.presence.attributes = attributes;
|
|
return result;
|
|
}
|
|
|
|
static PropertyCondition presence(
|
|
VM&, JSCell*, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
|
|
{
|
|
return presenceWithoutBarrier(uid, offset, attributes);
|
|
}
|
|
|
|
// NOTE: The prototype is the storedPrototype not the prototypeForLookup.
|
|
static PropertyCondition absenceWithoutBarrier(UniquedStringImpl* uid, JSObject* prototype)
|
|
{
|
|
PropertyCondition result;
|
|
result.m_header = Header(uid, Absence);
|
|
result.u.prototype.prototype = prototype;
|
|
return result;
|
|
}
|
|
|
|
static PropertyCondition absence(
|
|
VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype)
|
|
{
|
|
if (owner)
|
|
vm.heap.writeBarrier(owner);
|
|
return absenceWithoutBarrier(uid, prototype);
|
|
}
|
|
|
|
static PropertyCondition absenceOfSetEffectWithoutBarrier(
|
|
UniquedStringImpl* uid, JSObject* prototype)
|
|
{
|
|
PropertyCondition result;
|
|
result.m_header = Header(uid, AbsenceOfSetEffect);
|
|
result.u.prototype.prototype = prototype;
|
|
return result;
|
|
}
|
|
|
|
static PropertyCondition absenceOfSetEffect(
|
|
VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype)
|
|
{
|
|
if (owner)
|
|
vm.heap.writeBarrier(owner);
|
|
return absenceOfSetEffectWithoutBarrier(uid, prototype);
|
|
}
|
|
|
|
static PropertyCondition equivalenceWithoutBarrier(
|
|
UniquedStringImpl* uid, JSValue value)
|
|
{
|
|
PropertyCondition result;
|
|
result.m_header = Header(uid, Equivalence);
|
|
result.u.equivalence.value = JSValue::encode(value);
|
|
return result;
|
|
}
|
|
|
|
static PropertyCondition equivalence(
|
|
VM& vm, JSCell* owner, UniquedStringImpl* uid, JSValue value)
|
|
{
|
|
if (value.isCell() && owner)
|
|
vm.heap.writeBarrier(owner);
|
|
return equivalenceWithoutBarrier(uid, value);
|
|
}
|
|
|
|
static PropertyCondition hasStaticProperty(UniquedStringImpl* uid)
|
|
{
|
|
PropertyCondition result;
|
|
result.m_header = Header(uid, HasStaticProperty);
|
|
return result;
|
|
}
|
|
|
|
static PropertyCondition hasPrototypeWithoutBarrier(JSObject* prototype)
|
|
{
|
|
PropertyCondition result;
|
|
result.m_header = Header(nullptr, HasPrototype);
|
|
result.u.prototype.prototype = prototype;
|
|
return result;
|
|
}
|
|
|
|
static PropertyCondition hasPrototype(VM& vm, JSCell* owner, JSObject* prototype)
|
|
{
|
|
if (owner)
|
|
vm.heap.writeBarrier(owner);
|
|
return hasPrototypeWithoutBarrier(prototype);
|
|
}
|
|
|
|
explicit operator bool() const { return m_header.pointer() || m_header.type() != Presence; }
|
|
|
|
Kind kind() const { return m_header.type(); }
|
|
UniquedStringImpl* uid() const { return m_header.pointer(); }
|
|
|
|
bool hasOffset() const { return !!*this && m_header.type() == Presence; };
|
|
PropertyOffset offset() const
|
|
{
|
|
ASSERT(hasOffset());
|
|
return u.presence.offset;
|
|
}
|
|
bool hasAttributes() const { return !!*this && m_header.type() == Presence; };
|
|
unsigned attributes() const
|
|
{
|
|
ASSERT(hasAttributes());
|
|
return u.presence.attributes;
|
|
}
|
|
|
|
bool hasPrototype() const
|
|
{
|
|
return !!*this
|
|
&& (m_header.type() == Absence || m_header.type() == AbsenceOfSetEffect || m_header.type() == HasPrototype);
|
|
}
|
|
JSObject* prototype() const
|
|
{
|
|
ASSERT(hasPrototype());
|
|
return u.prototype.prototype;
|
|
}
|
|
|
|
bool hasRequiredValue() const { return !!*this && m_header.type() == Equivalence; }
|
|
JSValue requiredValue() const
|
|
{
|
|
ASSERT(hasRequiredValue());
|
|
return JSValue::decode(u.equivalence.value);
|
|
}
|
|
|
|
void dumpInContext(PrintStream&, DumpContext*) const;
|
|
void dump(PrintStream&) const;
|
|
|
|
unsigned hash() const
|
|
{
|
|
unsigned result = WTF::PtrHash<UniquedStringImpl*>::hash(m_header.pointer()) + static_cast<unsigned>(m_header.type());
|
|
switch (m_header.type()) {
|
|
case Presence:
|
|
result ^= u.presence.offset;
|
|
result ^= u.presence.attributes;
|
|
break;
|
|
case Absence:
|
|
case AbsenceOfSetEffect:
|
|
case HasPrototype:
|
|
result ^= WTF::PtrHash<JSObject*>::hash(u.prototype.prototype);
|
|
break;
|
|
case Equivalence:
|
|
result ^= EncodedJSValueHash::hash(u.equivalence.value);
|
|
break;
|
|
case HasStaticProperty:
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool operator==(const PropertyCondition& other) const
|
|
{
|
|
if (m_header.pointer() != other.m_header.pointer())
|
|
return false;
|
|
if (m_header.type() != other.m_header.type())
|
|
return false;
|
|
switch (m_header.type()) {
|
|
case Presence:
|
|
return u.presence.offset == other.u.presence.offset
|
|
&& u.presence.attributes == other.u.presence.attributes;
|
|
case Absence:
|
|
case AbsenceOfSetEffect:
|
|
case HasPrototype:
|
|
return u.prototype.prototype == other.u.prototype.prototype;
|
|
case Equivalence:
|
|
return u.equivalence.value == other.u.equivalence.value;
|
|
case HasStaticProperty:
|
|
return true;
|
|
}
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
return false;
|
|
}
|
|
|
|
bool isHashTableDeletedValue() const
|
|
{
|
|
return !m_header.pointer() && m_header.type() == Absence;
|
|
}
|
|
|
|
// Two conditions are compatible if they are identical or if they speak of different uids. If
|
|
// false is returned, you have to decide how to resolve the conflict - for example if there is
|
|
// a Presence and an Equivalence then in some cases you'll want the more general of the two
|
|
// while in other cases you'll want the more specific of the two. This will also return false
|
|
// for contradictions, like Presence and Absence on the same uid. By convention, invalid
|
|
// conditions aren't compatible with anything.
|
|
bool isCompatibleWith(const PropertyCondition& other) const
|
|
{
|
|
if (!*this || !other)
|
|
return false;
|
|
return *this == other || uid() != other.uid();
|
|
}
|
|
|
|
// Checks if the object's structure claims that the property won't be intercepted.
|
|
bool isStillValidAssumingImpurePropertyWatchpoint(Structure*, JSObject* base = nullptr) const;
|
|
|
|
// Returns true if we need an impure property watchpoint to ensure validity even if
|
|
// isStillValidAccordingToStructure() returned true.
|
|
bool validityRequiresImpurePropertyWatchpoint(Structure*) const;
|
|
|
|
// Checks if the condition is still valid right now for the given object and structure.
|
|
// May conservatively return false, if the object and structure alone don't guarantee the
|
|
// condition. This happens for an Absence condition on an object that may have impure
|
|
// properties. If the object is not supplied, then a "true" return indicates that checking if
|
|
// an object has the given structure guarantees the condition still holds. If an object is
|
|
// supplied, then you may need to use some other watchpoints on the object to guarantee the
|
|
// condition in addition to the structure check.
|
|
bool isStillValid(Structure*, JSObject* base = nullptr) const;
|
|
|
|
// In some cases, the condition is not watchable, but could be made watchable by enabling the
|
|
// appropriate watchpoint. For example, replacement watchpoints are enabled only when some
|
|
// access is cached on the property in some structure. This is mainly to save space for
|
|
// dictionary properties or properties that never get very hot. But, it's always safe to
|
|
// enable watching, provided that this is called from the main thread.
|
|
enum WatchabilityEffort {
|
|
// This is the default. It means that we don't change the state of any Structure or
|
|
// object, and implies that if the property happens not to be watchable then we don't make
|
|
// it watchable. This is mandatory if calling from a JIT thread. This is also somewhat
|
|
// preferable when first deciding whether to watch a condition for the first time (i.e.
|
|
// not from a watchpoint fire that causes us to see if we should adapt), since a
|
|
// watchpoint not being initialized for watching implies that maybe we don't know enough
|
|
// yet to make it profitable to watch -- as in, the thing being watched may not have
|
|
// stabilized yet. We prefer to only assume that a condition will hold if it has been
|
|
// known to hold for a while already.
|
|
MakeNoChanges,
|
|
|
|
// Do what it takes to ensure that the property can be watched, if doing so has no
|
|
// user-observable effect. For now this just means that we will ensure that a property
|
|
// replacement watchpoint is enabled if it hadn't been enabled already. Do not use this
|
|
// from JIT threads, since the act of enabling watchpoints is not thread-safe.
|
|
EnsureWatchability
|
|
};
|
|
|
|
// This means that it's still valid and we could enforce validity by setting a transition
|
|
// watchpoint on the structure and possibly an impure property watchpoint.
|
|
bool isWatchableAssumingImpurePropertyWatchpoint(
|
|
Structure*, JSObject* base, WatchabilityEffort = MakeNoChanges) const;
|
|
|
|
// This means that it's still valid and we could enforce validity by setting a transition
|
|
// watchpoint on the structure.
|
|
bool isWatchable(
|
|
Structure*, JSObject*, WatchabilityEffort = MakeNoChanges) const;
|
|
|
|
bool watchingRequiresStructureTransitionWatchpoint() const
|
|
{
|
|
// Currently, this is required for all of our conditions.
|
|
return !!*this;
|
|
}
|
|
bool watchingRequiresReplacementWatchpoint() const
|
|
{
|
|
return !!*this && m_header.type() == Equivalence;
|
|
}
|
|
|
|
template<typename Functor>
|
|
void forEachDependentCell(const Functor& functor) const
|
|
{
|
|
if (hasPrototype() && prototype())
|
|
functor(prototype());
|
|
|
|
if (hasRequiredValue() && requiredValue() && requiredValue().isCell())
|
|
functor(requiredValue().asCell());
|
|
}
|
|
|
|
void validateReferences(const TrackedReferences&) const;
|
|
|
|
static bool isValidValueForAttributes(VM&, JSValue, unsigned attributes);
|
|
|
|
bool isValidValueForPresence(VM&, JSValue) const;
|
|
|
|
PropertyCondition attemptToMakeEquivalenceWithoutBarrier(VM&, JSObject* base) const;
|
|
|
|
private:
|
|
bool isWatchableWhenValid(Structure*, WatchabilityEffort) const;
|
|
|
|
Header m_header;
|
|
union {
|
|
struct {
|
|
PropertyOffset offset;
|
|
unsigned attributes;
|
|
} presence;
|
|
struct {
|
|
JSObject* prototype;
|
|
} prototype;
|
|
struct {
|
|
EncodedJSValue value;
|
|
} equivalence;
|
|
} u;
|
|
};
|
|
|
|
struct PropertyConditionHash {
|
|
static unsigned hash(const PropertyCondition& key) { return key.hash(); }
|
|
static bool equal(
|
|
const PropertyCondition& a, const PropertyCondition& b)
|
|
{
|
|
return a == b;
|
|
}
|
|
static constexpr bool safeToCompareToEmptyOrDeleted = true;
|
|
};
|
|
|
|
} // namespace JSC
|
|
|
|
namespace WTF {
|
|
|
|
void printInternal(PrintStream&, JSC::PropertyCondition::Kind);
|
|
|
|
template<typename T> struct DefaultHash;
|
|
template<> struct DefaultHash<JSC::PropertyCondition> : JSC::PropertyConditionHash { };
|
|
|
|
template<typename T> struct HashTraits;
|
|
template<> struct HashTraits<JSC::PropertyCondition> : SimpleClassHashTraits<JSC::PropertyCondition> { };
|
|
|
|
} // namespace WTF
|