Arquivos
hhvm/hphp/compiler/expression/parameter_expression.cpp
T
2013-05-15 13:05:04 -07:00

297 linhas
9.9 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/type_annotation.h>
#include <compiler/expression/parameter_expression.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/code_error.h>
#include <util/util.h>
#include <compiler/option.h>
#include <compiler/expression/constant_expression.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ParameterExpression::ParameterExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
TypeAnnotationPtr type, bool hhType, const std::string &name, bool ref,
ExpressionPtr defaultValue, ExpressionPtr attributeList)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ParameterExpression)),
m_originalType(type), m_name(name), m_hhType(hhType), m_ref(ref),
m_defaultValue(defaultValue), m_attributeList(attributeList) {
m_type = Util::toLower(type ? type->simpleName() : "");
if (m_defaultValue) {
m_defaultValue->setContext(InParameterExpression);
}
}
ExpressionPtr ParameterExpression::clone() {
ParameterExpressionPtr exp(new ParameterExpression(*this));
Expression::deepCopy(exp);
exp->m_defaultValue = Clone(m_defaultValue);
exp->m_attributeList = Clone(m_attributeList);
return exp;
}
const std::string ParameterExpression::getOriginalTypeHint() const {
assert(hasTypeHint());
return m_originalType->simpleName();
}
const std::string ParameterExpression::getUserTypeHint() const {
assert(hasUserType());
return m_originalType->fullName();
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
void ParameterExpression::parseHandler(ClassScopePtr cls) {
// Trait has not been 'inlined' into using class so context is not available
if (!m_type.empty() && !cls->isTrait()) {
fixupSelfAndParentTypehints(cls);
if (m_defaultValue) {
compatibleDefault();
}
}
}
void ParameterExpression::fixupSelfAndParentTypehints(ClassScopePtr cls) {
if (m_type == "self") {
m_type = cls->getName();
} else if (m_type == "parent") {
if (!cls->getOriginalParent().empty()) {
m_type = Util::toLower(cls->getOriginalParent());
}
}
}
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void ParameterExpression::analyzeProgram(AnalysisResultPtr ar) {
if (m_defaultValue) m_defaultValue->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (!m_type.empty()) {
addUserClass(ar, m_type);
}
// Have to use non const ref params for magic methods
FunctionScopePtr fs = getFunctionScope();
if (fs->isMagicMethod() || fs->getName() == "offsetget") {
fs->getVariables()->addLvalParam(m_name);
}
}
}
ConstructPtr ParameterExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_defaultValue;
default:
assert(false);
break;
}
return ConstructPtr();
}
int ParameterExpression::getKidCount() const {
return 1;
}
void ParameterExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_defaultValue = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
break;
}
}
TypePtr ParameterExpression::getTypeSpecForClass(AnalysisResultPtr ar,
bool forInference) {
TypePtr ret;
if (forInference) {
ClassScopePtr cls = ar->findClass(m_type);
if (!cls || cls->isRedeclaring() || cls->derivedByDynamic()) {
if (!cls && getScope()->isFirstPass()) {
ConstructPtr self = shared_from_this();
Compiler::Error(Compiler::UnknownClass, self);
}
ret = Type::Variant;
}
}
if (!ret) {
ret = Type::CreateObjectType(m_type);
}
always_assert(ret);
return ret;
}
TypePtr ParameterExpression::getTypeSpec(AnalysisResultPtr ar,
bool forInference) {
const Type::TypePtrMap &types = Type::GetTypeHintTypes(m_hhType);
Type::TypePtrMap::const_iterator iter;
TypePtr ret;
if (m_type.empty()) {
ret = Type::Some;
} else if ((iter = types.find(m_type)) != types.end()) {
ret = iter->second;
} else {
ret = getTypeSpecForClass(ar, forInference);
}
ConstantExpressionPtr p;
if (ret->isPrimitive() &&
m_defaultValue &&
(p = dynamic_pointer_cast<ConstantExpression>(m_defaultValue)) &&
p->isNull()) {
// if we have a primitive type on the LHS w/ a default
// of null, then don't bother to infer it's type, since we will
// not specialize for this case
ret = Type::Some;
}
// we still want the above to run, so to record errors and infer defaults
if (m_ref && forInference) {
ret = Type::Variant;
}
return ret;
}
TypePtr ParameterExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
assert(type->is(Type::KindOfSome) || type->is(Type::KindOfAny));
TypePtr ret = getTypeSpec(ar, true);
VariableTablePtr variables = getScope()->getVariables();
// Functions that can be called dynamically have to have
// variant parameters, even if they have a type hint
if ((Option::AllDynamic || getFunctionScope()->isDynamic()) ||
getFunctionScope()->isRedeclaring() ||
getFunctionScope()->isVirtual()) {
if (!Option::HardTypeHints || !ret->isExactType()) {
variables->forceVariant(ar, m_name, VariableTable::AnyVars);
ret = Type::Variant;
}
}
if (m_defaultValue && !m_ref) {
TypePtr r = m_defaultValue->inferAndCheck(ar, Type::Some, false);
if (!m_defaultValue->is(KindOfConstantExpression) ||
!static_pointer_cast<ConstantExpression>(m_defaultValue)->isNull()) {
ret = Type::Coerce(ar, r, ret);
}
}
// parameters are like variables, but we need to remember these are
// parameters so when variable table is generated, they are not generated
// as declared variables.
return variables->addParamLike(m_name, ret, ar, shared_from_this(),
getScope()->isFirstPass());
}
void ParameterExpression::compatibleDefault() {
bool compat = true;
if (!m_defaultValue || !hasTypeHint()) return;
DataType defaultType = KindOfUninit;
if (m_defaultValue->isArray()) {
defaultType = KindOfArray;
} else if (m_defaultValue->isLiteralNull()) {
defaultType = KindOfNull;
} else {
Variant defaultValue;
if (m_defaultValue->getScalarValue(defaultValue)) {
defaultType = defaultValue.getType();
}
}
const char* msg = "Default value for parameter %s with type %s "
"needs to have the same type as the type hint %s";
if (m_hhType) {
// Normally a named type like 'int' is compatable with Int but not integer
// Since the default value's type is inferred from the value itself it is
// ok to compare against the lower case version of the type hint in hint
const char* hint = getTypeHint().c_str();
switch(defaultType) {
case KindOfBoolean:
compat = (!strcmp(hint, "bool") || !strcmp(hint, "boolean")); break;
case KindOfInt64:
compat = (!strcmp(hint, "int") || !strcmp(hint, "integer")); break;
case KindOfDouble:
compat = (!strcmp(hint, "float") || !strcmp(hint, "double")); break;
case KindOfString: /* fall through */
case KindOfStaticString:
compat = !strcmp(hint, "string"); break;
case KindOfArray:
compat = !strcmp(hint, "array"); break;
case KindOfUninit: /* fall through */
case KindOfNull: compat = true; break;
/* KindOfClass is an hhvm internal type, can not occur here */
case KindOfObject: /* fall through */
case KindOfRef: assert(false /* likely parser bug */);
default: compat = false; break;
}
} else {
msg = "Default value for parameter %s with a class type hint "
"can only be NULL";
switch(defaultType) {
case KindOfNull:
compat = true; break;
case KindOfArray:
compat = strcmp(getTypeHint().c_str(), "array") == 0; break;
default:
compat = false;
if (strcmp(getTypeHint().c_str(), "array") == 0) {
msg = "Default value for parameter %s with array type hint "
"can only be an array or NULL";
}
break;
}
}
if (!compat) {
string name = getName();
string tdefault = HPHP::tname(defaultType);
parseTimeFatal(Compiler::BadDefaultValueType, msg,
name.c_str(), tdefault.c_str(),
getOriginalTypeHint().c_str());
}
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ParameterExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
if (!m_type.empty()) cg_printf("%s ", m_originalType->simpleName().c_str());
if (m_ref) cg_printf("&");
cg_printf("$%s", m_name.c_str());
if (m_defaultValue) {
cg_printf(" = ");
m_defaultValue->outputPHP(cg, ar);
}
}