We should support CreateThis in the FTL
[WebKit-https.git] / Source / JavaScriptCore / bytecode / PutByIdVariant.cpp
index 33d8e94..c39869d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-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
 #include "config.h"
 #include "PutByIdVariant.h"
 
+#include "CallLinkStatus.h"
+#include "JSCInlines.h"
 #include <wtf/ListDump.h>
 
 namespace JSC {
 
+PutByIdVariant::PutByIdVariant(const PutByIdVariant& other)
+    : PutByIdVariant()
+{
+    *this = other;
+}
+
+PutByIdVariant& PutByIdVariant::operator=(const PutByIdVariant& other)
+{
+    m_kind = other.m_kind;
+    m_oldStructure = other.m_oldStructure;
+    m_newStructure = other.m_newStructure;
+    m_conditionSet = other.m_conditionSet;
+    m_offset = other.m_offset;
+    m_requiredType = other.m_requiredType;
+    if (other.m_callLinkStatus)
+        m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus);
+    else
+        m_callLinkStatus = nullptr;
+    return *this;
+}
+
+PutByIdVariant PutByIdVariant::replace(
+    const StructureSet& structure, PropertyOffset offset, const InferredType::Descriptor& requiredType)
+{
+    PutByIdVariant result;
+    result.m_kind = Replace;
+    result.m_oldStructure = structure;
+    result.m_offset = offset;
+    result.m_requiredType = requiredType;
+    return result;
+}
+
+PutByIdVariant PutByIdVariant::transition(
+    const StructureSet& oldStructure, Structure* newStructure,
+    const ObjectPropertyConditionSet& conditionSet, PropertyOffset offset,
+    const InferredType::Descriptor& requiredType)
+{
+    PutByIdVariant result;
+    result.m_kind = Transition;
+    result.m_oldStructure = oldStructure;
+    result.m_newStructure = newStructure;
+    result.m_conditionSet = conditionSet;
+    result.m_offset = offset;
+    result.m_requiredType = requiredType;
+    return result;
+}
+
+PutByIdVariant PutByIdVariant::setter(
+    const StructureSet& structure, PropertyOffset offset,
+    const ObjectPropertyConditionSet& conditionSet,
+    std::unique_ptr<CallLinkStatus> callLinkStatus)
+{
+    PutByIdVariant result;
+    result.m_kind = Setter;
+    result.m_oldStructure = structure;
+    result.m_conditionSet = conditionSet;
+    result.m_offset = offset;
+    result.m_callLinkStatus = WTFMove(callLinkStatus);
+    result.m_requiredType = InferredType::Top;
+    return result;
+}
+
 Structure* PutByIdVariant::oldStructureForTransition() const
 {
-    ASSERT(kind() == Transition);
-    ASSERT(m_oldStructure.size() <= 2);
+    RELEASE_ASSERT(kind() == Transition);
+    RELEASE_ASSERT(m_oldStructure.size() <= 2);
     for (unsigned i = m_oldStructure.size(); i--;) {
         Structure* structure = m_oldStructure[i];
         if (structure != m_newStructure)
             return structure;
     }
     RELEASE_ASSERT_NOT_REACHED();
+
+    return nullptr;
+}
+
+void PutByIdVariant::fixTransitionToReplaceIfNecessary()
+{
+    if (kind() != Transition)
+        return;
+    
+    RELEASE_ASSERT(m_oldStructure.size() <= 2);
+    for (unsigned i = m_oldStructure.size(); i--;) {
+        Structure* structure = m_oldStructure[i];
+        if (structure != m_newStructure)
+            return;
+    }
+    
+    m_newStructure = nullptr;
+    m_kind = Replace;
+    m_conditionSet = ObjectPropertyConditionSet();
+    RELEASE_ASSERT(!m_callLinkStatus);
 }
 
 bool PutByIdVariant::writesStructures() const
 {
-    return kind() == Transition;
+    switch (kind()) {
+    case Transition:
+    case Setter:
+        return true;
+    default:
+        return false;
+    }
 }
 
 bool PutByIdVariant::reallocatesStorage() const
 {
-    if (kind() != Transition)
-        return false;
-    
-    if (oldStructureForTransition()->outOfLineCapacity() == newStructure()->outOfLineCapacity())
+    switch (kind()) {
+    case Transition:
+        return oldStructureForTransition()->outOfLineCapacity() != newStructure()->outOfLineCapacity();
+    case Setter:
+        return true;
+    default:
         return false;
-    
-    return true;
+    }
+}
+
+bool PutByIdVariant::makesCalls() const
+{
+    return kind() == Setter;
 }
 
 bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other)
 {
     if (m_offset != other.m_offset)
         return false;
+
+    if (m_requiredType != other.m_requiredType)
+        return false;
     
     switch (m_kind) {
-    case Replace:
+    case NotSet:
+        RELEASE_ASSERT_NOT_REACHED();
+        return false;
+        
+    case Replace: {
         switch (other.m_kind) {
         case Replace: {
-            ASSERT(m_constantChecks.isEmpty());
-            ASSERT(other.m_constantChecks.isEmpty());
+            ASSERT(m_conditionSet.isEmpty());
+            ASSERT(other.m_conditionSet.isEmpty());
             
             m_oldStructure.merge(other.m_oldStructure);
             return true;
@@ -86,19 +189,63 @@ bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other)
         default:
             return false;
         }
+    }
         
     case Transition:
         switch (other.m_kind) {
         case Replace:
             return attemptToMergeTransitionWithReplace(other);
             
+        case Transition: {
+            if (m_oldStructure != other.m_oldStructure)
+                return false;
+            
+            if (m_newStructure != other.m_newStructure)
+                return false;
+            
+            ObjectPropertyConditionSet mergedConditionSet;
+            if (!m_conditionSet.isEmpty()) {
+                mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
+                if (!mergedConditionSet.isValid())
+                    return false;
+            }
+            m_conditionSet = mergedConditionSet;
+            return true;
+        }
+            
         default:
             return false;
         }
         
-    default:
-        return false;
-    }
+    case Setter: {
+        if (other.m_kind != Setter)
+            return false;
+        
+        if (m_callLinkStatus || other.m_callLinkStatus) {
+            if (!(m_callLinkStatus && other.m_callLinkStatus))
+                return false;
+        }
+        
+        if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty())
+            return false;
+        
+        ObjectPropertyConditionSet mergedConditionSet;
+        if (!m_conditionSet.isEmpty()) {
+            mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
+            if (!mergedConditionSet.isValid() || !mergedConditionSet.hasOneSlotBaseCondition())
+                return false;
+        }
+        m_conditionSet = mergedConditionSet;
+        
+        if (m_callLinkStatus)
+            m_callLinkStatus->merge(*other.m_callLinkStatus);
+        
+        m_oldStructure.merge(other.m_oldStructure);
+        return true;
+    } }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+    return false;
 }
 
 bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& replace)
@@ -108,6 +255,7 @@ bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& r
     ASSERT(m_offset == replace.m_offset);
     ASSERT(!replace.writesStructures());
     ASSERT(!replace.reallocatesStorage());
+    ASSERT(replace.conditionSet().isEmpty());
     
     // This sort of merging only works when we have one path along which we add a new field which
     // transitions to structure S while the other path was already on structure S. This doesn't
@@ -123,6 +271,26 @@ bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& r
     return true;
 }
 
+void PutByIdVariant::markIfCheap(SlotVisitor& visitor)
+{
+    m_oldStructure.markIfCheap(visitor);
+    if (m_newStructure)
+        m_newStructure->markIfCheap(visitor);
+}
+
+bool PutByIdVariant::finalize()
+{
+    if (!m_oldStructure.isStillAlive())
+        return false;
+    if (m_newStructure && !Heap::isMarked(m_newStructure))
+        return false;
+    if (!m_conditionSet.areStillLive())
+        return false;
+    if (m_callLinkStatus && !m_callLinkStatus->finalize())
+        return false;
+    return true;
+}
+
 void PutByIdVariant::dump(PrintStream& out) const
 {
     dumpInContext(out, 0);
@@ -137,14 +305,25 @@ void PutByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
         
     case Replace:
         out.print(
-            "<Replace: ", inContext(structure(), context), ", ", offset(), ">");
+            "<Replace: ", inContext(structure(), context), ", offset = ", offset(), ", ",
+            inContext(requiredType(), context), ">");
         return;
         
     case Transition:
         out.print(
-            "<Transition: ", inContext(oldStructure(), context), " -> ",
+            "<Transition: ", inContext(oldStructure(), context), " to ",
             pointerDumpInContext(newStructure(), context), ", [",
-            listDumpInContext(constantChecks(), context), "], ", offset(), ">");
+            inContext(m_conditionSet, context), "], offset = ", offset(), ", ",
+            inContext(requiredType(), context), ">");
+        return;
+        
+    case Setter:
+        out.print(
+            "<Setter: ", inContext(structure(), context), ", [",
+            inContext(m_conditionSet, context), "]");
+        out.print(", offset = ", m_offset);
+        out.print(", call = ", *m_callLinkStatus);
+        out.print(">");
         return;
     }