| /* |
| * Copyright (C) 2015 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 "ObjectPropertyConditionSet.h" |
| |
| #include "JSCInlines.h" |
| #include <wtf/ListDump.h> |
| |
| namespace JSC { |
| |
| ObjectPropertyCondition ObjectPropertyConditionSet::forObject(JSObject* object) const |
| { |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (condition.object() == object) |
| return condition; |
| } |
| return ObjectPropertyCondition(); |
| } |
| |
| ObjectPropertyCondition ObjectPropertyConditionSet::forConditionKind( |
| PropertyCondition::Kind kind) const |
| { |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (condition.kind() == kind) |
| return condition; |
| } |
| return ObjectPropertyCondition(); |
| } |
| |
| unsigned ObjectPropertyConditionSet::numberOfConditionsWithKind(PropertyCondition::Kind kind) const |
| { |
| unsigned result = 0; |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (condition.kind() == kind) |
| result++; |
| } |
| return result; |
| } |
| |
| bool ObjectPropertyConditionSet::hasOneSlotBaseCondition() const |
| { |
| return (numberOfConditionsWithKind(PropertyCondition::Presence) == 1) != (numberOfConditionsWithKind(PropertyCondition::Equivalence) == 1); |
| } |
| |
| ObjectPropertyCondition ObjectPropertyConditionSet::slotBaseCondition() const |
| { |
| ObjectPropertyCondition result; |
| unsigned numFound = 0; |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (condition.kind() == PropertyCondition::Presence |
| || condition.kind() == PropertyCondition::Equivalence) { |
| result = condition; |
| numFound++; |
| } |
| } |
| RELEASE_ASSERT(numFound == 1); |
| return result; |
| } |
| |
| ObjectPropertyConditionSet ObjectPropertyConditionSet::mergedWith( |
| const ObjectPropertyConditionSet& other) const |
| { |
| if (!isValid() || !other.isValid()) |
| return invalid(); |
| |
| Vector<ObjectPropertyCondition> result; |
| |
| if (!isEmpty()) |
| result.appendVector(m_data->vector); |
| |
| for (const ObjectPropertyCondition& newCondition : other) { |
| bool foundMatch = false; |
| for (const ObjectPropertyCondition& existingCondition : *this) { |
| if (newCondition == existingCondition) { |
| foundMatch = true; |
| continue; |
| } |
| if (!newCondition.isCompatibleWith(existingCondition)) |
| return invalid(); |
| } |
| if (!foundMatch) |
| result.append(newCondition); |
| } |
| |
| return create(result); |
| } |
| |
| bool ObjectPropertyConditionSet::structuresEnsureValidity() const |
| { |
| if (!isValid()) |
| return false; |
| |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (!condition.structureEnsuresValidity()) |
| return false; |
| } |
| return true; |
| } |
| |
| bool ObjectPropertyConditionSet::structuresEnsureValidityAssumingImpurePropertyWatchpoint() const |
| { |
| if (!isValid()) |
| return false; |
| |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint()) |
| return false; |
| } |
| return true; |
| } |
| |
| bool ObjectPropertyConditionSet::needImpurePropertyWatchpoint() const |
| { |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (condition.validityRequiresImpurePropertyWatchpoint()) |
| return true; |
| } |
| return false; |
| } |
| |
| bool ObjectPropertyConditionSet::areStillLive() const |
| { |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (!condition.isStillLive()) |
| return false; |
| } |
| return true; |
| } |
| |
| void ObjectPropertyConditionSet::dumpInContext(PrintStream& out, DumpContext* context) const |
| { |
| if (!isValid()) { |
| out.print("<invalid>"); |
| return; |
| } |
| |
| out.print("["); |
| if (m_data) |
| out.print(listDumpInContext(m_data->vector, context)); |
| out.print("]"); |
| } |
| |
| void ObjectPropertyConditionSet::dump(PrintStream& out) const |
| { |
| dumpInContext(out, nullptr); |
| } |
| |
| bool ObjectPropertyConditionSet::isValidAndWatchable() const |
| { |
| if (!isValid()) |
| return false; |
| |
| for (ObjectPropertyCondition condition : m_data->vector) { |
| if (!condition.isWatchable()) |
| return false; |
| } |
| return true; |
| } |
| |
| namespace { |
| |
| namespace ObjectPropertyConditionSetInternal { |
| static const bool verbose = false; |
| } |
| |
| ObjectPropertyCondition generateCondition( |
| VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyCondition::Kind conditionKind) |
| { |
| Structure* structure = object->structure(); |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Creating condition ", conditionKind, " for ", pointerDump(structure), "\n"); |
| |
| ObjectPropertyCondition result; |
| switch (conditionKind) { |
| case PropertyCondition::Presence: { |
| unsigned attributes; |
| PropertyOffset offset = structure->getConcurrently(uid, attributes); |
| if (offset == invalidOffset) |
| return ObjectPropertyCondition(); |
| result = ObjectPropertyCondition::presence(vm, owner, object, uid, offset, attributes); |
| break; |
| } |
| case PropertyCondition::Absence: { |
| if (structure->hasPolyProto()) |
| return ObjectPropertyCondition(); |
| result = ObjectPropertyCondition::absence( |
| vm, owner, object, uid, object->structure()->storedPrototypeObject()); |
| break; |
| } |
| case PropertyCondition::AbsenceOfSetEffect: { |
| if (structure->hasPolyProto()) |
| return ObjectPropertyCondition(); |
| result = ObjectPropertyCondition::absenceOfSetEffect( |
| vm, owner, object, uid, object->structure()->storedPrototypeObject()); |
| break; |
| } |
| case PropertyCondition::Equivalence: { |
| unsigned attributes; |
| PropertyOffset offset = structure->getConcurrently(uid, attributes); |
| if (offset == invalidOffset) |
| return ObjectPropertyCondition(); |
| JSValue value = object->getDirect(offset); |
| result = ObjectPropertyCondition::equivalence(vm, owner, object, uid, value); |
| break; |
| } |
| default: |
| RELEASE_ASSERT_NOT_REACHED(); |
| return ObjectPropertyCondition(); |
| } |
| |
| if (!result.isStillValidAssumingImpurePropertyWatchpoint()) { |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Failed to create condition: ", result, "\n"); |
| return ObjectPropertyCondition(); |
| } |
| |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("New condition: ", result, "\n"); |
| return result; |
| } |
| |
| enum Concurrency { |
| MainThread, |
| Concurrent |
| }; |
| template<typename Functor> |
| ObjectPropertyConditionSet generateConditions( |
| VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* prototype, const Functor& functor, |
| Concurrency concurrency = MainThread) |
| { |
| Vector<ObjectPropertyCondition> conditions; |
| |
| for (;;) { |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Considering structure: ", pointerDump(structure), "\n"); |
| |
| if (structure->isProxy()) { |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("It's a proxy, so invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| |
| if (structure->hasPolyProto()) { |
| // FIXME: Integrate this with PolyProtoAccessChain: |
| // https://bugs.webkit.org/show_bug.cgi?id=177339 |
| // Or at least allow OPC set generation when the |
| // base is not poly proto: |
| // https://bugs.webkit.org/show_bug.cgi?id=177721 |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| |
| JSValue value = structure->prototypeForLookup(globalObject); |
| |
| if (value.isNull()) { |
| if (!prototype) { |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Reached end of prototype chain as expected, done.\n"); |
| break; |
| } |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Unexpectedly reached end of prototype chain, so invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| |
| JSObject* object = jsCast<JSObject*>(value); |
| structure = object->structure(vm); |
| |
| if (structure->isDictionary()) { |
| if (concurrency == MainThread) { |
| if (structure->hasBeenFlattenedBefore()) { |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Dictionary has been flattened before, so invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Flattening ", pointerDump(structure)); |
| structure->flattenDictionaryStructure(vm, object); |
| } else { |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Cannot flatten dictionary when not on main thread, so invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| } |
| |
| if (!functor(conditions, object)) { |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Functor failed, invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| |
| if (object == prototype) { |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Reached desired prototype, done.\n"); |
| break; |
| } |
| } |
| |
| if (ObjectPropertyConditionSetInternal::verbose) |
| dataLog("Returning conditions: ", listDump(conditions), "\n"); |
| return ObjectPropertyConditionSet::create(conditions); |
| } |
| |
| } // anonymous namespace |
| |
| ObjectPropertyConditionSet generateConditionsForPropertyMiss( |
| VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid) |
| { |
| return generateConditions( |
| vm, exec->lexicalGlobalObject(), headStructure, nullptr, |
| [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { |
| ObjectPropertyCondition result = |
| generateCondition(vm, owner, object, uid, PropertyCondition::Absence); |
| if (!result) |
| return false; |
| conditions.append(result); |
| return true; |
| }); |
| } |
| |
| ObjectPropertyConditionSet generateConditionsForPropertySetterMiss( |
| VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid) |
| { |
| return generateConditions( |
| vm, exec->lexicalGlobalObject(), headStructure, nullptr, |
| [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { |
| ObjectPropertyCondition result = |
| generateCondition(vm, owner, object, uid, PropertyCondition::AbsenceOfSetEffect); |
| if (!result) |
| return false; |
| conditions.append(result); |
| return true; |
| }); |
| } |
| |
| ObjectPropertyConditionSet generateConditionsForPrototypePropertyHit( |
| VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype, |
| UniquedStringImpl* uid) |
| { |
| return generateConditions( |
| vm, exec->lexicalGlobalObject(), headStructure, prototype, |
| [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { |
| PropertyCondition::Kind kind = |
| object == prototype ? PropertyCondition::Presence : PropertyCondition::Absence; |
| ObjectPropertyCondition result = |
| generateCondition(vm, owner, object, uid, kind); |
| if (!result) |
| return false; |
| conditions.append(result); |
| return true; |
| }); |
| } |
| |
| ObjectPropertyConditionSet generateConditionsForPrototypeEquivalenceConcurrently( |
| VM& vm, JSGlobalObject* globalObject, Structure* headStructure, JSObject* prototype, UniquedStringImpl* uid) |
| { |
| return generateConditions(vm, globalObject, headStructure, prototype, |
| [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { |
| PropertyCondition::Kind kind = |
| object == prototype ? PropertyCondition::Equivalence : PropertyCondition::Absence; |
| ObjectPropertyCondition result = generateCondition(vm, nullptr, object, uid, kind); |
| if (!result) |
| return false; |
| conditions.append(result); |
| return true; |
| }, Concurrent); |
| } |
| |
| ObjectPropertyConditionSet generateConditionsForPropertyMissConcurrently( |
| VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid) |
| { |
| return generateConditions( |
| vm, globalObject, headStructure, nullptr, |
| [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { |
| ObjectPropertyCondition result = generateCondition(vm, nullptr, object, uid, PropertyCondition::Absence); |
| if (!result) |
| return false; |
| conditions.append(result); |
| return true; |
| }, Concurrent); |
| } |
| |
| ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently( |
| VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid) |
| { |
| return generateConditions( |
| vm, globalObject, headStructure, nullptr, |
| [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool { |
| ObjectPropertyCondition result = |
| generateCondition(vm, nullptr, object, uid, PropertyCondition::AbsenceOfSetEffect); |
| if (!result) |
| return false; |
| conditions.append(result); |
| return true; |
| }, Concurrent); |
| } |
| |
| ObjectPropertyCondition generateConditionForSelfEquivalence( |
| VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid) |
| { |
| return generateCondition(vm, owner, object, uid, PropertyCondition::Equivalence); |
| } |
| |
| } // namespace JSC |
| |