text: /*
   * Copyright (C) 2015-2016 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. 
   */
  
  #pragma once
  
  #include "ConcurrentJSLock.h"
  #include "JSCell.h"
  #include "PropertyName.h"
  #include "PutByIdFlags.h"
  #include "Watchpoint.h"
  #include <wtf/ThreadSafeRefCounted.h>
  
  namespace JSC {
  
  // This is an object used for the inference of the types of object properties.
  
  class InferredType final : public JSCell {
  public:
      typedef JSCell Base;
  
      static InferredType* create(VM&);
  
      static const bool needsDestruction = true;
      static void destroy(JSCell*);
  
      static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags;
      
      static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
  
      static void visitChildren(JSCell*, SlotVisitor&);
  
      DECLARE_INFO;
  
      enum Kind : uint8_t {
          Bottom,
          Boolean,
          Other,
          Int32,
          Number,
          String,
          Symbol,
          ObjectWithStructure,
          ObjectWithStructureOrOther,
          Object,
          ObjectOrOther,
          Top
      };
  
      static Kind kindForFlags(PutByIdFlags);
      
      static bool hasStructure(Kind kind)
      {
          switch (kind) {
          case ObjectWithStructure:
          case ObjectWithStructureOrOther:
              return true;
          default:
              return false;
          }
      }
  
      class Descriptor {
      public:
          Descriptor()
              : m_kind(Bottom)
              , m_structure(nullptr)
          {
          }
  
          Descriptor(Kind kind, Structure* structure = nullptr)
              : m_kind(kind)
              , m_structure(structure)
          {
              if (hasStructure(kind))
                  ASSERT(structure);
          }
  
          static Descriptor forValue(JSValue);
  
          static Descriptor forFlags(VM&, PutByIdFlags);
  
          explicit operator bool() const
          {
              return m_kind != Bottom || m_structure;
          }
  
          Kind kind() const { return m_kind; }
          Structure* structure() const { return m_structure; }
  
          PutByIdFlags putByIdFlags() const;
  
          ALWAYS_INLINE bool includesValue(JSValue value)
          {
              switch (m_kind) {
              case Bottom:
                  return false;
              case Boolean:
                  return value.isBoolean();
              case Other:
                  return value.isUndefinedOrNull();
              case Int32:
                  return value.isInt32();
              case Number:
                  return value.isNumber();
              case String:
                  return value.isString();
              case Symbol:
                  return value.isSymbol();
              case ObjectWithStructure:
                  return value.isCell() && value.asCell()->structure() == m_structure;
              case ObjectWithStructureOrOther:
                  return value.isUndefinedOrNull()
                      || (value.isCell() && value.asCell()->structure() == m_structure);
              case Object:
                  return value.isObject();
              case ObjectOrOther:
                  return value.isUndefinedOrNull() || value.isObject();
              case Top:
                  return true;
              }
  
              RELEASE_ASSERT_NOT_REACHED();
          }
  
          bool operator==(const Descriptor& other) const
          {
              return m_kind == other.m_kind
                  && m_structure == other.m_structure;
          }
  
          bool operator!=(const Descriptor& other) const
          {
              return !(*this == other);
          }
  
          unsigned hash() const
          {
              return WTF::PtrHash<Structure*>::hash(m_structure) ^ static_cast<unsigned>(m_kind);
          }
      
          void merge(const Descriptor&);
          void removeStructure();
  
          // Returns true if this descriptor is more general than the other one.
          bool subsumes(const Descriptor&) const;
  
          void dumpInContext(PrintStream&, DumpContext*) const;
          void dump(PrintStream&) const;
  
      private:
          Kind m_kind;
          Structure* m_structure;
      };
  
      ConcurrentJSLock& lock() const { return m_lock; }
  
      Descriptor descriptorMainThread() const
      {
          return Descriptor(m_kind, m_structure ? m_structure->structure() : nullptr);
      }
      
      Descriptor descriptor(const ConcurrentJSLocker&) const
      {
          return descriptorMainThread();
      }
      Descriptor descriptor() const
      {
          ConcurrentJSLocker locker(m_lock);
          return descriptor(locker);
      }
      
      Kind kind(const ConcurrentJSLocker& locker) const { return descriptor(locker).kind(); }
  
      bool isTop() const { return m_kind == Top; }
      bool isRelevant() const { return m_kind != Top; }
  
      // Returns true if the InferredType is still relevant after the store. It's not relevant if it's Top.
      ALWAYS_INLINE bool willStoreValue(VM& vm, PropertyName propertyName, JSValue value)
      {
          Descriptor currentDescriptor = descriptorMainThread();
          if (currentDescriptor.includesValue(value))
              return currentDescriptor.kind() != Top;
          return willStoreValueSlow(vm, propertyName, value);
      }
  
      // Immediately makes this type irrelevant.
      void makeTop(VM& vm, PropertyName propertyName)
      {
          if (isTop())
              return;
          makeTopSlow(vm, propertyName);
      }
  
      // Returns true if it currently makes sense to watch this InferredType for this descriptor. Note that
      // this will always return false for Top.
      bool canWatch(const ConcurrentJSLocker&, const Descriptor&);
      bool canWatch(const Descriptor&);
      
      void addWatchpoint(const ConcurrentJSLocker&, Watchpoint*);
      void addWatchpoint(Watchpoint*);
  
      void dump(PrintStream&) const;
  
  private:
      InferredType(VM&);
      ~InferredType();
  
      bool willStoreValueSlow(VM&, PropertyName, JSValue);
      void makeTopSlow(VM&, PropertyName);
  
      // Helper for willStoreValueSlow() and makeTopSlow(). This returns true if we should fire the
      // watchpoint set.
      bool set(const ConcurrentJSLocker&, VM&, Descriptor);
      
      void removeStructure();
  
      mutable ConcurrentJSLock m_lock;
      
      Kind m_kind { Bottom };
  
      class InferredStructureWatchpoint : public Watchpoint {
      public:
          InferredStructureWatchpoint() { }
      protected:
          void fireInternal(const FireDetail&) override;
      };
  
      class InferredStructureFinalizer : public UnconditionalFinalizer {
      public:
          InferredStructureFinalizer() { }
      protected:
          void finalizeUnconditionally() override;
      };
  
      class InferredStructure : public ThreadSafeRefCounted<InferredStructure> {
      public:
          InferredStructure(VM&, InferredType* parent, Structure*);
  
          Structure* structure() const { return m_structure.get(); };
  
      private:
          friend class InferredType;
          friend class InferredStructureWatchpoint;
          friend class InferredStructureFinalizer;
          
          InferredType* m_parent;
          WriteBarrier<Structure> m_structure;
  
          InferredStructureWatchpoint m_watchpoint;
          InferredStructureFinalizer m_finalizer;
      };
  
      RefPtr<InferredStructure> m_structure;
  
      // NOTE: If this is being watched, we transform to Top because that implies that it wouldn't be
      // profitable to watch it again. Also, this set is initialized clear, and is never exposed to the DFG
      // thread. The DFG will use the InferredType as the thing that it watches.
      InlineWatchpointSet m_watchpointSet;
  };
  
  } // namespace JSC
  
  namespace WTF {
  
  void printInternal(PrintStream&, JSC::InferredType::Kind);
  
  } // namespace WTF
  