| /* |
| * 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; |
| } |
| |
| ObjectPropertyCondition ObjectPropertyConditionSet::slotBaseCondition() const |
| { |
| ObjectPropertyCondition result; |
| unsigned numFound = 0; |
| for (const ObjectPropertyCondition& condition : *this) { |
| if (condition.kind() == PropertyCondition::Presence) { |
| 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) { |
| for (const ObjectPropertyCondition& existingCondition : *this) { |
| if (newCondition == existingCondition) |
| continue; |
| if (!newCondition.isCompatibleWith(existingCondition)) |
| return invalid(); |
| 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); |
| } |
| |
| namespace { |
| |
| bool verbose = false; |
| |
| ObjectPropertyCondition generateCondition( |
| VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyCondition::Kind conditionKind) |
| { |
| Structure* structure = object->structure(); |
| if (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: { |
| result = ObjectPropertyCondition::absence( |
| vm, owner, object, uid, object->structure()->storedPrototypeObject()); |
| break; |
| } |
| case PropertyCondition::AbsenceOfSetter: { |
| result = ObjectPropertyCondition::absenceOfSetter( |
| vm, owner, object, uid, object->structure()->storedPrototypeObject()); |
| break; |
| } |
| default: |
| RELEASE_ASSERT_NOT_REACHED(); |
| return ObjectPropertyCondition(); |
| } |
| |
| if (!result.structureEnsuresValidityAssumingImpurePropertyWatchpoint()) { |
| if (verbose) |
| dataLog("Failed to create condition: ", result, "\n"); |
| return ObjectPropertyCondition(); |
| } |
| |
| if (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 (verbose) |
| dataLog("Considering structure: ", pointerDump(structure), "\n"); |
| |
| if (structure->isProxy()) { |
| if (verbose) |
| dataLog("It's a proxy, so invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| |
| JSValue value = structure->prototypeForLookup(globalObject); |
| |
| if (value.isNull()) { |
| if (!prototype) { |
| if (verbose) |
| dataLog("Reached end up prototype chain as expected, done.\n"); |
| break; |
| } |
| if (verbose) |
| dataLog("Unexpectedly reached end of prototype chain, so invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| |
| JSObject* object = jsCast<JSObject*>(value); |
| structure = object->structure(vm); |
| |
| // Since we're accessing a prototype repeatedly, it's a good bet that it should not be |
| // treated as a dictionary. |
| if (structure->isDictionary()) { |
| if (concurrency == MainThread) |
| structure->flattenDictionaryStructure(vm, object); |
| else { |
| if (verbose) |
| dataLog("Cannot flatten dictionary when not on main thread, so invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| } |
| |
| if (!functor(conditions, object)) { |
| if (verbose) |
| dataLog("Functor failed, invalid.\n"); |
| return ObjectPropertyConditionSet::invalid(); |
| } |
| |
| if (object == prototype) { |
| if (verbose) |
| dataLog("Reached desired prototype, done.\n"); |
| break; |
| } |
| } |
| |
| if (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::AbsenceOfSetter); |
| 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 generateConditionsForPrototypePropertyHitCustom( |
| 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 { |
| if (object == prototype) |
| return true; |
| ObjectPropertyCondition result = |
| generateCondition(vm, owner, object, uid, PropertyCondition::Absence); |
| if (!result) |
| return false; |
| conditions.append(result); |
| return true; |
| }); |
| } |
| |
| 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::AbsenceOfSetter); |
| if (!result) |
| return false; |
| conditions.append(result); |
| return true; |
| }, Concurrent); |
| } |
| |
| } // namespace JSC |
| |