Arquivos
hhvm/hphp/compiler/expression/object_method_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

491 linhas
16 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/object_method_expression.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/statement/statement.h>
#include <util/util.h>
#include <util/hash.h>
#include <compiler/option.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/parser/parser.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ObjectMethodExpression::ObjectMethodExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr object, ExpressionPtr method, ExpressionListPtr params)
: FunctionCall(
EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ObjectMethodExpression),
method, "", params, ExpressionPtr()),
m_object(object), m_bindClass(true) {
m_object->setContext(Expression::ObjectContext);
m_object->clearContext(Expression::LValue);
m_object->clearContext(Expression::AccessContext);
}
ExpressionPtr ObjectMethodExpression::clone() {
ObjectMethodExpressionPtr exp(new ObjectMethodExpression(*this));
FunctionCall::deepCopy(exp);
exp->m_object = Clone(m_object);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void ObjectMethodExpression::analyzeProgram(AnalysisResultPtr ar) {
FunctionCall::analyzeProgram(ar);
m_object->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
FunctionScopePtr func = m_funcScope;
if (!func && m_object->isThis() && !m_name.empty()) {
ClassScopePtr cls = getClassScope();
if (cls) {
m_classScope = cls;
func = cls->findFunction(ar, m_name, true, true);
if (func &&
!cls->isInterface() &&
!(func->isVirtual() &&
(ar->isSystem() || func->isAbstract() ||
(func->hasOverride() &&
cls->getAttribute(ClassScope::NotFinal))) &&
!func->isPerfectVirtual())) {
m_funcScope = func;
func->addCaller(getScope());
}
}
}
markRefParams(func, m_name, canInvokeFewArgs());
}
// This is OK because AnalyzeFinal is guaranteed to run for a CPP
// target, regardless of opts (and we only need the following
// for CPP targets)
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
// necessary because we set the expected type of m_object to
// Type::Some during type inference.
TypePtr at(m_object->getActualType());
TypePtr it(m_object->getImplementedType());
if (!m_object->isThis() && at && at->is(Type::KindOfObject)) {
if (at->isSpecificObject() && it && Type::IsMappedToVariant(it)) {
// fast-cast inference
ClassScopePtr scope(ar->findClass(at->getName()));
if (scope) {
// add a dependency to m_object's class type
// to allow the fast cast to succeed
addUserClass(ar, at->getName());
}
}
m_object->setExpectedType(at);
}
}
}
ConstructPtr ObjectMethodExpression::getNthKid(int n) const {
if (!n) return m_object;
return FunctionCall::getNthKid(n);
}
void ObjectMethodExpression::setNthKid(int n, ConstructPtr cp) {
if (!n) {
m_object = boost::dynamic_pointer_cast<Expression>(cp);
} else {
FunctionCall::setNthKid(n, cp);
}
}
TypePtr ObjectMethodExpression::inferTypes(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
assert(false);
return TypePtr();
}
void ObjectMethodExpression::setInvokeParams(AnalysisResultPtr ar) {
FunctionScope::FunctionInfoPtr info;
if (Option::WholeProgram) {
info = FunctionScope::GetFunctionInfo(m_name);
}
if (!Option::WholeProgram || info || m_name.empty()) {
for (int i = m_params->getCount(); i--; ) {
if (!info || info->isRefParam(i)) {
m_params->markParam(i, canInvokeFewArgs());
}
}
}
// If we cannot find information of the so-named function, it might not
// exist, or it might go through __call(), either of which cannot have
// reference parameters.
for (int i = 0; i < m_params->getCount(); i++) {
(*m_params)[i]->inferAndCheck(ar, Type::Variant, false);
}
m_params->resetOutputCount();
}
ExpressionPtr ObjectMethodExpression::preOptimize(AnalysisResultConstPtr ar) {
if (ar->getPhase() < AnalysisResult::FirstPreOptimize) {
return ExpressionPtr();
}
if (m_classScope && m_funcScope &&
(!m_funcScope->isVirtual() ||
(!ar->isSystem() && !m_funcScope->hasOverride()))) {
if (Option::DynamicInvokeFunctions.size()) {
if (Option::DynamicInvokeFunctions.find(
m_classScope->getName() + "::" + m_funcScope->getName()) !=
Option::DynamicInvokeFunctions.end()) {
setNoInline();
}
}
return inliner(ar, m_object, "");
}
return ExpressionPtr();
}
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
assert(type);
IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());
resetTypes();
reset();
ConstructPtr self = shared_from_this();
TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false);
m_valid = true;
m_bindClass = true;
if (m_name.empty()) {
m_nameExp->inferAndCheck(ar, Type::String, false);
setInvokeParams(ar);
// we have to use a variant to hold dynamic value
return checkTypesImpl(ar, type, Type::Variant, coerce);
}
ClassScopePtr cls;
if (objectType && !objectType->getName().empty()) {
if (m_classScope && !strcasecmp(objectType->getName().c_str(),
m_classScope->getName().c_str())) {
cls = m_classScope;
} else {
cls = ar->findExactClass(shared_from_this(), objectType->getName());
}
}
if (!cls) {
m_classScope.reset();
m_funcScope.reset();
m_valid = false;
setInvokeParams(ar);
return checkTypesImpl(ar, type, Type::Variant, coerce);
}
if (m_classScope != cls) {
m_classScope = cls;
m_funcScope.reset();
}
FunctionScopePtr func = m_funcScope;
if (!func) {
func = cls->findFunction(ar, m_name, true, true);
if (!func) {
if (!cls->isTrait() &&
!cls->getAttribute(ClassScope::MayHaveUnknownMethodHandler) &&
!cls->getAttribute(ClassScope::HasUnknownMethodHandler) &&
!cls->getAttribute(ClassScope::InheritsUnknownMethodHandler)) {
if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
if (!Option::AllDynamic) {
setDynamicByIdentifier(ar, m_name);
}
} else {
Compiler::Error(Compiler::UnknownObjectMethod, self);
}
}
m_valid = false;
setInvokeParams(ar);
return checkTypesImpl(ar, type, Type::Variant, coerce);
}
m_funcScope = func;
func->addCaller(getScope(), !type->is(Type::KindOfAny));
}
bool valid = true;
m_bindClass = func->isStatic();
// use $this inside a static function
if (m_object->isThis()) {
FunctionScopePtr localfunc = getFunctionScope();
if (localfunc->isStatic()) {
if (getScope()->isFirstPass()) {
Compiler::Error(Compiler::MissingObjectContext, self);
}
valid = false;
}
}
// invoke() will return Variant
if (cls->isInterface() ||
(func->isVirtual() &&
(ar->isSystem() || func->isAbstract() ||
(func->hasOverride() && cls->getAttribute(ClassScope::NotFinal))) &&
!func->isPerfectVirtual())) {
valid = false;
}
if (!valid) {
setInvokeParams(ar);
checkTypesImpl(ar, type, Type::Variant, coerce);
m_valid = false; // so we use invoke() syntax
if (!Option::AllDynamic) {
func->setDynamic();
}
assert(m_actualType);
return m_actualType;
}
assert(func);
return checkParamsAndReturn(ar, type, coerce, func, false);
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ObjectMethodExpression::outputPHP(CodeGenerator &cg,
AnalysisResultPtr ar) {
outputLineMap(cg, ar);
m_object->outputPHP(cg, ar);
cg_printf("->");
if (m_nameExp->getKindOf() == Expression::KindOfScalarExpression) {
m_nameExp->outputPHP(cg, ar);
} else {
cg_printf("{");
m_nameExp->outputPHP(cg, ar);
cg_printf("}");
}
cg_printf("(");
m_params->outputPHP(cg, ar);
cg_printf(")");
}
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;
bool checkedThis = getFunctionScope()->isStatic();
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
if (!cls->derivesFrom(ar, thisImplType->getName(), true, false)) {
assert(cls->derivesFromRedeclaring() &&
implCls->derivedByDynamic());
checkedThis = true;
}
cg_printf("static_cast<%s%s*>(",
Option::ClassPrefix,
cls->getId().c_str());
close = true;
}
}
if (checkedThis) {
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(")");
}
}
void ObjectMethodExpression::outputCPPObjectCall(CodeGenerator &cg,
AnalysisResultPtr ar) {
outputCPPObject(cg, ar);
bool isThis = m_object->isThis();
if (!isThis) {
m_object->outputCPPGuardedObjectPtr(cg);
cg_printf("->");
}
if (m_bindClass && m_classScope) {
cg_printf("bindThis(fi.getThreadInfo()), %s%s::",
Option::ClassPrefix, m_classScope->getId().c_str());
}
}
bool ObjectMethodExpression::preOutputCPP(CodeGenerator &cg,
AnalysisResultPtr ar, int state) {
if (!m_name.empty() && m_valid && m_object->getType()->isSpecificObject()) {
if (m_object->hasEffect()) {
if (cg.inExpression()) {
m_object->preOutputCPP(cg, ar, FixOrder);
if (m_params) m_params->preOutputCPP(cg, ar, 0);
if (state & FixOrder) {
preOutputStash(cg, ar, state);
}
}
return true;
}
return FunctionCall::preOutputCPP(cg, ar, state);
}
// Short circuit out if inExpression() returns false
if (!cg.inExpression()) return true;
cg.wrapExpressionBegin();
m_ciTemp = cg.createNewLocalId(shared_from_this());
if (outputLineMap(cg, ar)) cg_printf("0);\n");
bool isThis = m_object->isThis();
if (!isThis) {
int s = 0;
if (m_name.empty() &&
m_nameExp->hasEffect() && !m_object->isScalar()) {
s = FixOrder;
}
m_object->preOutputCPP(cg, ar, s);
}
if (m_name.empty()) {
m_nameExp->preOutputCPP(cg, ar, 0);
}
cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp);
if (!isThis) {
TypePtr t = m_object->getType();
if (t && t->is(Type::KindOfObject)) {
cg_printf("CObjRef obj%d = ", m_ciTemp);
} else {
cg_printf("CVarRef obj%d = ", m_ciTemp);
}
outputCPPObject(cg, ar);
cg_printf(";\n");
}
if (m_name.empty()) {
cg_printf("CStrRef mth%d = ", m_ciTemp);
m_nameExp->outputCPP(cg, ar);
cg_printf(";\n");
}
cg_printf("mcp%d.methodCall((", m_ciTemp);
if (isThis) {
if (!getClassScope() || getClassScope()->derivedByDynamic() ||
!static_pointer_cast<SimpleVariable>(m_object)->isGuarded()) {
cg_printf("GET_THIS_VALID()");
} else {
cg_printf("this");
}
} else {
cg_printf("obj%d", m_ciTemp);
m_object->outputCPPGuardedObjectPtr(cg);
}
cg_printf("), ");
if (!m_name.empty()) {
strhash_t hash = hash_string_i(m_name.c_str());
cg_printString(m_origName, ar, shared_from_this());
cg_printf(", " STRHASH_FMT ");\n", hash);
} else {
cg_printf("mth%d, -1);\n", m_ciTemp);
}
cg_printf("const CallInfo *cit%d ATTRIBUTE_UNUSED = mcp%d.ci;\n",
m_ciTemp, m_ciTemp);
if (m_params && m_params->getCount() > 0) {
cg.pushCallInfo(m_ciTemp);
m_params->preOutputCPP(cg, ar, 0);
cg.popCallInfo();
}
if (state & FixOrder) {
cg.pushCallInfo(m_ciTemp);
preOutputStash(cg, ar, state);
cg.popCallInfo();
}
return true;
}
void ObjectMethodExpression::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
if (!m_name.empty() && m_valid && m_object->getType()->isSpecificObject()) {
// Direct method call
ClassScopePtr cls(m_object->getType()->getClass(ar, getScope()));
if (cls) getFileScope()->addUsedClassFullHeader(cls);
if (m_bindClass && m_classScope) cg_printf("(");
outputCPPObjectCall(cg, ar);
cg_printf("%s%s(", m_funcScope ?
m_funcScope->getPrefix(ar, m_params) : Option::MethodPrefix,
m_name.c_str());
FunctionScope::OutputCPPArguments(m_params, m_funcScope, cg, ar,
m_extraArg, m_variableArgument,
m_argArrayId, m_argArrayHash,
m_argArrayIndex);
cg_printf(")");
if (m_bindClass && m_classScope) cg_printf(")");
} else {
bool maybeStatic = true;
if (Option::WholeProgram && !ar->isSystem() && !m_name.empty()) {
FunctionScope::FunctionInfoPtr info =
FunctionScope::GetFunctionInfo(m_name);
if (info && !info->getMaybeStatic()) maybeStatic = false;
}
cg_printf("(mcp%d.%s->",
m_ciTemp, maybeStatic ? "bindClass(fi)" : "ci" );
outputDynamicCall(cg, ar, true);
}
}