| /* | 
 |  * Copyright (C) 2013-2018 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 "DFGAbstractValue.h" | 
 |  | 
 | #if ENABLE(DFG_JIT) | 
 |  | 
 | #include "DFGGraph.h" | 
 | #include "JSCInlines.h" | 
 | #include "TrackedReferences.h" | 
 |  | 
 | namespace JSC { namespace DFG { | 
 |  | 
 | void AbstractValue::observeTransitions(const TransitionVector& vector) | 
 | { | 
 |     if (m_type & SpecCell) { | 
 |         m_structure.observeTransitions(vector); | 
 |         ArrayModes newModes = 0; | 
 |         for (unsigned i = vector.size(); i--;) { | 
 |             if (m_arrayModes & arrayModesFromStructure(vector[i].previous.get())) | 
 |                 newModes |= arrayModesFromStructure(vector[i].next.get()); | 
 |         } | 
 |         m_arrayModes |= newModes; | 
 |     } | 
 |     checkConsistency(); | 
 | } | 
 |  | 
 | void AbstractValue::set(Graph& graph, const FrozenValue& value, StructureClobberState clobberState) | 
 | { | 
 |     if (!!value && value.value().isCell()) { | 
 |         Structure* structure = value.structure(); | 
 |         StructureRegistrationResult result; | 
 |         RegisteredStructure registeredStructure = graph.registerStructure(structure, result); | 
 |         if (result == StructureRegisteredAndWatched) { | 
 |             m_structure = registeredStructure; | 
 |             if (clobberState == StructuresAreClobbered) { | 
 |                 m_arrayModes = ALL_ARRAY_MODES; | 
 |                 m_structure.clobber(); | 
 |             } else | 
 |                 m_arrayModes = arrayModesFromStructure(structure); | 
 |         } else { | 
 |             m_structure.makeTop(); | 
 |             m_arrayModes = ALL_ARRAY_MODES; | 
 |         } | 
 |     } else { | 
 |         m_structure.clear(); | 
 |         m_arrayModes = 0; | 
 |     } | 
 |      | 
 |     m_type = speculationFromValue(value.value()); | 
 |     m_value = value.value(); | 
 |      | 
 |     checkConsistency(); | 
 |     assertIsRegistered(graph); | 
 | } | 
 |  | 
 | void AbstractValue::set(Graph& graph, Structure* structure) | 
 | { | 
 |     set(graph, graph.registerStructure(structure)); | 
 | } | 
 |  | 
 | void AbstractValue::set(Graph& graph, RegisteredStructure structure) | 
 | { | 
 |     RELEASE_ASSERT(structure); | 
 |      | 
 |     m_structure = structure; | 
 |     m_arrayModes = arrayModesFromStructure(structure.get()); | 
 |     m_type = speculationFromStructure(structure.get()); | 
 |     m_value = JSValue(); | 
 |      | 
 |     checkConsistency(); | 
 |     assertIsRegistered(graph); | 
 | } | 
 |  | 
 | void AbstractValue::set(Graph& graph, const RegisteredStructureSet& set) | 
 | { | 
 |     m_structure = set; | 
 |     m_arrayModes = set.arrayModesFromStructures(); | 
 |     m_type = set.speculationFromStructures(); | 
 |     m_value = JSValue(); | 
 |      | 
 |     checkConsistency(); | 
 |     assertIsRegistered(graph); | 
 | } | 
 |  | 
 | void AbstractValue::setType(Graph& graph, SpeculatedType type) | 
 | { | 
 |     SpeculatedType cellType = type & SpecCell; | 
 |     if (cellType) { | 
 |         if (!(cellType & ~SpecString)) | 
 |             m_structure = graph.stringStructure; | 
 |         else if (isSymbolSpeculation(cellType)) | 
 |             m_structure = graph.symbolStructure; | 
 |         else | 
 |             m_structure.makeTop(); | 
 |         m_arrayModes = ALL_ARRAY_MODES; | 
 |     } else { | 
 |         m_structure.clear(); | 
 |         m_arrayModes = 0; | 
 |     } | 
 |     m_type = type; | 
 |     m_value = JSValue(); | 
 |     checkConsistency(); | 
 | } | 
 |  | 
 | void AbstractValue::fixTypeForRepresentation(Graph& graph, NodeFlags representation, Node* node) | 
 | { | 
 |     if (representation == NodeResultDouble) { | 
 |         if (m_value) { | 
 |             DFG_ASSERT(graph, node, m_value.isNumber()); | 
 |             if (m_value.isInt32()) | 
 |                 m_value = jsDoubleNumber(m_value.asNumber()); | 
 |         } | 
 |         if (m_type & SpecIntAnyFormat) { | 
 |             m_type &= ~SpecIntAnyFormat; | 
 |             m_type |= SpecAnyIntAsDouble; | 
 |         } | 
 |         if (m_type & ~SpecFullDouble) | 
 |             DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for double node has type outside SpecFullDouble.\n").data()); | 
 |     } else if (representation == NodeResultInt52) { | 
 |         if (m_type & SpecAnyIntAsDouble) { | 
 |             // AnyIntAsDouble can produce i32 or i52. SpecAnyIntAsDouble doesn't bound the magnitude of the value. | 
 |             m_type &= ~SpecAnyIntAsDouble; | 
 |             m_type |= SpecInt52Any; | 
 |         } | 
 |  | 
 |         if (m_type & SpecInt32Only) { | 
 |             m_type &= ~SpecInt32Only; | 
 |             m_type |= SpecInt32AsInt52; | 
 |         } | 
 |  | 
 |         if (m_type & ~SpecInt52Any) | 
 |             DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for int52 node has type outside SpecInt52Any.\n").data()); | 
 |  | 
 |         if (m_value) { | 
 |             DFG_ASSERT(graph, node, m_value.isAnyInt()); | 
 |             m_type = int52AwareSpeculationFromValue(m_value); | 
 |         } | 
 |     } else { | 
 |         if (m_type & SpecInt32AsInt52) { | 
 |             m_type &= ~SpecInt32AsInt52; | 
 |             m_type |= SpecInt32Only; | 
 |         } | 
 |         if (m_type & SpecNonInt32AsInt52) { | 
 |             m_type &= ~SpecNonInt32AsInt52; | 
 |             m_type |= SpecAnyIntAsDouble; | 
 |         } | 
 |         if (m_type & ~SpecBytecodeTop) | 
 |             DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for value node has type outside SpecBytecodeTop.\n").data()); | 
 |     } | 
 |      | 
 |     checkConsistency(); | 
 | } | 
 |  | 
 | void AbstractValue::fixTypeForRepresentation(Graph& graph, Node* node) | 
 | { | 
 |     fixTypeForRepresentation(graph, node->result(), node); | 
 | } | 
 |  | 
 | bool AbstractValue::mergeOSREntryValue(Graph& graph, JSValue value, VariableAccessData* variable, Node* node) | 
 | { | 
 |     FlushFormat flushFormat = variable->flushFormat(); | 
 |  | 
 |     { | 
 |         if (flushFormat == FlushedDouble && value.isNumber()) | 
 |             value = jsDoubleNumber(value.asNumber()); | 
 |         SpeculatedType incomingType = resultFor(flushFormat) == NodeResultInt52 ? int52AwareSpeculationFromValue(value) : speculationFromValue(value); | 
 |         SpeculatedType requiredType = typeFilterFor(flushFormat); | 
 |         if (incomingType & ~requiredType) | 
 |             return false; | 
 |     } | 
 |  | 
 |     AbstractValue oldMe = *this; | 
 |      | 
 |     if (isClear()) { | 
 |         FrozenValue* frozenValue = graph.freeze(value); | 
 |         if (frozenValue->pointsToHeap()) { | 
 |             m_structure = graph.registerStructure(frozenValue->structure()); | 
 |             m_arrayModes = arrayModesFromStructure(frozenValue->structure()); | 
 |         } else { | 
 |             m_structure.clear(); | 
 |             m_arrayModes = 0; | 
 |         } | 
 |          | 
 |         m_type = speculationFromValue(value); | 
 |         m_value = value; | 
 |     } else { | 
 |         mergeSpeculation(m_type, speculationFromValue(value)); | 
 |         if (!!value && value.isCell()) { | 
 |             RegisteredStructure structure = graph.registerStructure(value.asCell()->structure(graph.m_vm)); | 
 |             mergeArrayModes(m_arrayModes, arrayModesFromStructure(structure.get())); | 
 |             m_structure.merge(RegisteredStructureSet(structure)); | 
 |         } | 
 |         if (m_value != value) | 
 |             m_value = JSValue(); | 
 |     } | 
 |      | 
 |     assertIsRegistered(graph); | 
 |  | 
 |     fixTypeForRepresentation(graph, resultFor(flushFormat), node); | 
 |  | 
 |     checkConsistency(); | 
 |      | 
 |     return oldMe != *this; | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::filter( | 
 |     Graph& graph, const RegisteredStructureSet& other, SpeculatedType admittedTypes) | 
 | { | 
 |     ASSERT(!(admittedTypes & SpecCell)); | 
 |      | 
 |     if (isClear()) | 
 |         return FiltrationOK; | 
 |      | 
 |     // FIXME: This could be optimized for the common case of m_type not | 
 |     // having structures, array modes, or a specific value. | 
 |     // https://bugs.webkit.org/show_bug.cgi?id=109663 | 
 |      | 
 |     m_type &= other.speculationFromStructures() | admittedTypes; | 
 |     m_arrayModes &= other.arrayModesFromStructures(); | 
 |     m_structure.filter(other); | 
 |      | 
 |     // It's possible that prior to the above two statements we had (Foo, TOP), where | 
 |     // Foo is a SpeculatedType that is disjoint with the passed RegisteredStructureSet. In that | 
 |     // case, we will now have (None, [someStructure]). In general, we need to make | 
 |     // sure that new information gleaned from the SpeculatedType needs to be fed back | 
 |     // into the information gleaned from the RegisteredStructureSet. | 
 |     m_structure.filter(m_type); | 
 |      | 
 |     filterArrayModesByType(); | 
 |     filterValueByType(); | 
 |     return normalizeClarity(graph); | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::changeStructure(Graph& graph, const RegisteredStructureSet& other) | 
 | { | 
 |     m_type &= other.speculationFromStructures(); | 
 |     m_arrayModes = other.arrayModesFromStructures(); | 
 |     m_structure = other; | 
 |      | 
 |     filterValueByType(); | 
 |      | 
 |     return normalizeClarity(graph); | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::filterArrayModes(ArrayModes arrayModes) | 
 | { | 
 |     ASSERT(arrayModes); | 
 |      | 
 |     if (isClear()) | 
 |         return FiltrationOK; | 
 |      | 
 |     m_type &= SpecCell; | 
 |     m_arrayModes &= arrayModes; | 
 |     return normalizeClarity(); | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::filterClassInfo(Graph& graph, const ClassInfo* classInfo) | 
 | { | 
 |     // FIXME: AI should track ClassInfo to leverage hierarchical class information. | 
 |     // https://bugs.webkit.org/show_bug.cgi?id=162989 | 
 |     if (isClear()) | 
 |         return FiltrationOK; | 
 |  | 
 |     m_type &= speculationFromClassInfo(classInfo); | 
 |     m_structure.filterClassInfo(classInfo); | 
 |  | 
 |     m_structure.filter(m_type); | 
 |  | 
 |     filterArrayModesByType(); | 
 |     filterValueByType(); | 
 |     return normalizeClarity(graph); | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::filterSlow(SpeculatedType type) | 
 | { | 
 |     m_type &= type; | 
 |      | 
 |     // It's possible that prior to this filter() call we had, say, (Final, TOP), and | 
 |     // the passed type is Array. At this point we'll have (None, TOP). The best way | 
 |     // to ensure that the structure filtering does the right thing is to filter on | 
 |     // the new type (None) rather than the one passed (Array). | 
 |     m_structure.filter(m_type); | 
 |     filterArrayModesByType(); | 
 |     filterValueByType(); | 
 |     return normalizeClarity(); | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::fastForwardToAndFilterSlow(AbstractValueClobberEpoch newEpoch, SpeculatedType type) | 
 | { | 
 |     if (newEpoch != m_effectEpoch) | 
 |         fastForwardToSlow(newEpoch); | 
 |      | 
 |     return filterSlow(type); | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::filterByValue(const FrozenValue& value) | 
 | { | 
 |     FiltrationResult result = filter(speculationFromValue(value.value())); | 
 |     if (m_type) | 
 |         m_value = value.value(); | 
 |     return result; | 
 | } | 
 |  | 
 | bool AbstractValue::contains(RegisteredStructure structure) const | 
 | { | 
 |     return couldBeType(speculationFromStructure(structure.get())) | 
 |         && (m_arrayModes & arrayModesFromStructure(structure.get())) | 
 |         && m_structure.contains(structure); | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::filter(const AbstractValue& other) | 
 | { | 
 |     m_type &= other.m_type; | 
 |     m_structure.filter(other.m_structure); | 
 |     m_arrayModes &= other.m_arrayModes; | 
 |  | 
 |     m_structure.filter(m_type); | 
 |     filterArrayModesByType(); | 
 |     filterValueByType(); | 
 |      | 
 |     if (normalizeClarity() == Contradiction) | 
 |         return Contradiction; | 
 |      | 
 |     if (m_value == other.m_value) | 
 |         return FiltrationOK; | 
 |      | 
 |     // Neither of us are BOTTOM, so an empty value means TOP. | 
 |     if (!m_value) { | 
 |         // We previously didn't prove a value but now we have done so. | 
 |         m_value = other.m_value;  | 
 |         return FiltrationOK; | 
 |     } | 
 |      | 
 |     if (!other.m_value) { | 
 |         // We had proved a value but the other guy hadn't, so keep our proof. | 
 |         return FiltrationOK; | 
 |     } | 
 |      | 
 |     // We both proved there to be a specific value but they are different. | 
 |     clear(); | 
 |     return Contradiction; | 
 | } | 
 |  | 
 | void AbstractValue::filterValueByType() | 
 | { | 
 |     // We could go further, and ensure that if the futurePossibleStructure contravenes | 
 |     // the value, then we could clear both of those things. But that's unlikely to help | 
 |     // in any realistic scenario, so we don't do it. Simpler is better. | 
 |  | 
 |     if (!m_value) | 
 |         return; | 
 |  | 
 |     if (validateTypeAcceptingBoxedInt52(m_value)) | 
 |         return; | 
 |  | 
 |     // We assume that the constant value can produce a narrower type at | 
 |     // some point. For example, rope JSString produces SpecString, but | 
 |     // it produces SpecStringIdent once it is resolved to AtomStringImpl. | 
 |     // We do not make this AbstractValue cleared, but clear the constant | 
 |     // value if validation fails currently. | 
 |     m_value = JSValue(); | 
 | } | 
 |  | 
 | void AbstractValue::filterArrayModesByType() | 
 | { | 
 |     if (!(m_type & SpecCell)) | 
 |         m_arrayModes = 0; | 
 |     else if (!(m_type & ~SpecArray)) | 
 |         m_arrayModes &= ALL_ARRAY_ARRAY_MODES; | 
 |      | 
 |     // NOTE: If m_type doesn't have SpecArray set, that doesn't mean that the | 
 |     // array modes have to be a subset of ALL_NON_ARRAY_ARRAY_MODES, since | 
 |     // in the speculated type type-system, RegExpMatchesArry and ArrayPrototype | 
 |     // are Otherobj (since they are not *exactly* JSArray) but in the ArrayModes | 
 |     // type system they are arrays (since they expose the magical length | 
 |     // property and are otherwise allocated using array allocation). Hence the | 
 |     // following would be wrong: | 
 |     // | 
 |     // if (!(m_type & SpecArray)) | 
 |     //    m_arrayModes &= ALL_NON_ARRAY_ARRAY_MODES; | 
 | } | 
 |  | 
 | bool AbstractValue::shouldBeClear() const | 
 | { | 
 |     if (m_type == SpecNone) | 
 |         return true; | 
 |      | 
 |     if (!(m_type & ~SpecCell) | 
 |         && (!m_arrayModes || m_structure.isClear())) | 
 |         return true; | 
 |      | 
 |     return false; | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::normalizeClarity() | 
 | { | 
 |     // It's useful to be able to quickly check if an abstract value is clear. | 
 |     // This normalizes everything to make that easy. | 
 |      | 
 |     FiltrationResult result; | 
 |      | 
 |     if (shouldBeClear()) { | 
 |         clear(); | 
 |         result = Contradiction; | 
 |     } else | 
 |         result = FiltrationOK; | 
 |  | 
 |     checkConsistency(); | 
 |      | 
 |     return result; | 
 | } | 
 |  | 
 | FiltrationResult AbstractValue::normalizeClarity(Graph& graph) | 
 | { | 
 |     FiltrationResult result = normalizeClarity(); | 
 |     assertIsRegistered(graph); | 
 |     return result; | 
 | } | 
 |  | 
 | #if !ASSERT_DISABLED | 
 | void AbstractValue::checkConsistency() const | 
 | { | 
 |     if (!(m_type & SpecCell)) { | 
 |         RELEASE_ASSERT(m_structure.isClear()); | 
 |         RELEASE_ASSERT(!m_arrayModes); | 
 |     } | 
 |      | 
 |     if (isClear()) | 
 |         RELEASE_ASSERT(!m_value); | 
 |      | 
 |     if (!!m_value) | 
 |         RELEASE_ASSERT(validateTypeAcceptingBoxedInt52(m_value)); | 
 |  | 
 |     // Note that it's possible for a prediction like (Final, []). This really means that | 
 |     // the value is bottom and that any code that uses the value is unreachable. But | 
 |     // we don't want to get pedantic about this as it would only increase the computational | 
 |     // complexity of the code. | 
 | } | 
 |  | 
 | void AbstractValue::assertIsRegistered(Graph& graph) const | 
 | { | 
 |     m_structure.assertIsRegistered(graph); | 
 | } | 
 | #endif // !ASSERT_DISABLED | 
 |  | 
 | ResultType AbstractValue::resultType() const | 
 | { | 
 |     ASSERT(isType(SpecBytecodeTop)); | 
 |     if (isType(SpecBoolean)) | 
 |         return ResultType::booleanType(); | 
 |     if (isType(SpecInt32Only)) | 
 |         return ResultType::numberTypeIsInt32(); | 
 |     if (isType(SpecBytecodeNumber)) | 
 |         return ResultType::numberType(); | 
 |     if (isType(SpecString)) | 
 |         return ResultType::stringType(); | 
 |     if (isType(SpecString | SpecBytecodeNumber)) | 
 |         return ResultType::stringOrNumberType(); | 
 |     return ResultType::unknownType(); | 
 | } | 
 |  | 
 | void AbstractValue::dump(PrintStream& out) const | 
 | { | 
 |     dumpInContext(out, 0); | 
 | } | 
 |  | 
 | void AbstractValue::dumpInContext(PrintStream& out, DumpContext* context) const | 
 | { | 
 |     out.print("(", SpeculationDump(m_type)); | 
 |     if (m_type & SpecCell) { | 
 |         out.print( | 
 |             ", ", ArrayModesDump(m_arrayModes), ", ", | 
 |             inContext(m_structure, context)); | 
 |     } | 
 |     if (!!m_value) | 
 |         out.print(", ", inContext(m_value, context)); | 
 |     out.print(", ", m_effectEpoch); | 
 |     out.print(")"); | 
 | } | 
 |  | 
 | void AbstractValue::validateReferences(const TrackedReferences& trackedReferences) | 
 | { | 
 |     trackedReferences.check(m_value); | 
 |     m_structure.validateReferences(trackedReferences); | 
 | } | 
 |  | 
 | #if USE(JSVALUE64) && !defined(NDEBUG) | 
 | void AbstractValue::ensureCanInitializeWithZeros() | 
 | { | 
 |     std::aligned_storage<sizeof(AbstractValue), alignof(AbstractValue)>::type zeroFilledStorage; | 
 |     memset(static_cast<void*>(&zeroFilledStorage), 0, sizeof(AbstractValue)); | 
 |     ASSERT(*this == *static_cast<AbstractValue*>(static_cast<void*>(&zeroFilledStorage))); | 
 | } | 
 | #endif | 
 |  | 
 | void AbstractValue::fastForwardToSlow(AbstractValueClobberEpoch newEpoch) | 
 | { | 
 |     ASSERT(newEpoch != m_effectEpoch); | 
 |      | 
 |     if (newEpoch.clobberEpoch() != m_effectEpoch.clobberEpoch()) | 
 |         clobberStructures(); | 
 |     if (newEpoch.structureClobberState() == StructuresAreWatched) | 
 |         m_structure.observeInvalidationPoint(); | 
 |      | 
 |     m_effectEpoch = newEpoch; | 
 |      | 
 |     checkConsistency(); | 
 | } | 
 |  | 
 | } } // namespace JSC::DFG | 
 |  | 
 | #endif // ENABLE(DFG_JIT) | 
 |  |