void ObjectMethodExpression::outputCPPObject(CodeGenerator &cg,
                                             AnalysisResultPtr ar) {
  bool isThis = m_object->isThis();
  if (isThis) {
    TypePtr thisImplType(m_object->getImplementedType());
    TypePtr thisActType (m_object->getActualType());
    bool close = false;
    if (thisImplType) {
      ASSERT(thisActType);
      ASSERT(!Type::SameType(thisActType, thisImplType));
      ClassScopePtr implCls(thisImplType->getClass(ar, getScope()));
      if (implCls &&
          !implCls->derivesFrom(ar, thisActType->getName(), true, false)) {
        // This happens in this case:
        // if ($this instanceof Y) {
        //   ... $this->meth() ...
        // }
        ClassScopePtr cls(thisActType->getClass(ar, getScope()));
        ASSERT(cls && !cls->derivedByDynamic()); // since we don't do type
                                                 // assertions for these
        cg_printf("static_cast<%s%s*>(",
                  Option::ClassPrefix,
                  cls->getId().c_str());
        close = true;
      }
    }
    if (getFunctionScope()->isStatic()) {
      if (close) {
        cg_printf("GET_THIS_VALID()");
      } else {
        cg_printf("GET_THIS_ARROW()");
      }
    } else {
      if (close) cg_printf("this");
    }
    if (close) {
      cg_printf(")->");
    }
  } else {
    TypePtr t = m_object->getType();
    bool ok = !t || t->is(Type::KindOfObject) || t->is(Type::KindOfVariant);
    if (!ok) cg_printf("Variant(");
    m_object->outputCPP(cg, ar);
    if (!ok) cg_printf(")");
  }
}
bool ObjectPropertyExpression::outputCPPObject(CodeGenerator &cg,
        AnalysisResultPtr ar,
        bool noEvalOnError) {
    if (m_object->isThis()) {
        TypePtr thisImplType(m_object->getImplementedType());
        TypePtr thisActType (m_object->getActualType());
        bool close = false;
        if (m_valid && thisImplType) {
            ASSERT(thisActType);
            ASSERT(!Type::SameType(thisActType, thisImplType));
            ClassScopePtr implCls(thisImplType->getClass(ar, getScope()));
            if (implCls &&
                    !implCls->derivesFrom(ar, thisActType->getName(), true, false)) {
                // This happens in this case:
                // if ($this instanceof Y) {
                //   ... $this->prop ...
                // }
                ClassScopePtr cls(thisActType->getClass(ar, getScope()));
                ASSERT(cls && !cls->derivedByDynamic()); // since we don't do type
                // assertions for these
                cg_printf("static_cast<%s%s*>(",
                          Option::ClassPrefix,
                          cls->getId().c_str());
                close = true;
            }
        }
        if (m_valid) {
            if (!m_object->getOriginalClass()) {
                m_valid = false;
            } else {
                FunctionScopeRawPtr fs = m_object->getOriginalFunction();
                if (!fs || fs->isStatic()) {
                    m_valid = false;
                } else if (m_object->getOriginalClass() != getClassScope()) {
                    if (m_object->getOriginalClass()->isRedeclaring()) {
                        m_valid = false;
                    } else {
                        m_objectClass = getClassScope();
                    }
                }
            }
        }
        if (m_valid) {
            if (close) cg_printf("this");
        } else {
            if (!getClassScope() || getClassScope()->derivedByDynamic() ||
                    !static_pointer_cast<SimpleVariable>(m_object)->isGuardedThis()) {
                if (close) {
                    cg_printf("GET_THIS_VALID()");
                } else {
                    cg_printf("GET_THIS_ARROW()");
                }
            }
        }
        if (close) {
            cg_printf(")->");
        }
    } else if (m_valid) {
        TypePtr act;
        if (!m_object->hasCPPTemp() && m_object->getImplementedType() &&
                !Type::SameType(m_object->getImplementedType(),
                                m_object->getActualType())) {
            act = m_object->getActualType();
            m_object->setActualType(m_object->getImplementedType());
            ClassScopePtr cls = ar->findExactClass(shared_from_this(),
                                                   act->getName());
            cg_printf("((%s%s*)", Option::ClassPrefix, cls->getId().c_str());
        }
        m_object->outputCPP(cg, ar);
        if (act) {
            if (m_object->getImplementedType()->is(Type::KindOfObject)) {
                cg_printf(".get())");
            } else {
                cg_printf(".getObjectData())");
            }
            m_object->setActualType(act);
        }
        cg_printf("->");
    } else {
        TypePtr t = m_object->getType();
        bool ok = t && (t->is(Type::KindOfObject) || t->is(Type::KindOfVariant));
        if (noEvalOnError && !ok) {
            if (!t || !t->is(Type::KindOfArray)) {
                cg_printf("(");
                if (m_object->outputCPPUnneeded(cg, ar)) cg_printf(", ");
                return true;
            }
        }
        ok = ok || !t;
        if (!ok) cg_printf("Variant(");
        m_object->outputCPP(cg, ar);
        if (!ok) cg_printf(")");
        cg_printf(".");
    }

    if (m_valid && m_propSym->isPrivate() &&
            m_objectClass != getOriginalClass()) {
        cg_printf("%s%s::",
                  Option::ClassPrefix, getOriginalClass()->getId().c_str());
    }
    return false;
}
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());
    }
}