Arquivos
hhvm/hphp/compiler/expression/expression.cpp
T
Jordan Delong 363d1bb20f Code move src/ -> hphp/
This change is mostly for FB internal organizational reasons.
Building is not effected beyond the fact that the target now
lands in hphp/hhvm/hhvm rather than src/hhvm/hhvm.
2013-02-11 02:10:41 -08:00

1717 linhas
50 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- 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. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/expression.h>
#include <compiler/analysis/code_error.h>
#include <compiler/parser/parser.h>
#include <util/parser/hphp.tab.hpp>
#include <util/util.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/constant_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/expression/assignment_expression.h>
#include <compiler/expression/array_pair_expression.h>
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/object_property_expression.h>
#include <compiler/expression/unary_op_expression.h>
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/expression/function_call.h>
#include <compiler/analysis/file_scope.h>
#include <util/hash.h>
#include <runtime/base/array/array_iterator.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
#define DEC_EXPR_NAMES(x,t) #x
const char *Expression::Names[] = {
DECLARE_EXPRESSION_TYPES(DEC_EXPR_NAMES)
};
#define DEC_EXPR_CLASSES(x,t) Expression::t
Expression::ExprClass Expression::Classes[] = {
DECLARE_EXPRESSION_TYPES(DEC_EXPR_CLASSES)
};
Expression::Expression(EXPRESSION_CONSTRUCTOR_BASE_PARAMETERS)
: Construct(scope, loc), m_context(RValue), m_kindOf(kindOf),
m_originalScopeSet(false), m_unused(false), m_canon_id(0), m_error(0),
m_canonPtr() {
}
ExpressionPtr Expression::replaceValue(ExpressionPtr rep) {
if (hasContext(Expression::RefValue) &&
isRefable(true) && !rep->isRefable(true)) {
/*
An assignment isRefable, but the rhs may not be. Need this to
prevent "bad pass by reference" errors.
*/
ExpressionListPtr el(new ExpressionList(getScope(), getLocation(),
ExpressionList::ListKindWrapped));
el->addElement(rep);
rep->clearContext(AssignmentRHS);
rep = el;
}
if (rep->is(KindOfSimpleVariable) && !is(KindOfSimpleVariable)) {
static_pointer_cast<SimpleVariable>(rep)->setAlwaysStash();
}
rep->copyContext(m_context & ~(DeadStore|AccessContext));
if (TypePtr t1 = getType()) {
if (TypePtr t2 = rep->getType()) {
if (!Type::SameType(t1, t2)) {
rep->setExpectedType(t1);
}
}
}
if (rep->getScope() != getScope()) {
rep->resetScope(getScope());
}
return rep;
}
void Expression::copyContext(int contexts) {
unsigned val = contexts;
while (val) {
unsigned next = val & (val - 1);
unsigned low = val ^ next; // lowest set bit
setContext((Context)low);
val = next;
}
}
void Expression::clearContext() {
unsigned val = m_context;
while (val) {
unsigned next = val & (val - 1);
unsigned low = val ^ next; // lowest set bit
clearContext((Context)low);
val = next;
}
}
void Expression::setArgNum(int n) {
m_argNum = n;
int kc = getKidCount();
for (int i=0; i < kc; i++) {
ExpressionPtr kid = getNthExpr(i);
if (kid) {
kid->setArgNum(n);
}
}
}
void Expression::deepCopy(ExpressionPtr exp) {
exp->m_actualType = m_actualType;
exp->m_expectedType = m_expectedType;
exp->m_implementedType = m_implementedType;
exp->m_assertedType = m_assertedType;
exp->m_canon_id = 0;
exp->m_unused = false;
exp->m_canonPtr.reset();
exp->m_replacement.reset();
exp->clearVisited();
};
bool Expression::hasSubExpr(ExpressionPtr sub) const {
if (this == sub.get()) return true;
for (int i = getKidCount(); i--; ) {
ExpressionPtr kid = getNthExpr(i);
if (kid && kid->hasSubExpr(sub)) return true;
}
return false;
}
Expression::ExprClass Expression::getExprClass() const {
ExprClass cls = Classes[m_kindOf];
if (cls == Update) {
ExpressionPtr k = getNthExpr(0);
if (!k || !(k->hasContext(OprLValue))) cls = Expression::None;
}
return cls;
}
FileScopeRawPtr Expression::getUsedScalarScope(CodeGenerator& cg) {
return cg.getLiteralScope() ?
cg.getLiteralScope() : getFileScope();
}
bool Expression::getEffectiveScalar(Variant &v) {
if (is(KindOfExpressionList)) {
ExpressionRawPtr sub = static_cast<ExpressionList*>(this)->listValue();
if (!sub) return false;
return sub->getEffectiveScalar(v);
}
return getScalarValue(v);
}
void Expression::addElement(ExpressionPtr exp) {
assert(false);
}
void Expression::insertElement(ExpressionPtr exp, int index /* = 0 */) {
assert(false);
}
ExpressionPtr Expression::unneededHelper() {
ExpressionListPtr elist = ExpressionListPtr
(new ExpressionList(getScope(), getLocation(),
ExpressionList::ListKindWrapped));
bool change = false;
for (int i=0, n = getKidCount(); i < n; i++) {
ExpressionPtr kid = getNthExpr(i);
if (kid && kid->getContainedEffects()) {
ExpressionPtr rep = kid->unneeded();
if (rep != kid) change = true;
if (rep->is(Expression::KindOfExpressionList)) {
for (int j=0, m = rep->getKidCount(); j < m; j++) {
elist->addElement(rep->getNthExpr(j));
}
} else {
elist->addElement(rep);
}
}
}
if (change) {
getScope()->addUpdates(BlockScope::UseKindCaller);
}
int n = elist->getCount();
assert(n);
if (n == 1) {
return elist->getNthExpr(0);
} else {
return elist;
}
}
ExpressionPtr Expression::unneeded() {
if (getLocalEffects() || is(KindOfScalarExpression) || isNoRemove()) {
return static_pointer_cast<Expression>(shared_from_this());
}
if (!getContainedEffects()) {
getScope()->addUpdates(BlockScope::UseKindCaller);
return ScalarExpressionPtr
(new ScalarExpression(getScope(), getLocation(),
T_LNUMBER, string("0")));
}
return unneededHelper();
}
///////////////////////////////////////////////////////////////////////////////
bool Expression::IsIdentifier(const string &value) {
if (value.empty()) {
return false;
}
unsigned char ch = value[0];
if ((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') &&
ch < '\x7f' && ch != '_') {
return false;
}
for (unsigned int i = 1; i < value.size(); i++) {
unsigned char ch = value[i];
if (((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') &&
(ch < '0' || ch > '9') && ch < '\x7f' && ch != '_')) {
if (ch == '\\' && i < value.size() - 1 && value[i+1] != '\\') {
continue;
}
return false;
}
}
return true;
}
TypePtr Expression::getType() {
if (m_expectedType) return m_expectedType;
if (m_actualType) return m_actualType;
return Type::Any;
}
TypePtr Expression::getGenType() {
if (m_expectedType) return m_expectedType;
if (m_implementedType) return m_implementedType;
if (m_actualType) return m_actualType;
return Type::Any;
}
TypePtr Expression::getCPPType() {
if (m_implementedType) return m_implementedType;
if (m_actualType) return m_actualType;
return Type::Variant;
}
TypePtr Expression::propagateTypes(AnalysisResultConstPtr ar, TypePtr inType) {
ExpressionPtr e = getCanonTypeInfPtr();
TypePtr ret = inType;
while (e) {
if (e->getAssertedType() && !getAssertedType()) {
setAssertedType(e->getAssertedType());
}
TypePtr inferred = Type::Inferred(ar, ret, e->m_actualType);
if (!inferred) {
break;
}
ret = inferred;
e = e->getCanonTypeInfPtr();
}
return ret;
}
void Expression::analyzeProgram(AnalysisResultPtr ar) {
}
BlockScopeRawPtr Expression::getOriginalScope() {
if (!m_originalScopeSet) {
m_originalScopeSet = true;
m_originalScope = getScope();
}
return m_originalScope;
}
void Expression::setOriginalScope(BlockScopeRawPtr scope) {
m_originalScope = scope;
m_originalScopeSet = true;
}
ClassScopeRawPtr Expression::getOriginalClass() {
BlockScopeRawPtr scope = getOriginalScope();
return scope ? scope->getContainingClass() : ClassScopeRawPtr();
}
FunctionScopeRawPtr Expression::getOriginalFunction() {
BlockScopeRawPtr scope = getOriginalScope();
return scope ? scope->getContainingFunction() : FunctionScopeRawPtr();
}
string Expression::originalClassName(CodeGenerator &cg, bool withComma) {
ClassScopeRawPtr cls = getOriginalClass();
string ret = withComma ? ", " : "";
if (cls) {
if (cls == getClassScope()) {
return ret + "s_class_name";
}
return ret + Option::ClassPrefix + cls->getId() + "::s_class_name";
} else if (FunctionScopePtr funcScope = getOriginalFunction()) {
if (!funcScope->inPseudoMain()) {
return ret + "empty_string";
}
}
return withComma ? "" : "null_string";
}
void Expression::resetTypes() {
m_actualType .reset();
m_expectedType .reset();
m_implementedType.reset();
}
TypePtr Expression::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());
assert(type);
resetTypes();
TypePtr actualType = inferTypes(ar, type, coerce);
if (type->is(Type::KindOfSome) || type->is(Type::KindOfAny)) {
m_actualType = actualType;
m_expectedType.reset();
return actualType;
}
return checkTypesImpl(ar, type, actualType, coerce);
}
TypePtr Expression::checkTypesImpl(AnalysisResultConstPtr ar,
TypePtr expectedType,
TypePtr actualType, bool coerce) {
TypePtr ret;
actualType = propagateTypes(ar, actualType);
assert(actualType);
if (coerce) {
ret = Type::Coerce(ar, expectedType, actualType);
setTypes(ar, actualType, expectedType);
} else {
ret = Type::Intersection(ar, actualType, expectedType);
setTypes(ar, actualType, ret);
}
assert(ret);
return ret;
}
void Expression::setTypes(AnalysisResultConstPtr ar, TypePtr actualType,
TypePtr expectedType) {
assert(actualType);
assert(expectedType);
m_actualType = actualType;
if (!expectedType->is(Type::KindOfAny) &&
!expectedType->is(Type::KindOfSome)) {
// store the expected type if it is not Any nor Some,
// regardless of the actual type
m_expectedType = expectedType;
} else {
m_expectedType.reset();
}
// This is a special case where Type::KindOfObject means any object.
if (m_expectedType && m_expectedType->is(Type::KindOfObject) &&
!m_expectedType->isSpecificObject() &&
m_actualType->isSpecificObject()) {
m_expectedType.reset();
}
if (m_actualType->isSpecificObject()) {
boost::const_pointer_cast<AnalysisResult>(ar)->
addClassDependency(getFileScope(), m_actualType->getName());
}
}
void Expression::setDynamicByIdentifier(AnalysisResultPtr ar,
const std::string &value) {
string id = Util::toLower(value);
size_t c = id.find("::");
FunctionScopePtr fi;
ClassScopePtr ci;
if (c != 0 && c != string::npos && c+2 < id.size()) {
string cl = id.substr(0, c);
string fn = id.substr(c+2);
if (IsIdentifier(cl) && IsIdentifier(fn)) {
ci = ar->findClass(cl);
if (ci) {
fi = ci->findFunction(ar, fn, false);
if (fi) fi->setDynamic();
}
}
} else if (IsIdentifier(id)) {
fi = ar->findFunction(id);
if (fi) fi->setDynamic();
ClassScopePtr ci = ar->findClass(id, AnalysisResult::MethodName);
if (ci) {
fi = ci->findFunction(ar, id, false);
if (fi) fi->setDynamic();
}
}
}
bool Expression::CheckNeededRHS(ExpressionPtr value) {
bool needed = true;
always_assert(value);
while (value->is(KindOfAssignmentExpression)) {
value = dynamic_pointer_cast<AssignmentExpression>(value)->getValue();
}
if (value->isScalar()) {
needed = false;
} else {
TypePtr type = value->getType();
if (type && (type->is(Type::KindOfSome) || type->is(Type::KindOfAny))) {
type = value->getActualType();
}
if (type && type->isNoObjectInvolved()) needed = false;
}
return needed;
}
bool Expression::CheckNeeded(ExpressionPtr variable, ExpressionPtr value) {
// if the value may involve object, consider the variable as "needed"
// so that objects are not destructed prematurely.
bool needed = true;
if (value) needed = CheckNeededRHS(value);
if (variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(variable);
const std::string &name = var->getName();
VariableTablePtr variables = var->getScope()->getVariables();
if (needed) {
variables->addNeeded(name);
} else {
needed = variables->isNeeded(name);
}
}
return needed;
}
bool Expression::CheckVarNR(ExpressionPtr value,
TypePtr expectedType /* = TypePtr */) {
if (!expectedType) expectedType = value->getExpectedType();
return (!value->hasContext(Expression::RefValue) &&
expectedType && expectedType->is(Type::KindOfVariant) &&
(value->getCPPType()->is(Type::KindOfArray) ||
value->getCPPType()->is(Type::KindOfString) ||
value->getCPPType()->is(Type::KindOfObject) ||
value->getCPPType()->isPrimitive() ||
value->isScalar()));
}
TypePtr Expression::inferAssignmentTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce, ExpressionPtr variable,
ExpressionPtr
value /* =ExpressionPtr() */) {
assert(type);
TypePtr ret = type;
if (value) {
ret = value->inferAndCheck(ar, Type::Some, false);
if (value->isLiteralNull()) {
ret = Type::Null;
}
assert(ret);
}
BlockScopePtr scope = getScope();
if (variable->is(Expression::KindOfConstantExpression)) {
// ...as in ClassConstant statement
ConstantExpressionPtr exp =
dynamic_pointer_cast<ConstantExpression>(variable);
BlockScope *defScope = NULL;
std::vector<std::string> bases;
scope->getConstants()->check(getScope(), exp->getName(), ret,
true, ar, variable,
bases, defScope);
}
m_implementedType.reset();
TypePtr vt = variable->inferAndCheck(ar, ret, true);
if (!coerce && type->is(Type::KindOfAny)) {
ret = vt;
} else {
TypePtr it = variable->getCPPType();
if (!Type::SameType(it, ret)) {
m_implementedType = it;
}
}
if (value) {
TypePtr vat(value->getActualType());
TypePtr vet(value->getExpectedType());
TypePtr vit(value->getImplementedType());
if (vat && !vet && vit &&
Type::IsMappedToVariant(vit) &&
Type::HasFastCastMethod(vat)) {
value->setExpectedType(vat);
}
}
return ret;
}
ExpressionPtr Expression::MakeConstant(AnalysisResultConstPtr ar,
BlockScopePtr scope,
LocationPtr loc,
const std::string &value) {
ConstantExpressionPtr exp(new ConstantExpression(
scope, loc,
value));
if (value == "true" || value == "false") {
if (ar->getPhase() >= AnalysisResult::PostOptimize) {
exp->m_actualType = Type::Boolean;
}
} else if (value == "null") {
if (ar->getPhase() >= AnalysisResult::PostOptimize) {
exp->m_actualType = Type::Variant;
}
} else {
assert(false);
}
return exp;
}
void Expression::CheckPassByReference(AnalysisResultPtr ar,
ExpressionPtr param) {
if (param->hasContext(Expression::RefValue) &&
!param->isRefable(true)) {
param->setError(Expression::BadPassByRef);
Compiler::Error(Compiler::BadPassByReference, param);
}
}
unsigned Expression::getCanonHash() const {
int64 val = hash_int64(getKindOf());
for (int i = getKidCount(); i--; ) {
ExpressionPtr k = getNthExpr(i);
if (k) {
val = hash_int64(val ^ (((int64)k->getKindOf()<<32)+k->getCanonID()));
}
}
return (unsigned)val ^ (unsigned)(val >> 32);
}
bool Expression::canonCompare(ExpressionPtr e) const {
if (e->getKindOf() != getKindOf()) {
return false;
}
int kk = getKidCount();
if (kk != e->getKidCount()) {
return false;
}
for (int i = kk; i--; ) {
ExpressionPtr k1 = getNthExpr(i);
ExpressionPtr k2 = e->getNthExpr(i);
if (k1 != k2) {
if (!k1 || !k2) {
return false;
}
if (k1->getCanonID() != k2->getCanonID()) {
return false;
}
}
}
return true;
}
bool Expression::equals(ExpressionPtr other) {
if (!other) return false;
// So that we can leverage canonCompare()
setCanonID(0);
other->setCanonID(0);
if (other->getKindOf() != getKindOf()) {
return false;
}
int nKids = getKidCount();
if (nKids != other->getKidCount()) {
return false;
}
for (int i = 0; i < nKids; i++) {
ExpressionPtr thisKid = getNthExpr(i);
ExpressionPtr otherKid = other->getNthExpr(i);
if (!thisKid || !otherKid) {
if (thisKid == otherKid) continue;
return false;
}
if (!thisKid->equals(otherKid)) {
return false;
}
}
return canonCompare(other);
}
ExpressionPtr Expression::getCanonTypeInfPtr() const {
if (!m_canonPtr) return ExpressionPtr();
if (!(m_context & (LValue|RefValue|UnsetContext|DeepReference))) {
return m_canonPtr;
}
if (!hasAnyContext(AccessContext|ObjectContext) ||
!m_canonPtr->getActualType()) {
return ExpressionPtr();
}
switch (m_canonPtr->getActualType()->getKindOf()) {
case Type::KindOfArray:
{
if (!hasContext(AccessContext)) break;
if (m_canonPtr->getAssertedType()) return m_canonPtr;
if (!is(Expression::KindOfSimpleVariable)) break;
SimpleVariableConstPtr sv(
static_pointer_cast<const SimpleVariable>(shared_from_this()));
if (sv->couldBeAliased()) return ExpressionPtr();
if (hasContext(LValue) &&
!(m_context & (RefValue | UnsetContext | DeepReference))) {
return m_canonPtr;
}
}
break;
case Type::KindOfObject:
{
if (!hasContext(ObjectContext)) break;
if (m_canonPtr->getAssertedType()) return m_canonPtr;
if (!is(Expression::KindOfSimpleVariable)) break;
SimpleVariableConstPtr sv(
static_pointer_cast<const SimpleVariable>(shared_from_this()));
if (sv->couldBeAliased()) return ExpressionPtr();
if (hasContext(LValue) &&
!(m_context & (RefValue | UnsetContext | DeepReference))) {
return m_canonPtr;
}
}
default:
break;
}
return ExpressionPtr();
}
ExpressionPtr Expression::fetchReplacement() {
ExpressionPtr t = m_replacement;
m_replacement.reset();
return t;
}
void Expression::computeLocalExprAltered() {
// if no kids, do nothing
if (getKidCount() == 0) return;
bool res = false;
for (int i = 0; i < getKidCount(); i++) {
ExpressionPtr k = getNthExpr(i);
if (k) {
k->computeLocalExprAltered();
res |= k->isLocalExprAltered();
}
}
if (res) {
setLocalExprAltered();
}
}
bool Expression::isArray() const {
if (is(KindOfUnaryOpExpression)) {
return static_cast<const UnaryOpExpression*>(this)->getOp() == T_ARRAY;
}
return false;
}
bool Expression::isUnquotedScalar() const {
if (!is(KindOfScalarExpression)) return false;
return !((ScalarExpression*)this)->isQuoted();
}
ExpressionPtr Expression::MakeScalarExpression(AnalysisResultConstPtr ar,
BlockScopePtr scope,
LocationPtr loc,
CVarRef value) {
if (value.isArray()) {
ExpressionListPtr el(new ExpressionList(scope, loc,
ExpressionList::ListKindParam));
for (ArrayIter iter(value); iter; ++iter) {
ExpressionPtr k(MakeScalarExpression(ar, scope, loc, iter.first()));
ExpressionPtr v(MakeScalarExpression(ar, scope, loc, iter.second()));
if (!k || !v) return ExpressionPtr();
ArrayPairExpressionPtr ap(
new ArrayPairExpression(scope, loc, k, v, false));
el->addElement(ap);
}
if (!el->getCount()) el.reset();
return ExpressionPtr(
new UnaryOpExpression(scope, loc, el, T_ARRAY, true));
} else if (value.isNull()) {
return MakeConstant(ar, scope, loc, "null");
} else if (value.isBoolean()) {
return MakeConstant(ar, scope, loc, value ? "true" : "false");
} else {
return ScalarExpressionPtr
(new ScalarExpression(scope, loc, value));
}
}
///////////////////////////////////////////////////////////////////////////////
bool Expression::outputLineMap(CodeGenerator &cg, AnalysisResultPtr ar,
bool force /* = false */) {
switch (cg.getOutput()) {
case CodeGenerator::TrimmedPHP:
if (cg.getStream(CodeGenerator::MapFile) &&
cg.usingStream(CodeGenerator::PrimaryStream)) {
cg.useStream(CodeGenerator::MapFile);
cg_printf("%d => '%s:%d',", cg.getLineNo(CodeGenerator::PrimaryStream),
getLocation()->file, getLocation()->line1);
cg.useStream(CodeGenerator::PrimaryStream);
}
break;
case CodeGenerator::ClusterCPP:
{
if (!force &&
!(getLocalEffects() & (Construct::CanThrow|
Construct::AccessorEffect|
Construct::DiagnosticEffect))) {
return false;
}
int line = cg.getLineNo(CodeGenerator::PrimaryStream);
LocationPtr loc = getLocation();
if (loc) {
ar->recordSourceInfo(cg.getFileName(), line, loc);
if (cg.getPHPLineNo() != loc->line1) {
cg.setPHPLineNo(loc->line1);
cg_printf("LINE(%d,", loc->line1);
return true;
}
}
}
break;
default:
break;
}
return false;
}
bool Expression::outputCPPArithArg(CodeGenerator &cg, AnalysisResultPtr ar,
bool arrayOk) {
TypePtr at = getActualType();
if (at &&
(at->is(Type::KindOfString) ||
at->is(Type::KindOfObject) ||
(at->is(Type::KindOfArray) && !arrayOk)) &&
(!hasCPPTemp() || getCPPType()->isExactType())) {
if (!hasCPPTemp() && !getCPPType()->isExactType()) {
TypePtr et = getExpectedType();
setExpectedType(TypePtr());
setActualType(getCPPType());
outputCPP(cg, ar);
setActualType(at);
setExpectedType(et);
} else {
cg_printf("(Variant)(");
outputCPP(cg, ar);
cg_printf(")");
}
return true;
}
return false;
}
void Expression::outputCPPCast(CodeGenerator &cg, AnalysisResultPtr ar) {
if (m_expectedType) {
m_expectedType->outputCPPCast(cg, ar, getScope());
}
}
void Expression::outputCPPDecl(CodeGenerator &cg, AnalysisResultPtr ar) {
TypePtr type = m_actualType;
if (!type) type = Type::Variant;
type->outputCPPDecl(cg, ar, getScope());
}
std::string Expression::genCPPTemp(CodeGenerator &cg, AnalysisResultPtr ar) {
std::ostringstream os;
os << Option::TempPrefix << cg.createNewLocalId(shared_from_this());
return os.str();
}
void Expression::preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar,
int state) {
if (hasCPPTemp() || isScalar()) return;
bool fastCast = needsFastCastTemp(ar);
if (!isLocalExprAltered() && !hasEffect() &&
!fastCast && !(state & StashAll)) {
return;
}
bool killCast = false;
TypePtr srcType, dstType, dstType0;
bool needsCast = getTypeCastPtrs(ar, srcType, dstType);
bool isLvalue = (m_context & LValue);
bool isTemp = isTemporary();
bool isReferenced = true;
if (fastCast) {
isTemp = true;
killCast = true;
dstType0 = dstType;
dstType = Type::Variant;
isReferenced = couldCppTypeBeReferenced();
} else if (needsCast) {
isTemp = true;
} else {
killCast = true;
dstType = srcType;
}
if (dstType) {
if (isUnused()) {
dstType.reset();
} else switch (dstType->getKindOf()) {
case Type::KindOfAny:
case Type::KindOfSome:
dstType = Type::Variant;
break;
default:
break;
}
}
bool eltOrPropArg = hasContext(InvokeArgument) &&
(is(KindOfArrayElementExpression) || is(KindOfObjectPropertyExpression));
bool constRef = dstType &&
((m_context & (RefValue|RefParameter)) ||
(isTemp && !dstType->isPrimitive()) || eltOrPropArg ||
(isLvalue && dynamic_cast<FunctionCall*>(this)));
cg.wrapExpressionBegin();
// make LINE macro separate to not interfere with persistance of expression.
// and because nested LINE macros dont work
if (outputLineMap(cg, ar)) cg_printf("0);\n");
if (constRef) {
cg_printf("const ");
}
if (dstType) {
bool inlinedRefReturn = hasAllContext(LValue|ReturnContext) &&
!hasAnyContext(InvokeArgument|RefValue);
bool refParam = hasContext(RefValue) &&
!hasAnyContext(InvokeArgument|AssignmentRHS|ReturnContext) &&
(is(KindOfObjectPropertyExpression) ||
is(KindOfArrayElementExpression));
if (inlinedRefReturn) {
cg_printf("Variant");
} else if (refParam) {
cg_printf("VRefParamValue");
} else {
dstType->outputCPPDecl(cg, ar, getScope());
}
std::string t = genCPPTemp(cg, ar);
const char *ref =
(!inlinedRefReturn &&
(isLvalue || constRef) &&
!(state & ForceTemp)) ? "&" : "";
/*
Note that double parens are necessary:
type_name1 tmp27(type_name2(foo));
type_name1 tmp28((type_name2(foo)));
tmp27 is a function returning a type_name1 taking a parameter of
type type_name2 (not what we want!)
tmp28 is an object of type type_name1, initialized by type_name2(foo)
*/
cg_printf(" %s%s((", ref, t.c_str());
/*
In c++ a temporary bound to a reference gets the lifetime of the
reference.
So "const Variant &tmp27 = foo()" generates a temporary, and binds it
to v, with the lifetime of v.
But "const Variant &tmp27 = ref(foo())", creates a temporary, binds it
to ref's parameter, returns a reference to that, and binds that to v;
but the temporary gets destroyed at the end of the statement.
So we clear the ref context here, and output the ref when we output the
use of the temporary (top of outputCPP, below), but in that case, we also
need to set the lval context, otherwise, rvalAt would be generated for
array elements and object properties.
*/
int save = m_context;
if (inlinedRefReturn) {
cg_printf("strongBind(");
} else if (refParam) {
m_context &= ~RefParameter;
} else if (hasContext(RefValue)) {
m_context &= ~RefValue;
if (is(KindOfObjectPropertyExpression) ||
(is(KindOfArrayElementExpression) &&
!(m_context & InvokeArgument))) {
m_context |= LValue;
m_context &= ~NoLValueWrapper;
}
}
TypePtr et = m_expectedType;
TypePtr at = m_actualType;
TypePtr it = m_implementedType;
if (killCast) {
m_actualType = dstType;
m_implementedType.reset();
m_expectedType.reset();
}
outputCPP(cg, ar);
if (killCast) {
m_actualType = at;
m_expectedType = et;
m_implementedType = it;
}
m_context = save;
if (inlinedRefReturn) cg_printf(")");
cg_printf("));\n");
if (!refParam && constRef &&
(isLvalue || hasContext(DeepReference) || hasContext(UnsetContext))) {
dstType->outputCPPDecl(cg, ar, getScope());
cg_printf(" &%s_lv = const_cast<", t.c_str());
dstType->outputCPPDecl(cg, ar, getScope());
cg_printf("&>(%s);\n", t.c_str());
t += "_lv";
}
if (fastCast) {
assert(dstType0);
assert(srcType == m_implementedType);
dstType = dstType0;
if (constRef && !dstType->isPrimitive()) {
cg_printf("const ");
}
dstType->outputCPPDecl(cg, ar, getScope());
cg_printf(" %s%s_vv = ",
dstType->isPrimitive() ? "" : "&",
t.c_str());
int closeParen = 0;
string method = Type::GetFastCastMethod(
m_actualType, isReferenced, constRef);
if (!Type::SameType(m_actualType, dstType)) {
dstType->outputCPPCast(cg, ar, getScope());
cg_printf("(");
closeParen++;
if (m_actualType->is(Type::KindOfObject)) {
method = "getObjectDataOrNull";
} else if (m_actualType->is(Type::KindOfString)) {
method = "getStringDataOrNull";
} else if (m_actualType->is(Type::KindOfArray)) {
method = "getArrayDataOrNull";
}
} else if (m_actualType->isSpecificObject()) {
cg_printf("(");
closeParen++;
m_actualType->outputCPPFastObjectCast(
cg, ar, getScope(), constRef);
cg_printf("(");
closeParen++;
}
cg_printf("%s.%s()", t.c_str(), method.c_str());
for (int i = 0; i < closeParen; i++) cg_printf(")");
cg_printf(";\n");
t += "_vv";
}
m_cppTemp = t;
} else {
if (outputCPPUnneeded(cg, ar)) {
cg_printf(";\n");
}
m_cppTemp = "null";
}
}
bool Expression::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state) {
if (isScalar()) {
return false;
}
bool paramList =
is(KindOfExpressionList) &&
static_cast<ExpressionList*>(this)->getListKind() ==
ExpressionList::ListKindParam;
bool stashAll = state & StashAll;
state &= ~StashAll;
bool doStash = (state & FixOrder) != 0 || needsFastCastTemp(ar);
bool ret = doStash;
int kidState = (state & ~(StashKidVars|StashVars|FixOrder|ForceTemp));
if (state & StashKidVars) kidState |= StashVars;
int lastEffect = -1, i;
int n = getKidCount();
if (hasEffect()) {
int j;
for (i = j = 0; i < n; i++) {
ExpressionPtr k = getNthExpr(i);
if (k && !k->isScalar()) {
if (k->hasEffect()) {
lastEffect = i;
if (!j && (n > 1 || paramList) && k->isTemporary() &&
(!k->hasContext(ExistContext) ||
(!k->is(KindOfObjectPropertyExpression) &&
!k->is(KindOfArrayElementExpression)))) {
j++;
}
}
j++;
}
}
if (lastEffect >= 0 && j > 1) {
kidState |= FixOrder;
if (stashAll) {
lastEffect = n - 1;
}
ret = true;
}
}
if (!ret || cg.inExpression()) {
if (paramList) kidState |= FixOrder;
bool skipLast = !stashAll;
int lastState = kidState | StashByRef;
if (stashAll) lastState |= StashVars;
for (i = 0; i <= lastEffect; i++) {
ExpressionPtr k = getNthExpr(i);
if (k && !k->isScalar()) {
int s = kidState;
if (i == n - 1 && skipLast) s = 0;
bool noEffect = false;
if (m_kindOf == KindOfExpressionList) {
ExpressionList *el = static_cast<ExpressionList*>(this);
if (i >= el->getOutputCount()) {
s = lastState = 0;
noEffect = true;
}
}
if (k->is(KindOfSimpleVariable)) {
skipLast = false;
}
if (k->preOutputCPP(cg, ar, i == lastEffect ? lastState : s)) {
ret = true;
if (!cg.inExpression()) break;
}
if (noEffect) {
k->outputCPPUnneeded(cg, ar);
k->setCPPTemp("0");
cg_printf(";\n");
}
}
}
}
if (doStash) {
if (cg.inExpression()) {
preOutputStash(cg, ar, state);
}
}
return ret;
}
static bool checkOffsetChain(CodeGenerator &cg, ExpressionPtr e,
bool &needsRefTemp) {
bool isArray = e->is(Expression::KindOfArrayElementExpression);
if (isArray || e->is(Expression::KindOfObjectPropertyExpression)) {
if (cg.hasReferenceTemp()) {
bool lval =
e->hasContext(Expression::LValue) ||
e->hasContext(Expression::RefValue) ||
e->hasContext(Expression::DeepReference) ||
e->hasContext(Expression::UnsetContext);
if (isArray) {
if (!lval && !e->hasContext(Expression::InvokeArgument) &&
!static_pointer_cast<ArrayElementExpression>(e)->isSuperGlobal()) {
TypePtr type = e->getNthExpr(0)->getActualType();
if (!type ||
(!type->is(Type::KindOfString) && !type->is(Type::KindOfArray))) {
needsRefTemp = true;
}
}
} else {
if (lval &&
!static_pointer_cast<ObjectPropertyExpression>(e)->isValid()) {
needsRefTemp = true;
}
}
}
if (ExpressionPtr e1 = e->getNthExpr(1)) {
if (e1->hasEffect()) return true;
}
return checkOffsetChain(cg, e->getNthExpr(0), needsRefTemp);
}
return e->hasEffect();
}
bool Expression::preOutputOffsetLHS(CodeGenerator &cg,
AnalysisResultPtr ar,
int state) {
bool ret = (state & FixOrder);
bool needRefTemp = false;
if (!hasContext(AccessContext)) {
if (!ret) {
ret = checkOffsetChain(cg, getNthExpr(0), needRefTemp);
if (!ret &&
(m_context & (LValue | OprLValue |
RefValue | DeepReference | RefParameter))) {
if (ExpressionPtr e1 = getNthExpr(1)) {
if (e1->hasEffect()) {
ret = true;
}
}
}
}
}
// check to see if this elem has a CSE substitution
// or is a chain root. in this case, we need to force a temp
// since we will be taking a reference out, if we need to
// fix our order
bool forceTemp = false;
bool hasCse = false;
ExpressionPtr p(getCanonCsePtr());
if (p && p->hasCPPCseTemp()) {
assert(p->isChainRoot() && !isChainRoot());
if (!isLocalExprAltered()) return false;
hasCse = forceTemp = true;
} else if (hasCPPCseTemp() && isChainRoot()) {
forceTemp = isLocalExprAltered();
}
if (!ret) {
if (hasCse) return false;
if (needRefTemp && cg.inExpression()) {
cg.wrapExpressionBegin();
}
return Expression::preOutputCPP(cg, ar, state) || needRefTemp;
}
if (forceTemp) {
// if a cast was needed, then no need to force temp
TypePtr srcType, dstType;
if (getTypeCastPtrs(ar, srcType, dstType)) {
forceTemp = false;
}
}
if (cg.inExpression()) {
cg.wrapExpressionBegin();
state |= FixOrder;
if (!hasCse) {
if (ExpressionPtr e0 = getNthExpr(0)) {
e0->preOutputCPP(cg, ar, state & ~(StashVars));
}
if (ExpressionPtr e1 = getNthExpr(1)) {
e1->preOutputCPP(cg, ar, state & ~(StashVars));
}
}
if (!hasContext(AccessContext)) {
if (!(m_context & (AssignmentLHS | OprLValue |
ExistContext | UnsetContext))) {
if (forceTemp) state |= ForceTemp;
Expression::preOutputStash(cg, ar, state);
}
}
}
return true;
}
void Expression::collectCPPTemps(ExpressionPtrVec &collection) {
if (isChainRoot()) {
collection.push_back(static_pointer_cast<Expression>(shared_from_this()));
} else {
for (int i = 0; i < getKidCount(); i++) {
ExpressionPtr kid = getNthExpr(i);
if (kid) kid->collectCPPTemps(collection);
}
}
}
void Expression::disableCSE() {
ExpressionPtrVec v;
collectCPPTemps(v);
ExpressionPtrVec::iterator it(v.begin());
for (; it != v.end(); ++it) {
ExpressionPtr p(*it);
p->clearChainRoot();
}
}
bool Expression::hasChainRoots() {
ExpressionPtrVec v;
collectCPPTemps(v);
return !v.empty();
}
bool Expression::GetCseTempInfo(
AnalysisResultPtr ar,
ExpressionPtr p,
TypePtr &t) {
assert(p);
switch (p->getKindOf()) {
case Expression::KindOfArrayElementExpression:
{
ArrayElementExpressionPtr ap(
static_pointer_cast<ArrayElementExpression>(p));
ExpressionPtr var(ap->getVariable());
TypePtr srcType, dstType;
bool needsCast =
var->getTypeCastPtrs(ar, srcType, dstType);
TypePtr testType(needsCast ? dstType : srcType);
if (testType) {
t = testType;
return !testType->is(Type::KindOfArray);
}
return true;
}
break;
default:
break;
}
return true;
}
ExpressionPtr Expression::getNextCanonCsePtr() const {
bool dAccessCtx =
hasContext(AccessContext);
bool dLval =
hasContext(LValue);
bool dExistCtx =
hasContext(ExistContext);
bool dUnsetCtx =
hasContext(UnsetContext);
bool dGlobals = false;
if (is(KindOfArrayElementExpression)) {
ArrayElementExpressionConstPtr a(
static_pointer_cast<const ArrayElementExpression>(
shared_from_this()));
dGlobals = a->isSuperGlobal() || a->isDynamicGlobal();
}
// see rules below - no hope to find CSE candidate
if (dExistCtx || dUnsetCtx || dGlobals || (!dAccessCtx && dLval)) {
return ExpressionPtr();
}
KindOf dKindOf = getKindOf();
ExpressionPtr match;
ExpressionPtr p(getCanonLVal());
for (; p; p = p->getCanonLVal()) {
// check if p is a suitable candidate for CSE of
// downstream. the rules are:
// A) rvals can always be CSE-ed regardless of access context,
// except for unset context, which it never can be CSE-ed for
// B) lvals can only be CSE-ed if in AccessContext
// C) rvals and lvals cannot be CSE-ed for each other
// D) for now, ExistContext is not optimized
// E) no CSE for $GLOBALS[...]
// F) node types need to match
bool pLval = p->hasContext(LValue);
KindOf pKindOf = p->getKindOf();
if (dKindOf != pKindOf) continue;
if (dLval) {
assert(dAccessCtx);
bool pAccessCtx = p->hasContext(AccessContext);
if (pLval && pAccessCtx) {
// match found
match = p;
break;
}
} else {
bool pExistCtx = p->hasContext(ExistContext);
bool pUnsetCtx = p->hasContext(UnsetContext);
if (!pLval && !pExistCtx && !pUnsetCtx) {
// match found
match = p;
break;
}
}
}
return match;
}
ExpressionPtr Expression::getCanonCsePtr() const {
ExpressionPtr next(getNextCanonCsePtr());
if (next) {
if (next->isChainRoot()) return next;
return next->getCanonCsePtr();
}
return ExpressionPtr();
}
bool Expression::preOutputCPPTemp(CodeGenerator &cg, AnalysisResultPtr ar,
bool emitTemps) {
ExpressionPtrVec temps;
collectCPPTemps(temps);
if (temps.empty()) return false;
if (emitTemps) {
for (ExpressionPtrVec::iterator it(temps.begin());
it != temps.end();
++it) {
ExpressionPtr p(*it);
if (p->hasCPPCseTemp()) continue;
p->m_cppCseTemp = genCPPTemp(cg, ar);
TypePtr t;
bool needsStorage = Expression::GetCseTempInfo(ar, p, t);
bool useConst = !p->hasContext(Expression::LValue);
bool isString = t && t->is(Type::KindOfString);
const char *s = isString ? "String" : "Variant";
if (!isString) {
cg_printf("%s%s *%s%s;\n",
useConst ? "const " : "",
s,
Option::CseTempVariablePrefix,
p->m_cppCseTemp.c_str());
if (needsStorage) {
cg_printf("%s %s%s;\n",
s,
Option::CseTempStoragePrefix,
p->m_cppCseTemp.c_str());
}
} else {
cg_printf("%s %s%s;\n",
s,
Option::CseTempVariablePrefix,
p->m_cppCseTemp.c_str());
}
}
}
return true;
}
bool Expression::outputCPPBegin(CodeGenerator &cg, AnalysisResultPtr ar) {
preOutputCPPTemp(cg, ar, !cg.inExpression());
cg.setInExpression(true);
return preOutputCPP(cg, ar, 0);
}
bool Expression::outputCPPEnd(CodeGenerator &cg, AnalysisResultPtr ar) {
bool ret = cg.wrapExpressionEnd();
cg.setInExpression(false);
return ret;
}
bool Expression::getTypeCastPtrs(
AnalysisResultPtr ar, TypePtr &srcType, TypePtr &dstType) {
srcType = m_actualType;
dstType = m_expectedType;
if (m_implementedType && srcType &&
!Type::SameType(m_implementedType, srcType)) {
srcType = m_implementedType;
}
if (!srcType && dstType && Type::IsCastNeeded(ar, Type::Variant, dstType)) {
srcType = Type::Variant;
return true;
}
return dstType && srcType && ((m_context & LValue) == 0) &&
Type::IsCastNeeded(ar, srcType, dstType);
}
bool Expression::needsFastCastTemp(AnalysisResultPtr ar) {
if (is(KindOfSimpleVariable)) return false;
if (!canUseFastCast(ar)) return false;
if (hasAnyContext(ExistContext|AccessContext) &&
(is(KindOfObjectPropertyExpression) ||
is(KindOfArrayElementExpression))) {
return false;
}
assert(m_actualType);
return !m_actualType->isPrimitive();
}
bool Expression::couldCppTypeBeReferenced() {
if (is(KindOfDynamicVariable)) return true;
SimpleVariablePtr p(
dynamic_pointer_cast<SimpleVariable>(
shared_from_this()));
BlockScopeRawPtr scope(getScope());
VariableTablePtr vt(scope ? scope->getVariables() : VariableTablePtr());
// a simple variable could have its CPP type referenced if:
// it could be aliased or,
// it is a non-lval parameter or,
// the scope it lives in has a dynamic variable or contains extract()
// note that we default to true (the conservative case) if no symbol
// or variable table is found
return p ?
(p->couldBeAliased() ||
(!p->getSymbol() ||
(p->getSymbol()->isParameter() && !p->getSymbol()->isLvalParam())) ||
(!vt || (vt->getAttribute(VariableTable::ContainsDynamicVariable) ||
vt->getAttribute(VariableTable::ContainsExtract)))) :
!isTemporary();
}
bool Expression::canUseFastCast(AnalysisResultPtr ar) {
TypePtr srcType, dstType;
getTypeCastPtrs(ar, srcType, dstType);
// if the impl type is Variant and the actual type is known
// with a fast cast method, and we have a dst type that
// is not Variant (in CPP), then we have something to benefit
// from doing a fast cast and should emit one.
if (m_implementedType &&
Type::IsMappedToVariant(m_implementedType) &&
m_actualType &&
Type::HasFastCastMethod(m_actualType) &&
dstType &&
!Type::IsMappedToVariant(dstType)) {
if (m_assertedType) return true;
if (is(KindOfSimpleVariable) &&
static_cast<SimpleVariable*>(this)->isGuarded()) {
return true;
}
}
return false;
}
bool Expression::outputCPPGuardedObjectPtr(CodeGenerator &cg) {
if (is(KindOfSimpleVariable) &&
static_cast<SimpleVariable*>(this)->isGuarded()) {
TypePtr at = getActualType();
if (at && at->is(Type::KindOfObject)) {
TypePtr it = getImplementedType();
if (it && !it->is(Type::KindOfObject)) {
TypePtr et = getExpectedType();
if (!et || !et->is(Type::KindOfObject)) {
cg_printf(".getObjectData()");
return true;
}
}
cg_printf(".get()");
return true;
}
}
return false;
}
void Expression::outputCPPInternal(CodeGenerator &cg, AnalysisResultPtr ar) {
if (hasError(Expression::BadPassByRef)) {
cg_printf("throw_fatal(\"bad pass by reference\")");
return;
}
int closeParen = 0;
TypePtr srcType, dstType;
bool needsCast = getTypeCastPtrs(ar, srcType, dstType);
bool useFastCast = false;
bool isReferenced = true;
bool isLval = (m_context & LValue);
if (canUseFastCast(ar)) {
useFastCast = true;
isReferenced = couldCppTypeBeReferenced();
}
if (needsCast) {
assert(dstType);
bool isSpecObj = m_actualType && m_actualType->isSpecificObject();
if (!useFastCast ||
!Type::SameType(m_actualType, dstType) ||
isSpecObj) {
if (useFastCast && isSpecObj) {
if (!Type::SameType(m_actualType, dstType)) {
dstType->outputCPPCast(cg, ar, getScope());
cg_printf("(");
closeParen++;
} else {
// specific object is special, since we do not have
// a fast cast method into a specific object on Variant,
// we must emit an additional (but also fast) cast.
// In the end, the cast will look like (for example):
//
// (const X&)(v_var.asCObjRef())
cg_printf("(");
closeParen++;
m_actualType->outputCPPFastObjectCast(cg, ar, getScope(), !isLval);
cg_printf("(");
closeParen++;
}
} else {
dstType->outputCPPCast(cg, ar, getScope());
cg_printf("(");
closeParen++;
}
}
} else {
if (hasContext(RefValue) && !hasContext(NoRefWrapper) &&
isRefable()) {
if (hasContext(RefParameter)) {
cg_printf("strongBind(");
} else {
cg_printf("ref(");
}
useFastCast = false; // cannot ref() or strongBind() a non-variant
closeParen++;
}
if (is(Expression::KindOfArrayElementExpression)) {
if (((m_context & LValue) || ((m_context & RefValue) &&
!(m_context & InvokeArgument))) &&
!(m_context & NoLValueWrapper)) {
isLval = true;
cg_printf("lval(");
closeParen++;
}
}
}
bool needsDeref = false;
if (hasCPPCseTemp()) {
TypePtr t;
GetCseTempInfo(
ar,
static_pointer_cast<Expression>(shared_from_this()),
t);
if (!t || !t->is(Type::KindOfString)) needsDeref = true;
if (isChainRoot()) {
if (!needsDeref) {
cg_printf("(%s%s = (",
Option::CseTempVariablePrefix, m_cppCseTemp.c_str());
closeParen += 2;
} else {
cg_printf("(*(%s%s = &(",
Option::CseTempVariablePrefix, m_cppCseTemp.c_str());
closeParen += 3;
}
}
}
if (hasCPPCseTemp() && !isChainRoot()) {
if (needsDeref) cg_printf("(*");
cg_printf("%s%s",
Option::CseTempVariablePrefix, m_cppCseTemp.c_str());
if (needsDeref) cg_printf(")");
} else {
outputCPPImpl(cg, ar);
}
if (useFastCast) {
assert(srcType == m_implementedType);
string method;
if (!Type::SameType(m_actualType, dstType)) {
if (m_actualType->is(Type::KindOfObject)) {
method = "getObjectDataOrNull";
} else if (m_actualType->is(Type::KindOfString)) {
method = "getStringDataOrNull";
} else if (m_actualType->is(Type::KindOfArray)) {
method = "getArrayDataOrNull";
}
}
if (method.empty()) {
method = Type::GetFastCastMethod(
m_actualType, isReferenced,
!isLval && !(m_context & UnsetContext));
}
cg_printf(".%s()", method.c_str());
}
for (int i = 0; i < closeParen; i++) {
cg_printf(")");
}
string comment;
if (is(Expression::KindOfScalarExpression)) {
ScalarExpressionPtr exp =
dynamic_pointer_cast<ScalarExpression>(shared_from_this());
comment = exp->getComment();
} else if (is(Expression::KindOfConstantExpression)) {
ConstantExpressionPtr exp =
dynamic_pointer_cast<ConstantExpression>(shared_from_this());
comment = exp->getComment();
}
if (!comment.empty()) {
if (cg.inComments()) {
cg_printf(" (%s)", comment.c_str());
} else {
cg_printf(" /* %s */", comment.c_str());
}
}
}
bool Expression::outputCPPUnneeded(CodeGenerator &cg, AnalysisResultPtr ar) {
if (hasEffect() && m_cppTemp.empty()) {
setUnused(true);
outputCPP(cg, ar);
return true;
}
return false;
}
void Expression::outputCPP(CodeGenerator &cg, AnalysisResultPtr ar) {
bool inExpression = cg.inExpression();
bool wrapped = false;
TypePtr et = m_expectedType;
TypePtr at = m_actualType;
TypePtr it = m_implementedType;
if (isUnused()) {
if (et) {
m_expectedType.reset();
}
if (it) {
m_implementedType.reset();
m_actualType = it;
}
}
if (!inExpression) {
wrapped = outputCPPBegin(cg, ar);
}
ExpressionPtr p(getCanonCsePtr());
if (p && p->hasCPPCseTemp() && !hasCPPCseTemp()) {
assert(p->isChainRoot() && !isChainRoot());
m_cppCseTemp = p->m_cppCseTemp;
}
if (!m_cppTemp.empty()) {
bool ref = hasContext(RefValue) &&
!hasContext(NoRefWrapper) &&
(hasAnyContext(ReturnContext|AssignmentRHS|RefParameter) ||
!(is(KindOfObjectPropertyExpression) ||
is(KindOfArrayElementExpression))) &&
isRefable();
if (ref) {
if (m_context & RefParameter) {
cg_printf("strongBind(");
} else {
cg_printf("ref(");
}
}
cg_printf("%s", m_cppTemp.c_str());
if (ref) cg_printf(")");
} else {
bool linemap = outputLineMap(cg, ar);
if (linemap) cg_printf("(");
outputCPPInternal(cg, ar);
if (linemap) cg_printf("))");
}
m_implementedType = it;
m_actualType = at;
m_expectedType = et;
if (!inExpression) {
if (wrapped) cg_printf(";");
cg.wrapExpressionEnd();
cg.setInExpression(inExpression);
}
}
void Expression::outputCPPExistTest(CodeGenerator &cg, AnalysisResultPtr ar,
int op) {
switch (op) {
case T_ISSET: cg_printf("isset("); break;
case T_EMPTY: cg_printf("empty("); break;
default: assert(false);
}
outputCPP(cg, ar);
cg_printf(")");
}
void Expression::outputCPPUnset(CodeGenerator &cg, AnalysisResultPtr ar) {
cg_printf("unset(");
outputCPP(cg, ar);
cg_printf(")");
}