Arquivos
hhvm/hphp/compiler/expression/object_method_expression.cpp
T
mwilliams d39bf0bcfe Method names should not be coerced to string
Zend fatals if the type is not string, where hphp would
coerce to string. In RepoAuthoritative mode, this meant that
scalar expressions would be converted to strings at bytecode
emission time, bypassing the fatal (or, in most cases, resulting
in a different fatal).
2013-04-18 12:19:35 -07:00

299 linhas
9.5 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::Some, 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(")");
}