blob: 377e07ba2ba4c9bae3686762e8dce30258fbcd5f [file] [log] [blame]
/*
* Copyright (C) 2015-2016 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 "PropertyCondition.h"
#include <wtf/HashMap.h>
namespace JSC {
class TrackedReferences;
class ObjectPropertyCondition {
public:
ObjectPropertyCondition()
: m_object(nullptr)
{
}
ObjectPropertyCondition(WTF::HashTableDeletedValueType token)
: m_object(nullptr)
, m_condition(token)
{
}
ObjectPropertyCondition(JSObject* object, const PropertyCondition& condition)
: m_object(object)
, m_condition(condition)
{
}
static ObjectPropertyCondition presenceWithoutBarrier(
JSObject* object, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
{
ObjectPropertyCondition result;
result.m_object = object;
result.m_condition = PropertyCondition::presenceWithoutBarrier(uid, offset, attributes);
return result;
}
static ObjectPropertyCondition presence(
VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyOffset offset,
unsigned attributes)
{
if (owner)
vm.heap.writeBarrier(owner);
return presenceWithoutBarrier(object, uid, offset, attributes);
}
// NOTE: The prototype is the storedPrototype, not the prototypeForLookup.
static ObjectPropertyCondition absenceWithoutBarrier(
JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
{
ObjectPropertyCondition result;
result.m_object = object;
result.m_condition = PropertyCondition::absenceWithoutBarrier(uid, prototype);
return result;
}
static ObjectPropertyCondition absence(
VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
{
if (owner)
vm.heap.writeBarrier(owner);
return absenceWithoutBarrier(object, uid, prototype);
}
static ObjectPropertyCondition absenceOfSetterWithoutBarrier(
JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
{
ObjectPropertyCondition result;
result.m_object = object;
result.m_condition = PropertyCondition::absenceOfSetterWithoutBarrier(uid, prototype);
return result;
}
static ObjectPropertyCondition absenceOfSetter(
VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
{
if (owner)
vm.heap.writeBarrier(owner);
return absenceOfSetterWithoutBarrier(object, uid, prototype);
}
static ObjectPropertyCondition equivalenceWithoutBarrier(
JSObject* object, UniquedStringImpl* uid, JSValue value)
{
ObjectPropertyCondition result;
result.m_object = object;
result.m_condition = PropertyCondition::equivalenceWithoutBarrier(uid, value);
return result;
}
static ObjectPropertyCondition equivalence(
VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSValue value)
{
if (owner)
vm.heap.writeBarrier(owner);
return equivalenceWithoutBarrier(object, uid, value);
}
explicit operator bool() const { return !!m_condition; }
JSObject* object() const { return m_object; }
PropertyCondition condition() const { return m_condition; }
PropertyCondition::Kind kind() const { return condition().kind(); }
UniquedStringImpl* uid() const { return condition().uid(); }
bool hasOffset() const { return condition().hasOffset(); }
PropertyOffset offset() const { return condition().offset(); }
unsigned hasAttributes() const { return condition().hasAttributes(); }
unsigned attributes() const { return condition().attributes(); }
bool hasPrototype() const { return condition().hasPrototype(); }
JSObject* prototype() const { return condition().prototype(); }
bool hasRequiredValue() const { return condition().hasRequiredValue(); }
JSValue requiredValue() const { return condition().requiredValue(); }
void dumpInContext(PrintStream&, DumpContext*) const;
void dump(PrintStream&) const;
unsigned hash() const
{
return WTF::PtrHash<JSObject*>::hash(m_object) ^ m_condition.hash();
}
bool operator==(const ObjectPropertyCondition& other) const
{
return m_object == other.m_object
&& m_condition == other.m_condition;
}
bool isHashTableDeletedValue() const
{
return !m_object && m_condition.isHashTableDeletedValue();
}
// Two conditions are compatible if they are identical or if they speak of different uids or
// different objects. 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
// object/uid. By convention, invalid conditions aren't compatible with anything.
bool isCompatibleWith(const ObjectPropertyCondition& other) const
{
if (!*this || !other)
return false;
return *this == other || uid() != other.uid() || object() != other.object();
}
// These validity-checking methods can optionally take a Struture* instead of loading the
// Structure* from the object. If you're in the concurrent JIT, then you must use the forms
// that take an explicit Structure* because you want the compiler to optimize for the same
// structure that you validated (i.e. avoid a TOCTOU race).
// Checks if the object's structure claims that the property won't be intercepted. Validity
// does not require watchpoints on the object.
bool structureEnsuresValidityAssumingImpurePropertyWatchpoint(Structure*) const;
bool structureEnsuresValidityAssumingImpurePropertyWatchpoint() const;
// Returns true if we need an impure property watchpoint to ensure validity even if
// isStillValidAccordingToStructure() returned true.
bool validityRequiresImpurePropertyWatchpoint(Structure*) const;
bool validityRequiresImpurePropertyWatchpoint() const;
// Checks if the condition still holds setting aside the need for an impure property watchpoint.
// Validity might still require watchpoints on the object.
bool isStillValidAssumingImpurePropertyWatchpoint(Structure*) const;
bool isStillValidAssumingImpurePropertyWatchpoint() const;
// Checks if the condition still holds. May conservatively return false, if the object and
// structure alone don't guarantee the condition. Note that this may return true if the
// condition still requires some watchpoints on the object in addition to checking the
// structure. If you want to check if the condition holds by using the structure alone,
// use structureEnsuresValidity().
bool isStillValid(Structure*) const;
bool isStillValid() const;
// Shorthand for condition().isStillValid(structure).
bool structureEnsuresValidity(Structure*) const;
bool structureEnsuresValidity() const;
// 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*,
PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
bool isWatchableAssumingImpurePropertyWatchpoint(
PropertyCondition::WatchabilityEffort = PropertyCondition::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*,
PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
bool isWatchable(
PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
bool watchingRequiresStructureTransitionWatchpoint() const
{
return condition().watchingRequiresStructureTransitionWatchpoint();
}
bool watchingRequiresReplacementWatchpoint() const
{
return condition().watchingRequiresReplacementWatchpoint();
}
// This means that the objects involved in this are still live.
bool isStillLive() const;
void validateReferences(const TrackedReferences&) const;
bool isValidValueForPresence(VM& vm, JSValue value) const
{
return condition().isValidValueForPresence(vm, value);
}
ObjectPropertyCondition attemptToMakeEquivalenceWithoutBarrier(VM&) const;
private:
JSObject* m_object;
PropertyCondition m_condition;
};
struct ObjectPropertyConditionHash {
static unsigned hash(const ObjectPropertyCondition& key) { return key.hash(); }
static bool equal(
const ObjectPropertyCondition& a, const ObjectPropertyCondition& b)
{
return a == b;
}
static const bool safeToCompareToEmptyOrDeleted = true;
};
} // namespace JSC
namespace WTF {
template<typename T> struct DefaultHash;
template<> struct DefaultHash<JSC::ObjectPropertyCondition> {
typedef JSC::ObjectPropertyConditionHash Hash;
};
template<typename T> struct HashTraits;
template<> struct HashTraits<JSC::ObjectPropertyCondition> : SimpleClassHashTraits<JSC::ObjectPropertyCondition> { };
} // namespace WTF