void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg,
                                           AnalysisResultPtr ar) {
  if (m_global) {
    if (!m_globalName.empty()) {
      VariableTablePtr variables = getScope()->getVariables();
      string name = variables->getGlobalVariableName(cg, ar, m_globalName);
      cg_printf("g->%s", name.c_str());
    } else {
      cg_printf("((LVariableTable *)g)->get(");
      m_offset->outputCPP(cg, ar);
      cg_printf(")");
    }
  } else {
    TypePtr type = m_variable->getActualType();
    if (hasContext(UnsetContext)) {
      cg_printf("unsetLval(");
      m_variable->outputCPP(cg, ar);
      cg_printf(", ");
    } else {
      if (m_variable->is(Expression::KindOfScalarExpression) ||
          (type && (type->isInteger() ||
                    type->is(Type::KindOfDouble) ||
                    type->is(Type::KindOfObject) ||
                    type->is(Type::KindOfBoolean)))) {
        cg_printf(type && type->is(Type::KindOfString) ? "((String)" :
                  "((Variant)");
        m_variable->outputCPP(cg, ar);
        cg_printf(")");
      } else {
        TypePtr act;
        if (!m_variable->hasCPPTemp() && m_variable->getImplementedType() &&
            type->is(Type::KindOfArray) &&
            !Type::SameType(m_variable->getImplementedType(), type)) {
          act = type;
          type = m_variable->getImplementedType();
          m_variable->setActualType(m_variable->getImplementedType());
        }
        m_variable->outputCPP(cg, ar);
        if (act) {
          m_variable->setActualType(act);
        }
      }
    }
    if (m_offset) {
      bool lvalAt = false;
      bool rvalAt = false;
      bool byRef = false;
      bool arrRef = false;
      const char *sep = ", AccessFlags::";
      if (hasContext(UnsetContext)) {
        // do nothing
      } else if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) {
        cg_printf(".argvalAt(cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum);
      } else if (m_context & (LValue|RefValue|DeepReference)) {
        cg_printf(".lvalAt(");
        lvalAt = true;
      } else {
        byRef = (m_context & AccessContext) &&
          (!type || !type->is(Type::KindOfString));
        arrRef = byRef && type && type->is(Type::KindOfArray);
        cg_printf(".rval%s%s(",
                  arrRef || !byRef ? "At" : "", byRef ? "Ref" : "");
        rvalAt = true;
      }
      m_offset->outputCPP(cg, ar);
      if (!type || !type->is(Type::KindOfString)) {
        if (rvalAt) {
          if (byRef && !arrRef) {
            const string &tmp = cg.getReferenceTemp();
            cg_printf(", %s", tmp.empty() ? "Variant()" : tmp.c_str());
          }
          if (!hasContext(ExistContext)) {
            cg_printf(", AccessFlags::Error"); // raise undefined index error
            sep = "_";
          }
        } else if (lvalAt) {
          if (hasContext(AccessContext)) {
            // Dont copy the array if the element is an object, or
            // is referenced.
            // This is safe in AccessContext (the parent is an ArrayElement,
            // or an ObjectProperty) because applying [] to an object will
            // either invoke OffsetGet, or fatal, and modifications to a
            // referenced element would be reflected in all copies
            // of the array anyway.
            cg_printf(", AccessFlags::CheckExist");
            sep = "_";
          }
        }
        ScalarExpressionPtr sc =
          dynamic_pointer_cast<ScalarExpression>(m_offset);
        if (!hasContext(UnsetContext) && sc && sc->isLiteralString()) {
          String s(sc->getLiteralString());
          int64 n;
          if (!s.get()->isStrictlyInteger(n)) {
            if (lvalAt || rvalAt) {
              cg_printf("%sKey", sep);
            } else {
              cg_printf(", true"); // skip toKey() at run time
            }
          }
        }
      }
      cg_printf(")");
    } else {
      cg_printf(".lvalAt()");
    }
  }
}
void ObjectPropertyExpression::outputCPPObjProperty(CodeGenerator &cg,
        AnalysisResultPtr ar,
        int doExist) {
    string func = Option::ObjectPrefix;
    const char *error = ", true";
    std::string context = "";
    bool doUnset = m_context & LValue && m_context & UnsetContext;
    bool needTemp = false;

    if (cg.getOutput() != CodeGenerator::SystemCPP) {
        context = originalClassName(cg, true);
    }
    if (doUnset) {
        func = "o_unset";
        error = "";
    } else if (doExist) {
        func = doExist > 0 ? "o_isset" : "o_empty";
        error = "";
    } else {
        if (m_context & ExistContext) {
            error = ", false";
        }
        if (m_context & InvokeArgument) {
            ASSERT(cg.callInfoTop() != -1);
            func += "argval";
        } else if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) {
            if (m_context & UnsetContext) {
                assert(!(m_context & LValue)); // handled by doUnset
                func += "unsetLval";
            } else {
                func += "lval";
            }
            error = "";
            needTemp = true;
        } else {
            func += "get";
            if (isNonPrivate(ar)) {
                func += "Public";
                context = "";
            }
        }
    }

    if (m_valid && doExist) cg_printf(doExist > 0 ? "isset(" : "empty(");
    bool flag = outputCPPObject(cg, ar, doUnset || (!m_valid && doExist));
    if (flag) {
        cg_printf("id(");
        outputCPPProperty(cg, ar);
        cg_printf(")");
        if (doExist) cg_printf(", %s", doExist > 0 ? "false" : "true");
        cg_printf(")");
    } else if (!doUnset && m_valid) {
        assert(m_object->getType()->isSpecificObject());
        ScalarExpressionPtr name =
            dynamic_pointer_cast<ScalarExpression>(m_property);
        cg_printf("%s%s", Option::PropertyPrefix, name->getString().c_str());
        if (doExist) cg_printf(")");
    } else {
        cg_printf("%s(", func.c_str());
        if (hasContext(InvokeArgument)) {
            cg_printf("cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum);
        }
        outputCPPProperty(cg, ar);
        if (needTemp) {
            const string &tmp = cg.getReferenceTemp();
            context = ", " + (tmp.empty() ? "Variant()" : tmp) + context;
        }
        cg_printf("%s%s)", error, context.c_str());
    }
}
void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg,
                                           AnalysisResultPtr ar) {
  if (m_global) {
    if (!m_globalName.empty()) {
      VariableTablePtr variables = getScope()->getVariables();
      string name = variables->getGlobalVariableName(ar, m_globalName);
      cg_printf("g->%s", name.c_str());
    } else {
      cg_printf("((LVariableTable *)g)->get(");
      m_offset->outputCPP(cg, ar);
      cg_printf(")");
    }
  } else {
    TypePtr type = m_variable->getType();
    if (hasContext(UnsetContext)) {
      cg_printf("unsetLval(");
      m_variable->outputCPP(cg, ar);
      cg_printf(", ");
    } else {
      if (m_variable->is(Expression::KindOfScalarExpression) ||
          (type && (type->isInteger() ||
                    type->is(Type::KindOfDouble) ||
                    type->is(Type::KindOfObject) ||
                    type->is(Type::KindOfBoolean)))) {
        cg_printf(type && type->is(Type::KindOfString) ? "((String)" :
                  "((Variant)");
        m_variable->outputCPP(cg, ar);
        cg_printf(")");
      } else {
        m_variable->outputCPP(cg, ar);
      }
    }
    if (m_offset) {
      bool lvalAt = false;
      bool rvalAt = false;
      bool byRef = false;
      bool arrRef = false;
      const char *sep = ", AccessFlags::";
      bool isArrayType = type && type->is(Type::KindOfArray);
      bool isStringType = type && type->is(Type::KindOfString);
      bool isRealChainRoot = isChainRoot() && hasCPPCseTemp();

      TypePtr t;
      bool hasCseStore = isRealChainRoot && GetCseTempInfo(
          ar,
          static_pointer_cast<Expression>(shared_from_this()),
          t);

      if (hasContext(UnsetContext)) {
        // do nothing
      } else if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) {
        ASSERT(!isRealChainRoot); // TODO: handle this case
        cg_printf(".argvalAt(cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum);
      } else if (m_context & (LValue|RefValue|DeepReference)) {
        // if we see an array access element in LValue context, the
        // type inference pass will never infer its type to be a string
        ASSERT(!isStringType);
        if (isRealChainRoot && !isArrayType) {
          // chain roots for non array types (variants) should call
          // lvalRef()
          cg_printf(".lvalRef(");
        } else {
          cg_printf(".lvalAt(");
        }
        lvalAt = true;
      } else {
        byRef =
          ((m_context & AccessContext) || isRealChainRoot) && !isStringType;
        arrRef = byRef && isArrayType;
        cg_printf(".rval%s%s(",
                  arrRef || !byRef ? "At" : "", byRef ? "Ref" : "");
        rvalAt = true;
      }
      m_offset->outputCPP(cg, ar);
      if (!isStringType) {
        if (rvalAt) {
          if (byRef && !arrRef) {
            string tmp;
            if (hasCseStore) {
              tmp = string(Option::CseTempStoragePrefix) + m_cppCseTemp;
            } else {
              tmp = cg.getReferenceTemp();
            }
            cg_printf(", %s", tmp.empty() ? "Variant()" : tmp.c_str());
          }
          if (!hasContext(ExistContext)) {
            cg_printf(", AccessFlags::Error"); // raise undefined index error
            sep = "_";
          }
        } else if (lvalAt) {
          if (hasCseStore && !isArrayType) {
            cg_printf(", %s%s",
                Option::CseTempStoragePrefix, m_cppCseTemp.c_str());
          }
          if (hasContext(AccessContext)) {
            // Dont copy the array if the element is an object, or
            // is referenced.
            // This is safe in AccessContext (the parent is an ArrayElement,
            // or an ObjectProperty) because applying [] to an object will
            // either invoke OffsetGet, or fatal, and modifications to a
            // referenced element would be reflected in all copies
            // of the array anyway.
            cg_printf(", AccessFlags::CheckExist");
            sep = "_";
          }
        }
        ScalarExpressionPtr sc =
          dynamic_pointer_cast<ScalarExpression>(m_offset);
        if (!hasContext(UnsetContext) && sc && sc->isLiteralString()) {
          String s(sc->getLiteralString());
          int64 n;
          if (!s.get()->isStrictlyInteger(n)) {
            if (lvalAt || rvalAt) {
              cg_printf("%sKey", sep);
            } else {
              cg_printf(", true"); // skip toKey() at run time
            }
          }
        }
      }
      cg_printf(")");
    } else {
      cg_printf(".lvalAt()");
    }
  }
}
void ObjectPropertyExpression::outputCPPObjProperty(CodeGenerator &cg,
        AnalysisResultPtr ar,
        int doExist) {
    if (m_valid) {
        TypePtr type = m_object->getActualType();
        if (type->isSpecificObject()) {
            ClassScopePtr cls(type->getClass(ar, getScope()));
            if (cls) getFileScope()->addUsedClassFullHeader(cls);
        }
    }

    string func = Option::ObjectPrefix;
    const char *error = ", true";
    std::string context = "";
    bool doUnset = m_context & LValue && m_context & UnsetContext;
    bool needTemp = false;

    if (cg.getOutput() != CodeGenerator::SystemCPP) {
        context = originalClassName(cg, true);
    }
    if (doUnset) {
        func = "o_unset";
        error = "";
    } else if (doExist) {
        func = doExist > 0 ? "o_isset" : "o_empty";
        error = "";
    } else {
        if (m_context & ExistContext) {
            error = ", false";
        }
        if (m_context & InvokeArgument) {
            if (cg.callInfoTop() != -1) {
                func += "argval";
            } else {
                func += "get";
            }
        } else if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) {
            if (m_context & UnsetContext) {
                always_assert(!(m_context & LValue)); // handled by doUnset
                func += "unsetLval";
            } else {
                func += "lval";
            }
            error = "";
            needTemp = true;
        } else {
            func += "get";
            if (isNonPrivate(ar)) {
                func += "Public";
                context = "";
            }
        }
    }

    if (m_valid && !m_object->isThis() &&
            (!m_object->is(KindOfSimpleVariable) ||
             !static_pointer_cast<SimpleVariable>(m_object)->isGuarded())) {
        cg_printf("(obj_tmp = ");
        outputCPPValidObject(cg, ar, false);
        bool write_context = hasAnyContext(LValue | RefValue | DeepReference |
                                           UnsetContext | OprLValue |
                                           DeepOprLValue | DeepAssignmentLHS |
                                           AssignmentLHS) && !doUnset;
        cg_printf(", LIKELY(obj_tmp != 0) %s ", write_context ? "||" : "?");
        always_assert(m_property->is(KindOfScalarExpression));
        ScalarExpressionPtr name =
            static_pointer_cast<ScalarExpression>(m_property);
        if (doExist || doUnset) {
            cg_printf(doUnset ? "unset" : doExist > 0 ? "isset" : "empty");
        }
        ClassScopePtr cls =
            ar->findExactClass(shared_from_this(),
                               m_object->getActualType()->getName());

        if (write_context) {
            cg_printf("(throw_null_object_prop(),false),");
        }
        cg_printf("(((%s%s*)obj_tmp)->%s%s)",
                  Option::ClassPrefix, cls->getId().c_str(),
                  Option::PropertyPrefix, name->getString().c_str());

        if (!write_context) {
            cg_printf(" : (raise_null_object_prop(),%s)",
                      doUnset ? "null_variant" :
                      doExist ? doExist > 0 ? "false" : "true" :
                      nullName(ar, getCPPType()).c_str());
        }
        cg_printf(")");
        return;
    }

    if (m_valid && (doExist || doUnset)) {
        cg_printf(doUnset ? "unset(" : doExist > 0 ? "isset(" : "empty(");
    }
    bool flag = outputCPPObject(cg, ar, !m_valid && (doUnset || doExist));
    if (flag) {
        cg_printf("id(");
        outputCPPProperty(cg, ar);
        cg_printf(")");
        if (doExist) cg_printf(", %s", doExist > 0 ? "false" : "true");
        cg_printf(")");
    } else if (m_valid) {
        always_assert(m_object->getActualType() &&
                      m_object->getActualType()->isSpecificObject());
        ScalarExpressionPtr name =
            dynamic_pointer_cast<ScalarExpression>(m_property);
        cg_printf("%s%s", Option::PropertyPrefix, name->getString().c_str());
        if (doExist || doUnset) cg_printf(")");
    } else {
        cg_printf("%s(", func.c_str());
        if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) {
            cg_printf("cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum);
        }
        outputCPPProperty(cg, ar);
        if (needTemp) {
            const string &tmp = cg.getReferenceTemp();
            context = ", " + (tmp.empty() ? "Variant()" : tmp) + context;
        }
        cg_printf("%s%s)", error, context.c_str());
    }
}