blob: e9b94b46af94f0e438eff01bb75afd10d8374f67 [file] [log] [blame]
/*
* Copyright (C) 2013-2017 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. AND ITS CONTRIBUTORS ``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 ITS 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 "StructureRareData.h"
#include "AdaptiveInferredPropertyValueWatchpointBase.h"
#include "JSImmutableButterfly.h"
#include "JSPropertyNameEnumerator.h"
#include "JSString.h"
#include "JSCInlines.h"
#include "ObjectPropertyConditionSet.h"
#include "ObjectToStringAdaptiveStructureWatchpoint.h"
namespace JSC {
const ClassInfo StructureRareData::s_info = { "StructureRareData", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(StructureRareData) };
Structure* StructureRareData::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
}
StructureRareData* StructureRareData::create(VM& vm, Structure* previous)
{
StructureRareData* rareData = new (NotNull, allocateCell<StructureRareData>(vm.heap)) StructureRareData(vm, previous);
rareData->finishCreation(vm);
return rareData;
}
void StructureRareData::destroy(JSCell* cell)
{
static_cast<StructureRareData*>(cell)->StructureRareData::~StructureRareData();
}
StructureRareData::StructureRareData(VM& vm, Structure* previous)
: JSCell(vm, vm.structureRareDataStructure.get())
, m_giveUpOnObjectToStringValueCache(false)
{
if (previous)
m_previous.set(vm, this, previous);
}
void StructureRareData::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
StructureRareData* thisObject = jsCast<StructureRareData*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
visitor.append(thisObject->m_previous);
visitor.append(thisObject->m_objectToStringValue);
visitor.append(thisObject->m_cachedPropertyNameEnumerator);
auto* cachedOwnKeys = thisObject->m_cachedOwnKeys.unvalidatedGet();
if (cachedOwnKeys != cachedOwnKeysSentinel())
visitor.appendUnbarriered(cachedOwnKeys);
}
// ----------- Object.prototype.toString() helper watchpoint classes -----------
class ObjectToStringAdaptiveInferredPropertyValueWatchpoint final : public AdaptiveInferredPropertyValueWatchpointBase {
public:
typedef AdaptiveInferredPropertyValueWatchpointBase Base;
ObjectToStringAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition&, StructureRareData*);
private:
bool isValid() const override;
void handleFire(VM&, const FireDetail&) override;
StructureRareData* m_structureRareData;
};
void StructureRareData::setObjectToStringValue(ExecState* exec, VM& vm, Structure* ownStructure, JSString* value, PropertySlot toStringTagSymbolSlot)
{
if (m_giveUpOnObjectToStringValueCache)
return;
ObjectPropertyConditionSet conditionSet;
if (toStringTagSymbolSlot.isValue()) {
// We don't handle the own property case of Symbol.toStringTag because we would never know if a new
// object transitioning to the same structure had the same value stored in Symbol.toStringTag.
// Additionally, this is a super unlikely case anyway.
if (!toStringTagSymbolSlot.isCacheable() || toStringTagSymbolSlot.slotBase()->structure(vm) == ownStructure)
return;
// This will not create a condition for the current structure but that is good because we know the Symbol.toStringTag
// is not on the ownStructure so we will transisition if one is added and this cache will no longer be used.
preparePrototypeChainForCaching(exec->lexicalGlobalObject(), ownStructure, toStringTagSymbolSlot.slotBase());
conditionSet = generateConditionsForPrototypePropertyHit(vm, this, exec, ownStructure, toStringTagSymbolSlot.slotBase(), vm.propertyNames->toStringTagSymbol.impl());
ASSERT(!conditionSet.isValid() || conditionSet.hasOneSlotBaseCondition());
} else if (toStringTagSymbolSlot.isUnset()) {
preparePrototypeChainForCaching(exec->lexicalGlobalObject(), ownStructure, nullptr);
conditionSet = generateConditionsForPropertyMiss(vm, this, exec, ownStructure, vm.propertyNames->toStringTagSymbol.impl());
} else
return;
if (!conditionSet.isValid()) {
m_giveUpOnObjectToStringValueCache = true;
return;
}
ObjectPropertyCondition equivCondition;
for (const ObjectPropertyCondition& condition : conditionSet) {
if (condition.condition().kind() == PropertyCondition::Presence) {
ASSERT(isValidOffset(condition.offset()));
condition.object()->structure(vm)->startWatchingPropertyForReplacements(vm, condition.offset());
equivCondition = condition.attemptToMakeEquivalenceWithoutBarrier(vm);
// The equivalence condition won't be watchable if we have already seen a replacement.
if (!equivCondition.isWatchable()) {
m_giveUpOnObjectToStringValueCache = true;
return;
}
} else if (!condition.isWatchable()) {
m_giveUpOnObjectToStringValueCache = true;
return;
}
}
ASSERT(conditionSet.structuresEnsureValidity());
for (ObjectPropertyCondition condition : conditionSet) {
if (condition.condition().kind() == PropertyCondition::Presence) {
m_objectToStringAdaptiveInferredValueWatchpoint = makeUnique<ObjectToStringAdaptiveInferredPropertyValueWatchpoint>(equivCondition, this);
m_objectToStringAdaptiveInferredValueWatchpoint->install(vm);
} else
m_objectToStringAdaptiveWatchpointSet.add(condition, this)->install(vm);
}
m_objectToStringValue.set(vm, this, value);
}
void StructureRareData::clearObjectToStringValue()
{
m_objectToStringAdaptiveWatchpointSet.clear();
m_objectToStringAdaptiveInferredValueWatchpoint.reset();
m_objectToStringValue.clear();
}
void StructureRareData::finalizeUnconditionally(VM& vm)
{
if (m_objectToStringAdaptiveInferredValueWatchpoint) {
if (!m_objectToStringAdaptiveInferredValueWatchpoint->key().isStillLive(vm)) {
clearObjectToStringValue();
return;
}
}
for (auto* watchpoint : m_objectToStringAdaptiveWatchpointSet) {
if (!watchpoint->key().isStillLive(vm)) {
clearObjectToStringValue();
return;
}
}
}
// ------------- Methods for Object.prototype.toString() helper watchpoint classes --------------
ObjectToStringAdaptiveInferredPropertyValueWatchpoint::ObjectToStringAdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition& key, StructureRareData* structureRareData)
: Base(key)
, m_structureRareData(structureRareData)
{
}
bool ObjectToStringAdaptiveInferredPropertyValueWatchpoint::isValid() const
{
return m_structureRareData->isLive();
}
void ObjectToStringAdaptiveInferredPropertyValueWatchpoint::handleFire(VM&, const FireDetail&)
{
m_structureRareData->clearObjectToStringValue();
}
} // namespace JSC