text: /****************************************************************************
  **
  ** Copyright (C) 2016 The Qt Company Ltd.
  ** Contact: https://www.qt.io/licensing/
  **
  ** This file is part of the QtQml module of the Qt Toolkit.
  **
  ** $QT_BEGIN_LICENSE:LGPL$
  ** Commercial License Usage
  ** Licensees holding valid commercial Qt licenses may use this file in
  ** accordance with the commercial license agreement provided with the
  ** Software or, alternatively, in accordance with the terms contained in
  ** a written agreement between you and The Qt Company. For licensing terms
  ** and conditions see https://www.qt.io/terms-conditions. For further
  ** information use the contact form at https://www.qt.io/contact-us.
  **
  ** GNU Lesser General Public License Usage
  ** Alternatively, this file may be used under the terms of the GNU Lesser
  ** General Public License version 3 as published by the Free Software
  ** Foundation and appearing in the file LICENSE.LGPL3 included in the
  ** packaging of this file. Please review the following information to
  ** ensure the GNU Lesser General Public License version 3 requirements
  ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
  **
  ** GNU General Public License Usage
  ** Alternatively, this file may be used under the terms of the GNU
  ** General Public License version 2.0 or (at your option) the GNU General
  ** Public license version 3 or any later version approved by the KDE Free
  ** Qt Foundation. The licenses are as published by the Free Software
  ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
  ** included in the packaging of this file. Please review the following
  ** information to ensure the GNU General Public License requirements will
  ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
  ** https://www.gnu.org/licenses/gpl-3.0.html.
  **
  ** $QT_END_LICENSE$
  **
  ****************************************************************************/
  #ifndef QV4JSIR_P_H
  #define QV4JSIR_P_H
  
  //
  //  W A R N I N G
  //  -------------
  //
  // This file is not part of the Qt API.  It exists purely as an
  // implementation detail.  This header file may change from version to
  // version without notice, or even be removed.
  //
  // We mean it.
  //
  
  #include "private/qv4global_p.h"
  #include <private/qqmljsmemorypool_p.h>
  #include <private/qqmljsastfwd_p.h>
  #include <private/qflagpointer_p.h>
  #ifndef V4_BOOTSTRAP
  #include <private/qqmlmetatype_p.h>
  #endif
  
  #include <QtCore/private/qnumeric_p.h>
  #include <QtCore/QVector>
  #include <QtCore/QString>
  #include <QtCore/QBitArray>
  #include <QtCore/qurl.h>
  #include <QtCore/QVarLengthArray>
  #include <QtCore/QDateTime>
  #include <qglobal.h>
  
  #if defined(CONST) && defined(Q_OS_WIN)
  # define QT_POP_CONST
  # pragma push_macro("CONST")
  # undef CONST // CONST conflicts with our own identifier
  #endif
  
  QT_BEGIN_NAMESPACE
  
  class QTextStream;
  class QQmlType;
  class QQmlPropertyData;
  class QQmlPropertyCache;
  class QQmlEnginePrivate;
  struct QQmlImportRef;
  class QQmlTypeNameCache;
  
  namespace QV4 {
  
  inline bool isNegative(double d)
  {
      uchar *dch = (uchar *)&d;
      if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
          return (dch[0] & 0x80);
      else
          return (dch[7] & 0x80);
  
  }
  
  namespace IR {
  
  struct BasicBlock;
  struct Function;
  struct Module;
  
  struct Stmt;
  struct Expr;
  
  // expressions
  struct Const;
  struct String;
  struct RegExp;
  struct Name;
  struct Temp;
  struct ArgLocal;
  struct Closure;
  struct Convert;
  struct Unop;
  struct Binop;
  struct Call;
  struct New;
  struct Subscript;
  struct Member;
  
  // statements
  struct Exp;
  struct Move;
  struct Jump;
  struct CJump;
  struct Ret;
  struct Phi;
  
  template<class T, int Prealloc>
  class VarLengthArray: public QVarLengthArray<T, Prealloc>
  {
  public:
      bool removeOne(const T &element)
      {
          for (int i = 0; i < this->size(); ++i) {
              if (this->at(i) == element) {
                  this->remove(i);
                  return true;
              }
          }
  
          return false;
      }
  };
  
  // Flag pointer:
  // * The first flag indicates whether the meta object is final.
  //   If final, then none of its properties themselves need to
  //   be final when considering for lookups in QML.
  // * The second flag indicates whether enums should be included
  //   in the lookup of properties or not. The default is false.
  typedef QFlagPointer<QQmlPropertyCache> IRMetaObject;
  
  enum AluOp {
      OpInvalid = 0,
  
      OpIfTrue,
      OpNot,
      OpUMinus,
      OpUPlus,
      OpCompl,
      OpIncrement,
      OpDecrement,
  
      OpBitAnd,
      OpBitOr,
      OpBitXor,
  
      OpAdd,
      OpSub,
      OpMul,
      OpDiv,
      OpMod,
  
      OpLShift,
      OpRShift,
      OpURShift,
  
      OpGt,
      OpLt,
      OpGe,
      OpLe,
      OpEqual,
      OpNotEqual,
      OpStrictEqual,
      OpStrictNotEqual,
  
      OpInstanceof,
      OpIn,
  
      OpAnd,
      OpOr,
  
      LastAluOp = OpOr
  };
  AluOp binaryOperator(int op);
  const char *opname(IR::AluOp op);
  
  enum Type : quint16 {
      UnknownType   = 0,
  
      MissingType   = 1 << 0,
      UndefinedType = 1 << 1,
      NullType      = 1 << 2,
      BoolType      = 1 << 3,
  
      SInt32Type    = 1 << 4,
      UInt32Type    = 1 << 5,
      DoubleType    = 1 << 6,
      NumberType    = SInt32Type | UInt32Type | DoubleType,
  
      StringType    = 1 << 7,
      QObjectType   = 1 << 8,
      VarType       = 1 << 9
  };
  
  inline bool strictlyEqualTypes(Type t1, Type t2)
  {
      return t1 == t2 || ((t1 & NumberType) && (t2 & NumberType));
  }
  
  QString typeName(Type t);
  
  struct MemberExpressionResolver;
  
  struct DiscoveredType {
      int type;
      MemberExpressionResolver *memberResolver;
  
      DiscoveredType() : type(UnknownType), memberResolver(0) {}
      DiscoveredType(Type t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); }
      explicit DiscoveredType(int t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); }
      explicit DiscoveredType(MemberExpressionResolver *memberResolver)
          : type(QObjectType)
          , memberResolver(memberResolver)
      { Q_ASSERT(memberResolver); }
  
      bool test(Type t) const { return type & t; }
      bool isNumber() const { return (type & NumberType) && !(type & ~NumberType); }
  
      bool operator!=(Type other) const { return type != other; }
      bool operator==(Type other) const { return type == other; }
      bool operator==(const DiscoveredType &other) const { return type == other.type; }
      bool operator!=(const DiscoveredType &other) const { return type != other.type; }
  };
  
  struct MemberExpressionResolver
  {
      typedef DiscoveredType (*ResolveFunction)(QQmlEnginePrivate *engine,
                                                const MemberExpressionResolver *resolver,
                                                Member *member);
  
      MemberExpressionResolver()
          : resolveMember(0), import(nullptr), propertyCache(nullptr), typenameCache(nullptr), owner(nullptr), flags(0) {}
  
      bool isValid() const { return !!resolveMember; }
      void clear() { *this = MemberExpressionResolver(); }
  
      ResolveFunction resolveMember;
  #ifndef V4_BOOTSTRAP
      QQmlType qmlType;
  #endif
      const QQmlImportRef *import;
      QQmlPropertyCache *propertyCache;
      QQmlTypeNameCache *typenameCache;
      Function *owner;
      unsigned int flags;
  };
  
  struct Q_AUTOTEST_EXPORT Expr {
      enum ExprKind : quint8 {
          NameExpr,
          TempExpr,
          ArgLocalExpr,
          SubscriptExpr,
          MemberExpr,
  
          LastLValue = MemberExpr,
  
          ConstExpr,
          StringExpr,
          RegExpExpr,
          ClosureExpr,
          ConvertExpr,
          UnopExpr,
          BinopExpr,
          CallExpr,
          NewExpr
      };
  
      Type type;
      const ExprKind exprKind;
  
      Expr &operator=(const Expr &other) {
          Q_ASSERT(exprKind == other.exprKind);
          type = other.type;
          return *this;
      }
  
      template <typename To>
      inline bool isa() const {
          return To::classof(this);
      }
  
      template <typename To>
      inline To *as() {
          if (isa<To>()) {
              return static_cast<To *>(this);
          } else {
              return nullptr;
          }
      }
  
      template <typename To>
      inline const To *as() const {
          if (isa<To>()) {
              return static_cast<const To *>(this);
          } else {
              return nullptr;
          }
      }
  
      Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {}
      bool isLValue() const;
  
      Const *asConst();
      String *asString();
      RegExp *asRegExp();
      Name *asName();
      Temp *asTemp();
      ArgLocal *asArgLocal();
      Closure *asClosure();
      Convert *asConvert();
      Unop *asUnop();
      Binop *asBinop();
      Call *asCall();
      New *asNew();
      Subscript *asSubscript();
      Member *asMember();
  };
  
  #define EXPR_VISIT_ALL_KINDS(e) \
      switch (e->exprKind) { \
      case QV4::IR::Expr::ConstExpr: \
          break; \
      case QV4::IR::Expr::StringExpr: \
          break; \
      case QV4::IR::Expr::RegExpExpr: \
          break; \
      case QV4::IR::Expr::NameExpr: \
          break; \
      case QV4::IR::Expr::TempExpr: \
          break; \
      case QV4::IR::Expr::ArgLocalExpr: \
          break; \
      case QV4::IR::Expr::ClosureExpr: \
          break; \
      case QV4::IR::Expr::ConvertExpr: { \
          auto casted = e->asConvert(); \
          visit(casted->expr); \
      } break; \
      case QV4::IR::Expr::UnopExpr: { \
          auto casted = e->asUnop(); \
          visit(casted->expr); \
      } break; \
      case QV4::IR::Expr::BinopExpr: { \
          auto casted = e->asBinop(); \
          visit(casted->left); \
          visit(casted->right); \
      } break; \
      case QV4::IR::Expr::CallExpr: { \
          auto casted = e->asCall(); \
          visit(casted->base); \
          for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
              visit(it->expr); \
      } break; \
      case QV4::IR::Expr::NewExpr: { \
          auto casted = e->asNew(); \
          visit(casted->base); \
          for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
              visit(it->expr); \
      } break; \
      case QV4::IR::Expr::SubscriptExpr: { \
          auto casted = e->asSubscript(); \
          visit(casted->base); \
          visit(casted->index); \
      } break; \
      case QV4::IR::Expr::MemberExpr: { \
          auto casted = e->asMember(); \
          visit(casted->base); \
      } break; \
      }
  
  struct ExprList {
      Expr *expr;
      ExprList *next;
  
      ExprList(): expr(0), next(0) {}
  
      void init(Expr *expr, ExprList *next = 0)
      {
          this->expr = expr;
          this->next = next;
      }
  };
  
  struct Const: Expr {
      double value;
  
      Const(): Expr(ConstExpr) {}
  
      void init(Type type, double value)
      {
          this->type = type;
          this->value = value;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == ConstExpr; }
  };
  
  struct String: Expr {
      const QString *value;
  
      String(): Expr(StringExpr) {}
  
      void init(const QString *value)
      {
          this->value = value;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == StringExpr; }
  };
  
  struct RegExp: Expr {
      // needs to be compatible with the flags in the lexer, and in RegExpObject
      enum Flags {
          RegExp_Global     = 0x01,
          RegExp_IgnoreCase = 0x02,
          RegExp_Multiline  = 0x04
      };
  
      const QString *value;
      int flags;
  
      RegExp(): Expr(RegExpExpr) {}
  
      void init(const QString *value, int flags)
      {
          this->value = value;
          this->flags = flags;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; }
  };
  
  struct Name: Expr {
      enum Builtin {
          builtin_invalid,
          builtin_typeof,
          builtin_delete,
          builtin_throw,
          builtin_rethrow,
          builtin_unwind_exception,
          builtin_push_catch_scope,
          builtin_foreach_iterator_object,
          builtin_foreach_next_property_name,
          builtin_push_with_scope,
          builtin_pop_scope,
          builtin_declare_vars,
          builtin_define_array,
          builtin_define_object_literal,
          builtin_setup_argument_object,
          builtin_convert_this_to_object,
          builtin_qml_context,
          builtin_qml_imported_scripts_object
      };
  
      const QString *id;
      Builtin builtin;
      bool global : 1;
      bool forceLookup : 1;
      bool qmlSingleton : 1;
      bool freeOfSideEffects : 1;
      quint32 line;
      quint32 column;
  
      Name(): Expr(NameExpr) {}
  
      void initGlobal(const QString *id, quint32 line, quint32 column, bool forceLookup = false);
      void init(const QString *id, quint32 line, quint32 column);
      void init(Builtin builtin, quint32 line, quint32 column);
  
      static bool classof(const Expr *c) { return c->exprKind == NameExpr; }
  };
  
  struct Q_AUTOTEST_EXPORT Temp: Expr {
      enum Kind {
          Invalid = 0,
          VirtualRegister,
          PhysicalRegister,
          StackSlot
      };
  
      unsigned index      : 28;
      unsigned isReadOnly :  1;
      unsigned kind       :  3;
  
      // Used when temp is used as base in member expression
      MemberExpressionResolver *memberResolver;
  
      Temp()
          : Expr(TempExpr)
          , index((1 << 28) - 1)
          , isReadOnly(0)
          , kind(Invalid)
          , memberResolver(0)
      {}
  
      Temp(Type type, Kind kind, unsigned index)
          : Expr(TempExpr)
          , index(index)
          , isReadOnly(0)
          , kind(kind)
          , memberResolver(0)
      {
          this->type = type;
      }
  
      void init(unsigned kind, unsigned index)
      {
          this->index = index;
          this->isReadOnly = false;
          this->kind = kind;
      }
  
      bool isInvalid() const { return kind == Invalid; }
  
      static bool classof(const Expr *c) { return c->exprKind == TempExpr; }
  };
  
  inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
  { return t1.index == t2.index && t1.kind == t2.kind && t1.type == t2.type; }
  
  inline bool operator!=(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
  { return !(t1 == t2); }
  
  inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW
  { return t.index ^ t.kind ^ seed; }
  
  bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW;
  
  struct Q_AUTOTEST_EXPORT ArgLocal: Expr {
      enum Kind {
          Formal = 0,
          ScopedFormal,
          Local,
          ScopedLocal
      };
  
      unsigned index;
      unsigned scope             : 29; // how many scopes outside the current one?
      unsigned kind              :  2;
      unsigned isArgumentsOrEval :  1;
  
      void init(unsigned kind, unsigned index, unsigned scope)
      {
          Q_ASSERT((kind == ScopedLocal && scope != 0) ||
                   (kind == ScopedFormal && scope != 0) ||
                   (scope == 0));
  
          this->kind = kind;
          this->index = index;
          this->scope = scope;
          this->isArgumentsOrEval = false;
      }
  
      ArgLocal(): Expr(ArgLocalExpr) {}
  
      bool operator==(const ArgLocal &other) const
      { return index == other.index && scope == other.scope && kind == other.kind; }
  
      static bool classof(const Expr *c) { return c->exprKind == ArgLocalExpr; }
  };
  
  struct Closure: Expr {
      int value; // index in _module->functions
      const QString *functionName;
  
      Closure(): Expr(ClosureExpr) {}
  
      void init(int functionInModule, const QString *functionName)
      {
          this->value = functionInModule;
          this->functionName = functionName;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; }
  };
  
  struct Convert: Expr {
      Expr *expr;
  
      Convert(): Expr(ConvertExpr) {}
  
      void init(Expr *expr, Type type)
      {
          this->expr = expr;
          this->type = type;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; }
  };
  
  struct Unop: Expr {
      Expr *expr;
      AluOp op;
  
      Unop(): Expr(UnopExpr) {}
  
      void init(AluOp op, Expr *expr)
      {
          this->op = op;
          this->expr = expr;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == UnopExpr; }
  };
  
  struct Binop: Expr {
      Expr *left; // Temp or Const
      Expr *right; // Temp or Const
      AluOp op;
  
      Binop(): Expr(BinopExpr) {}
  
      void init(AluOp op, Expr *left, Expr *right)
      {
          this->op = op;
          this->left = left;
          this->right = right;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == BinopExpr; }
  };
  
  struct Call: Expr {
      Expr *base; // Name, Member, Temp
      ExprList *args; // List of Temps
  
      Call(): Expr(CallExpr) {}
  
      void init(Expr *base, ExprList *args)
      {
          this->base = base;
          this->args = args;
      }
  
      Expr *onlyArgument() const {
          if (args && ! args->next)
              return args->expr;
          return 0;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == CallExpr; }
  };
  
  struct New: Expr {
      Expr *base; // Name, Member, Temp
      ExprList *args; // List of Temps
  
      New(): Expr(NewExpr) {}
  
      void init(Expr *base, ExprList *args)
      {
          this->base = base;
          this->args = args;
      }
  
      Expr *onlyArgument() const {
          if (args && ! args->next)
              return args->expr;
          return 0;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == NewExpr; }
  };
  
  struct Subscript: Expr {
      Expr *base;
      Expr *index;
  
      Subscript(): Expr(SubscriptExpr) {}
  
      void init(Expr *base, Expr *index)
      {
          this->base = base;
          this->index = index;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; }
  };
  
  struct Member: Expr {
      // Used for property dependency tracking
      enum MemberKind {
          UnspecifiedMember,
          MemberOfEnum,
          MemberOfQmlScopeObject,
          MemberOfQmlContextObject,
          MemberOfIdObjectsArray,
          MemberOfSingletonObject,
      };
  
      Expr *base;
      const QString *name;
      QQmlPropertyData *property;
      union {  // depending on kind
          int attachedPropertiesId;
          int enumValue;
          int idIndex;
      };
      uchar freeOfSideEffects : 1;
  
      // This is set for example for for QObject properties. All sorts of extra behavior
      // is defined when writing to them, for example resettable properties are reset
      // when writing undefined to them, and an exception is thrown when they're missing
      // a reset function. And then there's also Qt.binding().
      uchar inhibitTypeConversionOnWrite: 1;
  
      uchar kind: 3; // MemberKind
  
      Member(): Expr(MemberExpr) {}
  
      void setEnumValue(int value) {
          kind = MemberOfEnum;
          enumValue = value;
      }
  
      void setAttachedPropertiesId(int id) {
          Q_ASSERT(kind != MemberOfEnum && kind != MemberOfIdObjectsArray);
          attachedPropertiesId = id;
      }
  
      void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int index = 0)
      {
          this->base = base;
          this->name = name;
          this->property = property;
          this->idIndex = index;
          this->freeOfSideEffects = false;
          this->inhibitTypeConversionOnWrite = property != 0;
          this->kind = kind;
      }
  
      static bool classof(const Expr *c) { return c->exprKind == MemberExpr; }
  };
  
  inline bool Expr::isLValue() const {
      if (auto t = as<Temp>())
          return !t->isReadOnly;
      return exprKind <= LastLValue;
  }
  
  struct Stmt {
      enum StmtKind: quint8 {
          MoveStmt,
          ExpStmt,
          JumpStmt,
          CJumpStmt,
          RetStmt,
          PhiStmt
      };
  
      template <typename To>
      inline bool isa() const {
          return To::classof(this);
      }
  
      template <typename To>
      inline To *as() {
          if (isa<To>())
              return static_cast<To *>(this);
          else
              return nullptr;
      }
  
      enum { InvalidId = -1 };
  
      QQmlJS::AST::SourceLocation location;
  
      explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {}
  
      Stmt *asTerminator();
  
      Exp *asExp();
      Move *asMove();
      Jump *asJump();
      CJump *asCJump();
      Ret *asRet();
      Phi *asPhi();
  
      int id() const { return _id; }
  
  private: // For memory management in BasicBlock
      friend struct BasicBlock;
  
  private:
      friend struct Function;
      int _id;
  
  public:
      const StmtKind stmtKind;
  };
  
  #define STMT_VISIT_ALL_KINDS(s) \
      switch (s->stmtKind) { \
      case QV4::IR::Stmt::MoveStmt: { \
          auto casted = s->asMove(); \
          visit(casted->target); \
          visit(casted->source); \
      } break; \
      case QV4::IR::Stmt::ExpStmt: { \
          auto casted = s->asExp(); \
          visit(casted->expr); \
      } break; \
      case QV4::IR::Stmt::JumpStmt: \
          break; \
      case QV4::IR::Stmt::CJumpStmt: { \
          auto casted = s->asCJump(); \
          visit(casted->cond); \
      } break; \
      case QV4::IR::Stmt::RetStmt: { \
          auto casted = s->asRet(); \
          visit(casted->expr); \
      } break; \
      case QV4::IR::Stmt::PhiStmt: { \
          auto casted = s->asPhi(); \
          visit(casted->targetTemp); \
          for (auto *e : casted->incoming) { \
              visit(e); \
          } \
      } break; \
      }
  
  struct Exp: Stmt {
      Expr *expr;
  
      Exp(int id): Stmt(id, ExpStmt) {}
  
      void init(Expr *expr)
      {
          this->expr = expr;
      }
  
      static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; }
  };
  
  struct Move: Stmt {
      Expr *target; // LHS - Temp, Name, Member or Subscript
      Expr *source;
      bool swap;
  
      Move(int id): Stmt(id, MoveStmt) {}
  
      void init(Expr *target, Expr *source)
      {
          this->target = target;
          this->source = source;
          this->swap = false;
      }
  
      static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; }
  };
  
  struct Jump: Stmt {
      BasicBlock *target;
  
      Jump(int id): Stmt(id, JumpStmt) {}
  
      void init(BasicBlock *target)
      {
          this->target = target;
      }
  
      static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; }
  };
  
  struct CJump: Stmt {
      Expr *cond; // Temp, Binop
      BasicBlock *iftrue;
      BasicBlock *iffalse;
      BasicBlock *parent;
  
      CJump(int id): Stmt(id, CJumpStmt) {}
  
      void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent)
      {
          this->cond = cond;
          this->iftrue = iftrue;
          this->iffalse = iffalse;
          this->parent = parent;
      }
  
      static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; }
  };
  
  struct Ret: Stmt {
      Expr *expr;
  
      Ret(int id): Stmt(id, RetStmt) {}
  
      void init(Expr *expr)
      {
          this->expr = expr;
      }
  
      static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; }
  };
  
  // Phi nodes can only occur at the start of a basic block. If there are any, they need to be
  // subsequent to eachother, and the first phi node should be the first statement in the basic-block.
  // A number of loops rely on this behavior, so they don't need to walk through the whole list
  // of instructions in a basic-block (e.g. the calls to destroyData in BasicBlock::~BasicBlock).
  struct Phi: Stmt {
      Temp *targetTemp;
      VarLengthArray<Expr *, 4> incoming;
  
      Phi(int id): Stmt(id, PhiStmt) {}
  
      static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; }
  
      void destroyData()
      { incoming.~VarLengthArray(); }
  };
  
  inline Stmt *Stmt::asTerminator()
  {
      if (auto s = asJump()) {
          return s;
      } else if (auto s = asCJump()) {
          return s;
      } else if (auto s = asRet()) {
          return s;
      } else {
          return nullptr;
      }
  }
  
  struct Q_QML_PRIVATE_EXPORT Module {
      QQmlJS::MemoryPool pool;
      QVector<Function *> functions;
      Function *rootFunction;
      QString fileName;
      QString finalUrl;
      QDateTime sourceTimeStamp;
      bool isQmlModule; // implies rootFunction is always 0
      uint unitFlags; // flags merged into CompiledData::Unit::flags
      QString targetABI; // fallback to QSysInfo::buildAbi() if empty
  #ifdef QT_NO_QML_DEBUGGER
      static const bool debugMode = false;
  #else
      bool debugMode;
  #endif
  
      Function *newFunction(const QString &name, Function *outer);
  
      Module(bool debugMode)
          : rootFunction(0)
          , isQmlModule(false)
          , unitFlags(0)
  #ifndef QT_NO_QML_DEBUGGER
          , debugMode(debugMode)
      {}
  #else
      { Q_UNUSED(debugMode); }
  #endif
      ~Module();
  
      void setFileName(const QString &name);
      void setFinalUrl(const QString &url);
  };
  
  struct BasicBlock {
  private:
      Q_DISABLE_COPY(BasicBlock)
  
  public:
      typedef VarLengthArray<BasicBlock *, 4> IncomingEdges;
      typedef VarLengthArray<BasicBlock *, 2> OutgoingEdges;
  
      Function *function;
      BasicBlock *catchBlock;
      IncomingEdges in;
      OutgoingEdges out;
      QQmlJS::AST::SourceLocation nextLocation;
  
      BasicBlock(Function *function, BasicBlock *catcher)
          : function(function)
          , catchBlock(catcher)
          , _containingGroup(0)
          , _index(-1)
          , _isExceptionHandler(false)
          , _groupStart(false)
          , _isRemoved(false)
      {}
  
      ~BasicBlock()
      {
          for (Stmt *s : qAsConst(_statements)) {
              if (Phi *p = s->asPhi()) {
                  p->destroyData();
              } else {
                  break;
              }
          }
      }
  
      const QVector<Stmt *> &statements() const
      {
          Q_ASSERT(!isRemoved());
          return _statements;
      }
  
      int statementCount() const
      {
          Q_ASSERT(!isRemoved());
          return _statements.size();
      }
  
      void setStatements(const QVector<Stmt *> &newStatements);
  
      template <typename Instr> inline Instr i(Instr i)
      {
          Q_ASSERT(!isRemoved());
          appendStatement(i);
          return i;
      }
  
      void appendStatement(Stmt *statement)
      {
          Q_ASSERT(!isRemoved());
          if (nextLocation.startLine)
              statement->location = nextLocation;
          _statements.append(statement);
      }
  
      void prependStatement(Stmt *stmt)
      {
          Q_ASSERT(!isRemoved());
          _statements.prepend(stmt);
      }
  
      void prependStatements(const QVector<Stmt *> &stmts)
      {
          Q_ASSERT(!isRemoved());
          QVector<Stmt *> newStmts = stmts;
          newStmts += _statements;
          _statements = newStmts;
      }
  
      void insertStatementBefore(Stmt *before, Stmt *newStmt)
      {
          int idx = _statements.indexOf(before);
          Q_ASSERT(idx >= 0);
          _statements.insert(idx, newStmt);
      }
  
      void insertStatementBefore(int index, Stmt *newStmt)
      {
          Q_ASSERT(index >= 0);
          _statements.insert(index, newStmt);
      }
  
      void insertStatementBeforeTerminator(Stmt *stmt)
      {
          Q_ASSERT(!isRemoved());
          _statements.insert(_statements.size() - 1, stmt);
      }
  
      void replaceStatement(int index, Stmt *newStmt)
      {
          Q_ASSERT(!isRemoved());
          if (Phi *p = _statements[index]->asPhi()) {
              p->destroyData();
          }
          _statements[index] = newStmt;
      }
  
      void removeStatement(Stmt *stmt)
      {
          Q_ASSERT(!isRemoved());
          if (Phi *p = stmt->asPhi()) {
              p->destroyData();
          }
          _statements.remove(_statements.indexOf(stmt));
      }
  
      void removeStatement(int idx)
      {
          Q_ASSERT(!isRemoved());
          if (Phi *p = _statements[idx]->asPhi()) {
              p->destroyData();
          }
          _statements.remove(idx);
      }
  
      inline bool isEmpty() const {
          Q_ASSERT(!isRemoved());
          return _statements.isEmpty();
      }
  
      inline Stmt *terminator() const {
          Q_ASSERT(!isRemoved());
          if (! _statements.isEmpty() && _statements.last()->asTerminator() != 0)
              return _statements.last();
          return 0;
      }
  
      inline bool isTerminated() const {
          Q_ASSERT(!isRemoved());
          if (terminator() != 0)
              return true;
          return false;
      }
  
      enum TempForWhom {
          NewTempForCodegen,
          NewTempForOptimizer
      };
      unsigned newTemp(TempForWhom tfw = NewTempForCodegen);
  
      Temp *TEMP(unsigned kind);
      ArgLocal *ARG(unsigned index, unsigned scope);
      ArgLocal *LOCAL(unsigned index, unsigned scope);
  
      Expr *CONST(Type type, double value);
      Expr *STRING(const QString *value);
      Expr *REGEXP(const QString *value, int flags);
  
      Name *NAME(const QString &id, quint32 line, quint32 column);
      Name *NAME(Name::Builtin builtin, quint32 line, quint32 column);
  
      Name *GLOBALNAME(const QString &id, quint32 line, quint32 column, bool forceLookup = false);
  
      Closure *CLOSURE(int functionInModule);
  
      Expr *CONVERT(Expr *expr, Type type);
      Expr *UNOP(AluOp op, Expr *expr);
      Expr *BINOP(AluOp op, Expr *left, Expr *right);
      Expr *CALL(Expr *base, ExprList *args = 0);
      Expr *NEW(Expr *base, ExprList *args = 0);
      Expr *SUBSCRIPT(Expr *base, Expr *index);
      Expr *MEMBER(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = Member::UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0);
  
      Stmt *EXP(Expr *expr);
  
      Stmt *MOVE(Expr *target, Expr *source);
  
      Stmt *JUMP(BasicBlock *target);
      Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
      Stmt *RET(Expr *expr);
  
      BasicBlock *containingGroup() const
      {
          Q_ASSERT(!isRemoved());
          return _containingGroup;
      }
  
      void setContainingGroup(BasicBlock *loopHeader)
      {
          Q_ASSERT(!isRemoved());
          _containingGroup = loopHeader;
      }
  
      bool isGroupStart() const
      {
          Q_ASSERT(!isRemoved());
          return _groupStart;
      }
  
      void markAsGroupStart(bool mark = true)
      {
          Q_ASSERT(!isRemoved());
          _groupStart = mark;
      }
  
      // Returns the index of the basic-block.
      // See Function for the full description.
      int index() const
      {
          Q_ASSERT(!isRemoved());
          return _index;
      }
  
      bool isExceptionHandler() const
      { return _isExceptionHandler; }
  
      void setExceptionHandler(bool onoff)
      { _isExceptionHandler = onoff; }
  
      bool isRemoved() const
      { return _isRemoved; }
  
  private: // For Function's eyes only.
      friend struct Function;
      void setIndex(int index)
      {
          Q_ASSERT(_index < 0);
          changeIndex(index);
      }
  
      void changeIndex(int index)
      {
          Q_ASSERT(index >= 0);
          _index = index;
      }
  
      void markAsRemoved()
      {
          _isRemoved = true;
          _index = -1;
      }
  
  private:
      QVector<Stmt *> _statements;
      BasicBlock *_containingGroup;
      int _index;
      unsigned _isExceptionHandler : 1;
      unsigned _groupStart : 1;
      unsigned _isRemoved : 1;
  };
  
  template <typename T>
  class SmallSet: public QVarLengthArray<T, 8>
  {
  public:
      void insert(int value)
      {
          for (auto it : *this) {
              if (it == value)
                  return;
          }
          this->append(value);
      }
  };
  
  // Map from meta property index (existence implies dependency) to notify signal index
  struct KeyValuePair
  {
      quint32 _key;
      quint32 _value;
  
      KeyValuePair(): _key(0), _value(0) {}
      KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
  
      quint32 key() const { return _key; }
      quint32 value() const { return _value; }
  };
  
  class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
  {
  public:
      void insert(quint32 key, quint32 value)
      {
          for (auto it = begin(), eit = end(); it != eit; ++it) {
              if (it->_key == key) {
                  it->_value = value;
                  return;
              }
          }
          append(KeyValuePair(key, value));
      }
  };
  
  // The Function owns (manages), among things, a list of basic-blocks. All the blocks have an index,
  // which corresponds to the index in the entry/index in the vector in which they are stored. This
  // means that algorithms/classes can also store any information about a basic block in an array,
  // where the index corresponds to the index of the basic block, which can then be used to query
  // the function for a pointer to a basic block. This also means that basic-blocks cannot be removed
  // or renumbered.
  //
  // Note that currently there is one exception: after optimization and block scheduling, the
  // method setScheduledBlocks can be called once, to register a newly ordered list. For debugging
  // purposes, these blocks are not immediately renumbered, so renumberBasicBlocks should be called
  // immediately after changing the order. That will restore the property of having a corresponding
  // block-index and block-position-in-basicBlocks-vector.
  //
  // In order for optimization/transformation passes to skip uninteresting basic blocks that will be
  // removed, the block can be marked as such. After doing so, any access will result in a failing
  // assertion.
  struct Function {
      Module *module;
      QQmlJS::MemoryPool *pool;
      const QString *name;
      int currentTemp = 0;
      int tempCount;
      int maxNumberOfArguments;
      QSet<QString> strings;
      QList<const QString *> formals;
      QList<const QString *> locals;
      QVector<Function *> nestedFunctions;
      Function *outer;
  
      int insideWithOrCatch;
  
      uint hasDirectEval: 1;
      uint usesArgumentsObject : 1;
      uint usesThis : 1;
      uint isStrict: 1;
      uint isNamedExpression : 1;
      uint hasTry: 1;
      uint hasWith: 1;
      uint isQmlBinding: 1;
      uint returnsClosure: 1;
      uint unused : 23;
  
      // Location of declaration in source code (0 if not specified)
      uint line;
      uint column;
  
      // Qml extension:
      SmallSet<int> idObjectDependencies;
      PropertyDependencyMap contextObjectPropertyDependencies;
      PropertyDependencyMap scopeObjectPropertyDependencies;
  
      template <typename T> T *New() { return new (pool->allocate(sizeof(T))) T(); }
      template <typename T> T *NewStmt() {
          return new (pool->allocate(sizeof(T))) T(getNewStatementId());
      }
  
      Function(Module *module, Function *outer, const QString &name);
      ~Function();
  
      enum BasicBlockInsertMode {
          InsertBlock,
          DontInsertBlock
      };
  
      BasicBlock *newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode = InsertBlock);
      const QString *newString(const QString &text);
  
      void RECEIVE(const QString &name) { formals.append(newString(name)); }
      void LOCAL(const QString &name) { locals.append(newString(name)); }
  
      BasicBlock *addBasicBlock(BasicBlock *block);
      void removeBasicBlock(BasicBlock *block);
  
      const QVector<BasicBlock *> &basicBlocks() const
      { return _basicBlocks; }
  
      BasicBlock *basicBlock(int idx) const
      { return _basicBlocks.at(idx); }
  
      int basicBlockCount() const
      { return _basicBlocks.size(); }
  
      int liveBasicBlocksCount() const;
  
      void removeSharedExpressions();
  
      int indexOfArgument(const QStringRef &string) const;
  
      bool variablesCanEscape() const
      { return hasDirectEval || !nestedFunctions.isEmpty() || module->debugMode; }
  
      void setScheduledBlocks(const QVector<BasicBlock *> &scheduled);
  
      int getNewStatementId() { return _statementCount++; }
      int statementCount() const { return _statementCount; }
  
  private:
      BasicBlock *getOrCreateBasicBlock(int index);
      void setStatementCount(int cnt);
  
  private:
      QVector<BasicBlock *> _basicBlocks;
      QVector<BasicBlock *> *_allBasicBlocks;
      int _statementCount;
  };
  
  class CloneExpr
  {
  public:
      explicit CloneExpr(IR::BasicBlock *block = 0);
  
      void setBasicBlock(IR::BasicBlock *block);
  
      template <typename ExprSubclass>
      ExprSubclass *operator()(ExprSubclass *expr)
      {
          return clone(expr);
      }
  
      template <typename ExprSubclass>
      ExprSubclass *clone(ExprSubclass *expr)
      {
          Expr *c = expr;
          qSwap(cloned, c);
          visit(expr);
          qSwap(cloned, c);
          return static_cast<ExprSubclass *>(c);
      }
  
      static Const *cloneConst(Const *c, Function *f)
      {
          Const *newConst = f->New<Const>();
          newConst->init(c->type, c->value);
          return newConst;
      }
  
      static Name *cloneName(Name *n, Function *f)
      {
          Name *newName = f->New<Name>();
          newName->type = n->type;
          newName->id = n->id;
          newName->builtin = n->builtin;
          newName->global = n->global;
          newName->qmlSingleton = n->qmlSingleton;
          newName->freeOfSideEffects = n->freeOfSideEffects;
          newName->line = n->line;
          newName->column = n->column;
          return newName;
      }
  
      static Temp *cloneTemp(Temp *t, Function *f)
      {
          Temp *newTemp = f->New<Temp>();
          newTemp->init(t->kind, t->index);
          newTemp->type = t->type;
          newTemp->memberResolver = t->memberResolver;
          return newTemp;
      }
  
      static ArgLocal *cloneArgLocal(ArgLocal *argLocal, Function *f)
      {
          ArgLocal *newArgLocal = f->New<ArgLocal>();
          newArgLocal->init(argLocal->kind, argLocal->index, argLocal->scope);
          newArgLocal->type = argLocal->type;
          return newArgLocal;
      }
  
  private:
      IR::ExprList *clone(IR::ExprList *list);
  
      void visit(Expr *e);
  
  protected:
      IR::BasicBlock *block;
  
  private:
      IR::Expr *cloned;
  };
  
  class Q_AUTOTEST_EXPORT IRPrinter
  {
  public:
      IRPrinter(QTextStream *out);
      virtual ~IRPrinter();
  
      void print(Stmt *s);
      void print(Expr *e);
      void print(const Expr &e);
  
      virtual void print(Function *f);
      virtual void print(BasicBlock *bb);
  
      void visit(Stmt *s);
      virtual void visitExp(Exp *s);
      virtual void visitMove(Move *s);
      virtual void visitJump(Jump *s);
      virtual void visitCJump(CJump *s);
      virtual void visitRet(Ret *s);
      virtual void visitPhi(Phi *s);
  
      void visit(Expr *e);
      virtual void visitConst(Const *e);
      virtual void visitString(String *e);
      virtual void visitRegExp(RegExp *e);
      virtual void visitName(Name *e);
      virtual void visitTemp(Temp *e);
      virtual void visitArgLocal(ArgLocal *e);
      virtual void visitClosure(Closure *e);
      virtual void visitConvert(Convert *e);
      virtual void visitUnop(Unop *e);
      virtual void visitBinop(Binop *e);
      virtual void visitCall(Call *e);
      virtual void visitNew(New *e);
      virtual void visitSubscript(Subscript *e);
      virtual void visitMember(Member *e);
  
      static QString escape(const QString &s);
  
  protected:
      virtual void addStmtNr(Stmt *s);
      void addJustifiedNr(int pos);
      void printBlockStart();
  
  protected:
      QTextStream *out;
      int positionSize;
      BasicBlock *currentBB;
  };
  
  inline unsigned BasicBlock::newTemp(TempForWhom tfw)
  {
      Q_ASSERT(!isRemoved());
  
      if (tfw == NewTempForOptimizer)
          return function->tempCount++;
  
      int t = function->currentTemp++;
      if (function->tempCount < function->currentTemp)
          function->tempCount = function->currentTemp;
      return t;
  }
  
  inline Temp *BasicBlock::TEMP(unsigned index)
  {
      Q_ASSERT(!isRemoved());
      Temp *e = function->New<Temp>();
      e->init(Temp::VirtualRegister, index);
      return e;
  }
  
  inline ArgLocal *BasicBlock::ARG(unsigned index, unsigned scope)
  {
      Q_ASSERT(!isRemoved());
      ArgLocal *e = function->New<ArgLocal>();
      e->init(scope ? ArgLocal::ScopedFormal : ArgLocal::Formal, index, scope);
      return e;
  }
  
  inline ArgLocal *BasicBlock::LOCAL(unsigned index, unsigned scope)
  {
      Q_ASSERT(!isRemoved());
      ArgLocal *e = function->New<ArgLocal>();
      e->init(scope ? ArgLocal::ScopedLocal : ArgLocal::Local, index, scope);
      return e;
  }
  
  inline Expr *BasicBlock::CONST(Type type, double value)
  {
      Q_ASSERT(!isRemoved());
      Const *e = function->New<Const>();
      if (type == NumberType) {
          int ival = (int)value;
          // +0 != -0, so we need to convert to double when negating 0
          if (ival == value && !(value == 0 && isNegative(value)))
              type = SInt32Type;
          else
              type = DoubleType;
      } else if (type == NullType) {
          value = 0;
      } else if (type == UndefinedType) {
          value = qt_qnan();
      }
  
      e->init(type, value);
      return e;
  }
  
  inline Expr *BasicBlock::STRING(const QString *value)
  {
      Q_ASSERT(!isRemoved());
      String *e = function->New<String>();
      e->init(value);
      return e;
  }
  
  inline Expr *BasicBlock::REGEXP(const QString *value, int flags)
  {
      Q_ASSERT(!isRemoved());
      RegExp *e = function->New<RegExp>();
      e->init(value, flags);
      return e;
  }
  
  inline Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
  {
      Q_ASSERT(!isRemoved());
      Name *e = function->New<Name>();
      e->init(function->newString(id), line, column);
      return e;
  }
  
  inline Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column, bool forceLookup)
  {
      Q_ASSERT(!isRemoved());
      Name *e = function->New<Name>();
      e->initGlobal(function->newString(id), line, column, forceLookup);
      return e;
  }
  
  
  inline Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column)
  {
      Q_ASSERT(!isRemoved());
      Name *e = function->New<Name>();
      e->init(builtin, line, column);
      return e;
  }
  
  inline Closure *BasicBlock::CLOSURE(int functionInModule)
  {
      Q_ASSERT(!isRemoved());
      Closure *clos = function->New<Closure>();
      clos->init(functionInModule, function->module->functions.at(functionInModule)->name);
      return clos;
  }
  
  inline Expr *BasicBlock::CONVERT(Expr *expr, Type type)
  {
      Q_ASSERT(!isRemoved());
      Convert *e = function->New<Convert>();
      e->init(expr, type);
      return e;
  }
  
  inline Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
  {
      Q_ASSERT(!isRemoved());
      Unop *e = function->New<Unop>();
      e->init(op, expr);
      return e;
  }
  
  inline Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
  {
      Q_ASSERT(!isRemoved());
      Binop *e = function->New<Binop>();
      e->init(op, left, right);
      return e;
  }
  
  inline Expr *BasicBlock::CALL(Expr *base, ExprList *args)
  {
      Q_ASSERT(!isRemoved());
      Call *e = function->New<Call>();
      e->init(base, args);
      int argc = 0;
      for (ExprList *it = args; it; it = it->next)
          ++argc;
      function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc);
      return e;
  }
  
  inline Expr *BasicBlock::NEW(Expr *base, ExprList *args)
  {
      Q_ASSERT(!isRemoved());
      New *e = function->New<New>();
      e->init(base, args);
      return e;
  }
  
  inline Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index)
  {
      Q_ASSERT(!isRemoved());
      Subscript *e = function->New<Subscript>();
      e->init(base, index);
      return e;
  }
  
  inline Expr *BasicBlock::MEMBER(Expr *base, const QString *name, QQmlPropertyData *property, uchar kind, int attachedPropertiesIdOrEnumValue)
  {
      Q_ASSERT(!isRemoved());
      Member*e = function->New<Member>();
      e->init(base, name, property, kind, attachedPropertiesIdOrEnumValue);
      return e;
  }
  
  inline Stmt *BasicBlock::EXP(Expr *expr)
  {
      Q_ASSERT(!isRemoved());
      if (isTerminated())
          return 0;
  
      Exp *s = function->NewStmt<Exp>();
      s->init(expr);
      appendStatement(s);
      return s;
  }
  
  inline Stmt *BasicBlock::MOVE(Expr *target, Expr *source)
  {
      Q_ASSERT(!isRemoved());
      if (isTerminated())
          return 0;
  
      Move *s = function->NewStmt<Move>();
      s->init(target, source);
      appendStatement(s);
      return s;
  }
  
  inline Stmt *BasicBlock::JUMP(BasicBlock *target)
  {
      Q_ASSERT(!isRemoved());
      if (isTerminated())
          return 0;
  
      Jump *s = function->NewStmt<Jump>();
      s->init(target);
      appendStatement(s);
  
      Q_ASSERT(! out.contains(target));
      out.append(target);
  
      Q_ASSERT(! target->in.contains(this));
      target->in.append(this);
  
      return s;
  }
  
  inline Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
  {
      Q_ASSERT(!isRemoved());
      if (isTerminated())
          return 0;
  
      if (iftrue == iffalse) {
          MOVE(TEMP(newTemp()), cond);
          return JUMP(iftrue);
      }
  
      CJump *s = function->NewStmt<CJump>();
      s->init(cond, iftrue, iffalse, this);
      appendStatement(s);
  
      Q_ASSERT(! out.contains(iftrue));
      out.append(iftrue);
  
      Q_ASSERT(! iftrue->in.contains(this));
      iftrue->in.append(this);
  
      Q_ASSERT(! out.contains(iffalse));
      out.append(iffalse);
  
      Q_ASSERT(! iffalse->in.contains(this));
      iffalse->in.append(this);
  
      return s;
  }
  
  inline Stmt *BasicBlock::RET(Expr *expr)
  {
      Q_ASSERT(!isRemoved());
      if (isTerminated())
          return 0;
  
      Ret *s = function->NewStmt<Ret>();
      s->init(expr);
      appendStatement(s);
      return s;
  }
  
  inline Const *Expr::asConst() { return as<Const>(); }
  inline String *Expr::asString() { return as<String>(); }
  inline RegExp *Expr::asRegExp() { return as<RegExp>(); }
  inline Name *Expr::asName() { return as<Name>(); }
  inline Temp *Expr::asTemp() { return as<Temp>(); }
  inline ArgLocal *Expr::asArgLocal() { return as<ArgLocal>(); }
  inline Closure *Expr::asClosure() { return as<Closure>(); }
  inline Convert *Expr::asConvert() { return as<Convert>(); }
  inline Unop *Expr::asUnop() { return as<Unop>(); }
  inline Binop *Expr::asBinop() { return as<Binop>(); }
  inline Call *Expr::asCall() { return as<Call>(); }
  inline New *Expr::asNew() { return as<New>(); }
  inline Subscript *Expr::asSubscript() { return as<Subscript>(); }
  inline Member *Expr::asMember() { return as<Member>(); }
  
  inline Exp *Stmt::asExp() { return as<Exp>(); }
  inline Move *Stmt::asMove() { return as<Move>(); }
  inline Jump *Stmt::asJump() { return as<Jump>(); }
  inline CJump *Stmt::asCJump() { return as<CJump>(); }
  inline Ret *Stmt::asRet() { return as<Ret>(); }
  inline Phi *Stmt::asPhi() { return as<Phi>(); }
  
  } // end of namespace IR
  
  } // end of namespace QV4
  
  QT_END_NAMESPACE
  
  #if defined(QT_POP_CONST)
  # pragma pop_macro("CONST") // Restore peace
  # undef QT_POP_CONST
  #endif
  
  #endif // QV4IR_P_H
  