summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/bytecode/PropertyCondition.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/bytecode/PropertyCondition.cpp')
-rw-r--r--Source/JavaScriptCore/bytecode/PropertyCondition.cpp364
1 files changed, 364 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/bytecode/PropertyCondition.cpp b/Source/JavaScriptCore/bytecode/PropertyCondition.cpp
new file mode 100644
index 000000000..347b86f8b
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/PropertyCondition.cpp
@@ -0,0 +1,364 @@
+/*
+ * 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;
+ 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