Skip to content
Snippets Groups Projects
Select Git revision
  • fix-raspi-issues-v2-oracular
  • fix-raspi-issues-v2-noble
  • fix-raspi-issues-v2
  • fix-raspi-issues
  • wk2/unstable default
  • wpe/unstable
  • wpewebkit-2.44
  • berto/wk2/bookworm
  • berto/wk2/bullseye
  • webkitgtk-2.44
  • wk2/experimental
  • wpewebkit-2.42
  • webkitgtk-2.42
  • webkitgtk-2.40
  • wpewebkit-2.40
  • buster
  • webkitgtk-2.38
  • wpewebkit-2.38
  • wpewebkit-2.36
  • webkitgtk-2.36
  • wpe/debian/2.44.1-1
  • wpe/upstream/2.44.1
  • debian/2.44.1-1
  • upstream/2.44.1
  • debian/2.44.0-2
  • debian/2.44.0-1
  • upstream/2.44.0
  • debian/2.43.4-2
  • debian/2.42.5-2
  • wpe/debian/2.42.5-1.2
  • wpe/debian/2.42.5-2_exp
  • wpe/debian/2.42.5-1.1
  • debian/2.42.5-1_deb11u1
  • debian/2.42.5-1_deb12u1
  • wpe/debian/2.42.5-1
  • wpe/upstream/2.42.5
  • debian/2.42.5-1
  • upstream/2.42.5
  • debian/2.43.4-1
  • upstream/2.43.4
40 results

PropertyCondition.cpp

Blame
  • Forked from WebKit / webkit
    2317 commits behind the upstream repository.
    PropertyCondition.cpp 11.50 KiB
    /*
     * 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 "PropertyCondition.h"
    
    #include "GetterSetter.h"
    #include "JSCInlines.h"
    #include "TrackedReferences.h"
    
    namespace JSC {
    
    static bool verbose = false;
    
    void PropertyCondition::dumpInContext(PrintStream& out, DumpContext* context) const
    {
        if (!*this) {
            out.print("<invalid>");
            return;
        }
        
        out.print(m_kind, " of ", m_uid);
        switch (m_kind) {
        case Presence:
            out.print(" at ", offset(), " with attributes ", attributes());
            return;
        case Absence:
        case AbsenceOfSetter:
            out.print(" with prototype ", inContext(JSValue(prototype()), context));
            return;
        case Equivalence:
            out.print(" with ", inContext(requiredValue(), context));
            return;
        }
        RELEASE_ASSERT_NOT_REACHED();
    }
    
    void PropertyCondition::dump(PrintStream& out) const
    {
        dumpInContext(out, nullptr);
    }
    
    bool PropertyCondition::isStillValidAssumingImpurePropertyWatchpoint(
        Structure* structure, JSObject* base) const
    {
        if (verbose) {
            dataLog(
                "Determining validity of ", *this, " with structure ", pointerDump(structure), " and base ",
                JSValue(base), " assuming impure property watchpoints are set.\n");
        }
        
        if (!*this) {
            if (verbose)
                dataLog("Invalid because unset.\n");
            return false;
        }
        
        if (!structure->propertyAccessesAreCacheable()) {
            if (verbose)
                dataLog("Invalid because accesses are not cacheable.\n");
            return false;
        }
        
        switch (m_kind) {
        case Presence: {
            unsigned currentAttributes;
            PropertyOffset currentOffset = structure->getConcurrently(uid(), currentAttributes);
            if (currentOffset != offset() || currentAttributes != attributes()) {
                if (verbose) {
                    dataLog(
                        "Invalid because we need offset, attributes to be ", offset(), ", ", attributes(),
                        " but they are ", currentOffset, ", ", currentAttributes, "\n");
                }
                return false;
            }
            return true;
        }
            
        case Absence: {
            if (structure->isDictionary()) {
                if (verbose)
                    dataLog("Invalid because it's a dictionary.\n");
                return false;
            }
    
            PropertyOffset currentOffset = structure->getConcurrently(uid());
            if (currentOffset != invalidOffset) {
                if (verbose)
                    dataLog("Invalid because the property exists at offset: ", currentOffset, "\n");
                return false;
            }
            
            if (structure->storedPrototypeObject() != prototype()) {
                if (verbose) {
                    dataLog(
                        "Invalid because the prototype is ", structure->storedPrototype(), " even though "
                        "it should have been ", JSValue(prototype()), "\n");
                }
                return false;
            }
            
            return true;
        }
        
        case AbsenceOfSetter: {
            if (structure->isDictionary()) {
                if (verbose)
                    dataLog("Invalid because it's a dictionary.\n");
                return false;
            }
            
            unsigned currentAttributes;
            PropertyOffset currentOffset = structure->getConcurrently(uid(), currentAttributes);
            if (currentOffset != invalidOffset) {
                if (currentAttributes & (Accessor | CustomAccessor)) {
                    if (verbose) {
                        dataLog(
                            "Invalid because we expected not to have a setter, but we have one at offset ",
                            currentOffset, " with attributes ", currentAttributes, "\n");
                    }
                    return false;
                }
            }
            
            if (structure->storedPrototypeObject() != prototype()) {
                if (verbose) {
                    dataLog(
                        "Invalid because the prototype is ", structure->storedPrototype(), " even though "
                        "it should have been ", JSValue(prototype()), "\n");
                }
                return false;
            }
            
            return true;
        }
            
        case Equivalence: {
            if (!base || base->structure() != structure) {
                // Conservatively return false, since we cannot verify this one without having the
                // object.
                if (verbose) {
                    dataLog(
                        "Invalid because we don't have a base or the base has the wrong structure: ",
                        RawPointer(base), "\n");
                }
                return false;
            }
            
            // FIXME: This is somewhat racy, and maybe more risky than we want.
            // https://bugs.webkit.org/show_bug.cgi?id=134641
            
            PropertyOffset currentOffset = structure->getConcurrently(uid());
            if (currentOffset == invalidOffset) {
                if (verbose) {
                    dataLog(
                        "Invalid because the base no long appears to have ", uid(), " on its structure: ",
                            RawPointer(base), "\n");
                }
                return false;
            }
    
            JSValue currentValue = base->getDirect(currentOffset);
            if (currentValue != requiredValue()) {
                if (verbose) {
                    dataLog(
                        "Invalid because the value is ", currentValue, " but we require ", requiredValue(),
                        "\n");
                }
                return false;
            }
            
            return true;
        } }
        
        RELEASE_ASSERT_NOT_REACHED();
        return false;
    }
    
    bool PropertyCondition::validityRequiresImpurePropertyWatchpoint(Structure* structure) const
    {
        if (!*this)
            return false;
        
        switch (m_kind) {
        case Presence:
        case Absence:
        case Equivalence:
            return structure->needImpurePropertyWatchpoint();
        default:
            return false;
        }
    }
    
    bool PropertyCondition::isStillValid(Structure* structure, JSObject* base) const
    {
        if (!isStillValidAssumingImpurePropertyWatchpoint(structure, base))
            return false;
    
        // Currently we assume that an impure property can cause a property to appear, and can also
        // "shadow" an existing JS property on the same object. Hence it affects both presence and
        // absence. It doesn't affect AbsenceOfSetter because impure properties aren't ever setters.
        switch (m_kind) {
        case Absence:
            if (structure->typeInfo().getOwnPropertySlotIsImpure() || structure->typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence())
                return false;
            break;
        case Presence:
        case Equivalence:
            if (structure->typeInfo().getOwnPropertySlotIsImpure())
                return false;
            break;
        default:
            break;
        }
        
        return true;
    }
    
    bool PropertyCondition::isWatchableWhenValid(
        Structure* structure, WatchabilityEffort effort) const
    {
        if (structure->transitionWatchpointSetHasBeenInvalidated())
            return false;
        
        switch (m_kind) {
        case Equivalence: {
            PropertyOffset offset = structure->getConcurrently(uid());
            
            // This method should only be called when some variant of isValid returned true, which
            // implies that we already confirmed that the structure knows of the property. We should
            // also have verified that the Structure is a cacheable dictionary, which means we
            // shouldn't have a TOCTOU race either.
            RELEASE_ASSERT(offset != invalidOffset);
            
            WatchpointSet* set = nullptr;
            switch (effort) {
            case MakeNoChanges:
                set = structure->propertyReplacementWatchpointSet(offset);
                break;
            case EnsureWatchability:
                set = structure->ensurePropertyReplacementWatchpointSet(
                    *Heap::heap(structure)->vm(), offset);
                break;
            }
            
            if (!set || !set->isStillValid())
                return false;
            
            break;
        }
            
        default:
            break;
        }
        
        return true;
    }
    
    bool PropertyCondition::isWatchableAssumingImpurePropertyWatchpoint(
        Structure* structure, JSObject* base, WatchabilityEffort effort) const
    {
        return isStillValidAssumingImpurePropertyWatchpoint(structure, base)
            && isWatchableWhenValid(structure, effort);
    }
    
    bool PropertyCondition::isWatchable(
        Structure* structure, JSObject* base, WatchabilityEffort effort) const
    {
        return isStillValid(structure, base)
            && isWatchableWhenValid(structure, effort);
    }
    
    bool PropertyCondition::isStillLive() const
    {
        if (hasPrototype() && prototype() && !Heap::isMarked(prototype()))
            return false;
        
        if (hasRequiredValue()
            && requiredValue()
            && requiredValue().isCell()
            && !Heap::isMarked(requiredValue().asCell()))
            return false;
        
        return true;
    }
    
    void PropertyCondition::validateReferences(const TrackedReferences& tracked) const
    {
        if (hasPrototype())
            tracked.check(prototype());
        
        if (hasRequiredValue())
            tracked.check(requiredValue());
    }
    
    bool PropertyCondition::isValidValueForAttributes(JSValue value, unsigned attributes)
    {
        bool attributesClaimAccessor = !!(attributes & Accessor);
        bool valueClaimsAccessor = !!jsDynamicCast<GetterSetter*>(value);
        return attributesClaimAccessor == valueClaimsAccessor;
    }
    
    bool PropertyCondition::isValidValueForPresence(JSValue value) const
    {
        return isValidValueForAttributes(value, attributes());
    }
    
    PropertyCondition PropertyCondition::attemptToMakeEquivalenceWithoutBarrier(JSObject* base) const
    {
        Structure* structure = base->structure();
        if (!structure->isValidOffset(offset()))
            return PropertyCondition();
        JSValue value = base->getDirect(offset());
        if (!isValidValueForPresence(value))
            return PropertyCondition();
        return equivalenceWithoutBarrier(uid(), value);
    }
    
    } // namespace JSC
    
    namespace WTF {
    
    void printInternal(PrintStream& out, JSC::PropertyCondition::Kind condition)
    {
        switch (condition) {
        case JSC::PropertyCondition::Presence:
            out.print("Presence");
            return;
        case JSC::PropertyCondition::Absence:
            out.print("Absence");
            return;
        case JSC::PropertyCondition::AbsenceOfSetter:
            out.print("Absence");
            return;
        case JSC::PropertyCondition::Equivalence:
            out.print("Equivalence");
            return;
        }
        RELEASE_ASSERT_NOT_REACHED();
    }
    
    } // namespace WTF