text: /*
     +----------------------------------------------------------------------+
     | HipHop for PHP                                                       |
     +----------------------------------------------------------------------+
     | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com)     |
     +----------------------------------------------------------------------+
     | This source file is subject to version 3.01 of the PHP license,      |
     | that is bundled with this package in the file LICENSE, and is        |
     | available through the world-wide-web at the following url:           |
     | http://www.php.net/license/3_01.txt                                  |
     | If you did not receive a copy of the PHP license and are unable to   |
     | obtain it through the world-wide-web, please send a note to          |
     | license@php.net so we can mail you a copy immediately.               |
     +----------------------------------------------------------------------+
  */
  
  #ifndef incl_HPHP_VM_UNIT_EMITTER_H_
  #define incl_HPHP_VM_UNIT_EMITTER_H_
  
  #include <list>
  #include <memory>
  #include <string>
  #include <utility>
  #include <vector>
  
  #include "hphp/parser/location.h"
  
  #include "hphp/runtime/base/types.h"
  #include "hphp/runtime/base/string-data.h"
  #include "hphp/runtime/base/typed-value.h"
  #include "hphp/runtime/vm/preclass.h"
  #include "hphp/runtime/vm/repo-helpers.h"
  #include "hphp/runtime/vm/type-alias.h"
  #include "hphp/runtime/vm/unit.h"
  
  #include "hphp/util/functional.h"
  #include "hphp/util/hash-map-typedefs.h"
  #include "hphp/util/md5.h"
  
  namespace HPHP {
  ///////////////////////////////////////////////////////////////////////////////
  
  struct FuncEmitter;
  struct PreClassEmitter;
  struct StringData;
  
  ///////////////////////////////////////////////////////////////////////////////
  
  /*
   * Pre-runtime representation of Unit used to emit bytecode and instantiate
   * runtime Units.
   */
  struct UnitEmitter {
    friend class UnitRepoProxy;
  
    /////////////////////////////////////////////////////////////////////////////
    // Initialization and execution.
  
    explicit UnitEmitter(const MD5& md5);
    ~UnitEmitter();
  
    /*
     * Commit this unit to a repo.
     */
    void commit(UnitOrigin unitOrigin);
  
    /*
     * Insert this unit in a repo as part of transaction `txn'.
     */
    bool insert(UnitOrigin unitOrigin, RepoTxn& txn);
  
    /*
     * Instatiate a runtime Unit*.
     */
    std::unique_ptr<Unit> create();
  
    template<class SerDe> void serdeMetaData(SerDe&);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // Basic data.
  
    /*
     * The MD5 hash of the Unit.
     */
    const MD5& md5() const;
  
    /*
     * Bytecode pointer and current emit position.
     */
    const unsigned char* bc() const;
    Offset bcPos() const;
  
    /*
     * Set the bytecode pointer by allocating a copy of `bc' with size `bclen'.
     *
     * Not safe to call with m_bc as the argument because we free our current
     * bytecode stream before allocating a copy of `bc'.
     */
    void setBc(const unsigned char* bc, size_t bclen);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // Litstrs and Arrays.
  
    /*
     * Look up a static string or array by ID.
     */
    const StringData* lookupLitstr(Id id) const;
    const ArrayData* lookupArray(Id id) const;
  
    /*
     * Merge a literal string into either the global LitstrTable or the table for
     * the Unit.
     */
    Id mergeLitstr(const StringData* litstr);
  
    /*
     * Merge a literal string into the table for the Unit.
     */
    Id mergeUnitLitstr(const StringData* litstr);
  
    /*
     * Merge a scalar array into the Unit.
     *
     * When `key' is provided, it should be the serialization of `a'; when not
     * provided, the array is serialized in the call.
     */
    Id mergeArray(const ArrayData* a);
    Id mergeArray(const ArrayData* a, const std::string& key);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // FuncEmitters.
  
    /*
     * The Unit's pseudomain emitter.
     */
    FuncEmitter* getMain();
  
    /*
     * Const reference to all of the Unit's FuncEmitters.
     */
    const std::vector<FuncEmitter*>& fevec() const;
  
    /*
     * Create the pseudomain emitter for the Unit.
     *
     * @requires: fevec().size() == 0
     */
    void initMain(int line1, int line2);
  
    /*
     * Create a trivial (i.e., Int 1; RetC) pseudomain emitter for the Unit.
     *
     * @requires: fevec().size() == 0
     */
    void addTrivialPseudoMain();
  
    /*
     * Create a new FuncEmitter and add it to the FE vector.
     */
    FuncEmitter* newFuncEmitter(const StringData* name);
  
    /*
     * Create a new FuncEmitter for the method given by `name' and `pce'.
     *
     * Does /not/ add it to the FE vector.
     */
    FuncEmitter* newMethodEmitter(const StringData* name, PreClassEmitter* pce);
  
    /*
     * Add `fe' to the FE vector.
     */
    void appendTopEmitter(FuncEmitter* fe);
  
    /*
     * Finish adding a FuncEmitter to the Unit and record its bytecode range.
     *
     * This can only be done once for each FuncEmitter, after it is added to the
     * FE vector.  None of the bytecode ranges of FuncEmitters added to the Unit
     * are allowed to overlap.
     *
     * Takes logical ownership of `fe'.
     */
    void recordFunction(FuncEmitter* fe);
  
    /*
     * Create a new function for `fe'.
     *
     * This should only be called from fe->create(), and just constructs a new
     * Func* and records it as emitted from `fe'.
     */
    Func* newFunc(const FuncEmitter* fe, Unit& unit, PreClass* preClass,
                  int line1, int line2, Offset base, Offset past,
                  const StringData* name, Attr attrs, bool top,
                  const StringData* docComment, int numParams,
                  bool needsNextClonedClosure);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // PreClassEmitters.
  
    /*
     * Number of PreClassEmitters in the Unit.
     */
    size_t numPreClasses() const;
  
    /*
     * The PreClassEmitter for `preClassId'.
     */
    const PreClassEmitter* pce(Id preClassId) const;
    PreClassEmitter* pce(Id preClassId);
  
    /*
     * Create a new PreClassEmitter and add it to the PCE vector.
     *
     * @see: PreClass::Hoistable
     */
    PreClassEmitter* newPreClassEmitter(const StringData* name,
                                        PreClass::Hoistable hoistable);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // Type aliases.
  
    /*
     * Const reference to all of the Unit's type aliases.
     */
    const std::vector<TypeAlias>& typeAliases() const;
  
    /*
     * Add a new type alias to the Unit.
     */
    Id addTypeAlias(const TypeAlias& td);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // Source locations.
  
    /*
     * Return a copy of the SrcLocTable for the Unit, if it has one; otherwise,
     * return an empty table.
     */
    SourceLocTable createSourceLocTable() const;
  
    /*
     * Does this Unit contain full source location information?
     *
     * Generally, UnitEmitters loaded from a production repo will have a
     * LineTable only instead of a full SourceLocTable.
     */
    bool hasSourceLocInfo() const;
  
    /*
     * Const reference to the Unit's LineTable.
     */
    const LineTable& lineTable() const;
  
    /*
     * Record source location information for the last chunk of bytecode added to
     * this UnitEmitter.
     *
     * Adjacent regions associated with the same source line will be collapsed as
     * this is created.
     */
    void recordSourceLocation(const Location* sLoc, Offset start);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // Mergeables.
    //
    // See unit.h for documentation of Unit merging.
  
    /*
     * Append a PreClassEmitter to the UnitEmitter's list of mergeables.
     */
    void pushMergeableClass(PreClassEmitter* e);
  
    /*
     * Add a Unit include to the UnitEmitter's list of mergeables.
     *
     * The `push' flavor first merges the litstr `unitName', and then appends the
     * mergeable object reference to the list.  The `insert' flavor inserts the
     * mergeable (kind, id) at `ix' in the list.
     */
    void pushMergeableInclude(Unit::MergeKind kind, const StringData* unitName);
    void insertMergeableInclude(int ix, Unit::MergeKind kind, Id id);
  
    /*
     * Add a constant definition to the UnitEmitter's list of mergeables.
     *
     * The `push' flavor first merges the litstr `unitName', and then appends the
     * mergeable object reference to the list.  The `insert' flavor inserts the
     * mergeable (kind, id) at `ix' in the list.
     *
     * The mergeable value is appended or inserted likewise.
     */
    void pushMergeableDef(Unit::MergeKind kind, const StringData* name,
                          const TypedValue& tv);
    void insertMergeableDef(int ix, Unit::MergeKind kind, Id id,
                            const TypedValue& tv);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // Bytecode emit.
    //
    // These methods emit values to bc() at bcPos() and then updates bcPos(),
    // realloc-ing the bytecode region if necessary.
  
    void emitOp(Op op, int64_t pos = -1);
    void emitByte(unsigned char n, int64_t pos = -1);
  
    void emitInt32(int n, int64_t pos = -1);
    void emitInt64(int64_t n, int64_t pos = -1);
    void emitDouble(double n, int64_t pos = -1);
  
    template<typename T>
    void emitIVA(T n);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // Other methods.
  
    /*
     * Is this a Unit for a systemlib?
     */
    bool isASystemLib() const;
  
  private:
    /*
     * Bytecode emit implementation.
     */
    template<class T>
    void emitImpl(T n, int64_t pos);
  
  
    /////////////////////////////////////////////////////////////////////////////
    // Data members.
  
  private:
    // Initial bytecode size.
    static const size_t BCMaxInit = 4096;
  
  public:
    int m_repoId{-1};
    int64_t m_sn{-1};
    const StringData* m_filepath{nullptr};
  
    bool m_mergeOnly{false};
    bool m_isHHFile{false};
    bool m_returnSeen{false};
    int m_preloadPriority{0};
    TypedValue m_mainReturn;
  
  private:
    MD5 m_md5;
  
    unsigned char* m_bc;
    size_t m_bclen;
    size_t m_bcmax;
  
    int m_nextFuncSn;
  
    /*
     * Litstr tables.
     */
    hphp_hash_map<const StringData*, Id,
                  string_data_hash, string_data_same> m_litstr2id;
    std::vector<const StringData*> m_litstrs;
  
    /*
     * Scalar array tables.
     */
    struct ArrayVecElm {
      std::string serialized;
      const ArrayData* array;
    };
    hphp_hash_map<std::string, Id, string_hash> m_array2id;
    std::vector<ArrayVecElm> m_arrays;
  
    /*
     * Type alias table.
     */
    std::vector<TypeAlias> m_typeAliases;
  
    /*
     * FuncEmitter tables.
     */
    std::vector<FuncEmitter*> m_fes;
    hphp_hash_map<const FuncEmitter*, const Func*,
                  pointer_hash<FuncEmitter>> m_fMap;
  
    /*
     * PreClassEmitter table.
     */
    std::vector<PreClassEmitter*> m_pceVec;
  
    /*
     * Hoistability tables.
     */
    bool m_allClassesHoistable;
    hphp_hash_set<const StringData*,
                  string_data_hash,
                  string_data_isame> m_hoistablePreClassSet;
    std::list<Id> m_hoistablePceIdList;
  
    /*
     * Mergeables tables.
     */
    std::vector<std::pair<Unit::MergeKind, Id>> m_mergeableStmts;
    std::vector<std::pair<Id, TypedValue>> m_mergeableValues;
  
    /*
     * Source location tables.
     *
     * Each entry encodes an open-closed range of bytecode offsets.
     *
     * The m_sourceLocTab is keyed by the start of each half-open range.  This is
     * to allow appending new bytecode offsets that are part of the same range to
     * coalesce.
     *
     * The m_feTab and m_lineTable are keyed by the past-the-end offset.  This is
     * the format we'll want them in when we go to create a Unit.
     */
    std::vector<std::pair<Offset,SourceLoc>> m_sourceLocTab;
    std::vector<std::pair<Offset,const FuncEmitter*>> m_feTab;
    LineTable m_lineTable;
  };
  
  ///////////////////////////////////////////////////////////////////////////////
  
  /*
   * Proxy for converting in-repo unit representations into UnitEmitters.
   */
  struct UnitRepoProxy : public RepoProxy {
    friend class Unit;
    friend class UnitEmitter;
  
    explicit UnitRepoProxy(Repo& repo);
    ~UnitRepoProxy();
    void createSchema(int repoId, RepoTxn& txn);
    std::unique_ptr<Unit> load(const std::string& name, const MD5& md5);
    std::unique_ptr<UnitEmitter> loadEmitter(const std::string& name,
                                             const MD5& md5);
  
    void insertUnitLineTable(int repoId, RepoTxn& txn, int64_t unitSn,
                             LineTable& lineTable);
    void getUnitLineTable(int repoId, int64_t unitSn, LineTable& lineTable);
  
  #define URP_IOP(o) URP_OP(Insert##o, insert##o)
  #define URP_GOP(o) URP_OP(Get##o, get##o)
  #define URP_OPS \
    URP_IOP(Unit) \
    URP_GOP(Unit) \
    URP_IOP(UnitLitstr) \
    URP_GOP(UnitLitstrs) \
    URP_IOP(UnitArray) \
    URP_GOP(UnitArrays) \
    URP_IOP(UnitMergeable) \
    URP_GOP(UnitMergeables) \
    URP_IOP(UnitSourceLoc) \
    URP_GOP(SourceLocTab)
  
    class InsertUnitStmt : public RepoProxy::Stmt {
     public:
      InsertUnitStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      void insert(const UnitEmitter& ue,
                  RepoTxn& txn,
                  int64_t& unitSn,
                  const MD5& md5,
                  const unsigned char* bc,
                  size_t bclen);
    };
    class GetUnitStmt : public RepoProxy::Stmt {
     public:
      GetUnitStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      bool get(UnitEmitter& ue, const MD5& md5);
    };
    class InsertUnitLitstrStmt : public RepoProxy::Stmt {
     public:
      InsertUnitLitstrStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      void insert(RepoTxn& txn, int64_t unitSn, Id litstrId,
                  const StringData* litstr);
    };
    class GetUnitLitstrsStmt : public RepoProxy::Stmt {
     public:
      GetUnitLitstrsStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      void get(UnitEmitter& ue);
    };
    class InsertUnitArrayStmt : public RepoProxy::Stmt {
     public:
      InsertUnitArrayStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      void insert(RepoTxn& txn, int64_t unitSn, Id arrayId,
                  const std::string& array);
    };
    class GetUnitArraysStmt : public RepoProxy::Stmt {
     public:
      GetUnitArraysStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      void get(UnitEmitter& ue);
    };
    class InsertUnitMergeableStmt : public RepoProxy::Stmt {
     public:
      InsertUnitMergeableStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      void insert(RepoTxn& txn, int64_t unitSn,
                  int ix, Unit::MergeKind kind,
                  Id id, TypedValue* value);
    };
    class GetUnitMergeablesStmt : public RepoProxy::Stmt {
     public:
      GetUnitMergeablesStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      void get(UnitEmitter& ue);
    };
    class InsertUnitSourceLocStmt : public RepoProxy::Stmt {
     public:
      InsertUnitSourceLocStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      void insert(RepoTxn& txn, int64_t unitSn, Offset pastOffset, int line0,
                  int char0, int line1, int char1);
    };
    class GetSourceLocTabStmt : public RepoProxy::Stmt {
     public:
      GetSourceLocTabStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {}
      bool get(int64_t unitSn, SourceLocTable& sourceLocTab);
    };
  
  private:
    bool loadHelper(UnitEmitter& ue, const std::string&, const MD5&);
  
  #define URP_OP(c, o) \
   public: \
    c##Stmt& o(int repoId) { return *m_##o[repoId]; } \
   private: \
    c##Stmt m_##o##Local; \
    c##Stmt m_##o##Central; \
    c##Stmt* m_##o[RepoIdCount];
    URP_OPS
  #undef URP_OP
  };
  
  ///////////////////////////////////////////////////////////////////////////////
  }
  
  #define incl_HPHP_VM_UNIT_EMITTER_INL_H_
  #include "hphp/runtime/vm/unit-emitter-inl.h"
  #undef incl_HPHP_VM_UNIT_EMITTER_INL_H_
  
  #endif // incl_HPHP_VM_UNIT_EMITTER_H_
  