Arquivos
hhvm/hphp/compiler/expression/object_method_expression.cpp
T
2013-03-08 17:52:38 -08:00

299 linhas
9.6 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() &&
(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() ||
(Option::WholeProgram && !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() &&
(!Option::WholeProgram || 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) {
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(")");
}