Arquivos
hhvm/hphp/compiler/expression/simple_variable.cpp
T
Herman Venter afbde6181a Provide a way to serialize the compiler's AST in the form of a PHP Code Model.
The AST classes now have an additional visitor that can serialize the AST in the format expected by the unserialize function. The concrete classes to be produced by the unserialize function can be controlled by passing in a prefix argument to the visitor.

Facebook only:

Also added is an extension function fb_serialize_code_model_for(codeobject, prefix) that takes a string as its first argument, prefixes it with "<?php " and then parses it as if it were an eval string and then returns the serialized AST.

Reviewed By: @paroski

Differential Revision: D1027004
2013-11-26 21:14:17 -08:00

327 linhas
11 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 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 "hphp/compiler/expression/simple_variable.h"
#include "hphp/compiler/analysis/function_scope.h"
#include "hphp/compiler/analysis/variable_table.h"
#include "hphp/compiler/analysis/class_scope.h"
#include "hphp/compiler/option.h"
#include "hphp/compiler/builtin_symbols.h"
#include "hphp/compiler/expression/scalar_expression.h"
#include "hphp/parser/hphp.tab.hpp"
#include "hphp/parser/parser.h"
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
SimpleVariable::SimpleVariable
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
const std::string &name,
const std::string &docComment /* = "" */)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(SimpleVariable)),
m_name(name), m_docComment(docComment),
m_sym(nullptr), m_originalSym(nullptr),
m_this(false), m_globals(false),
m_superGlobal(false), m_alwaysStash(false) {
setContext(Expression::NoLValueWrapper);
}
ExpressionPtr SimpleVariable::clone() {
SimpleVariablePtr exp(new SimpleVariable(*this));
Expression::deepCopy(exp);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void SimpleVariable::setContext(Context context) {
m_context |= context;
if (m_this) {
bool ref = context & (RefValue | RefAssignmentLHS);
bool unset = ((context & Expression::UnsetContext) &&
(context & Expression::LValue));
if (ref || unset) {
if (FunctionScopePtr func = getFunctionScope()) {
func->setContainsBareThis(true, true);
}
}
}
}
int SimpleVariable::getLocalEffects() const {
if (m_context == Declaration &&
m_sym && m_sym->isShrinkWrapped()) {
return LocalEffect;
}
return NoEffect;
}
void SimpleVariable::updateSymbol(SimpleVariablePtr src) {
m_sym = getScope()->getVariables()->addSymbol(m_name);
if (src && src->m_sym) {
m_sym->update(src->m_sym);
}
}
bool SimpleVariable::couldBeAliased() const {
if (m_globals || m_superGlobal) return true;
always_assert(m_sym);
if (m_sym->isGlobal() || m_sym->isStatic()) return true;
if (getScope()->inPseudoMain() && !m_sym->isHidden()) return true;
if (isReferencedValid()) return isReferenced();
return m_sym->isReferenced();
}
bool SimpleVariable::isHidden() const {
return m_sym && m_sym->isHidden();
}
void SimpleVariable::coalesce(SimpleVariablePtr other) {
always_assert(m_sym);
always_assert(other->m_sym);
if (!m_originalSym) m_originalSym = m_sym;
m_sym->clearUsed();
m_sym->clearNeeded();
m_sym = other->m_sym;
m_name = m_sym->getName();
}
/*
This simple variable is about to go out of scope.
Is it ok to kill the last assignment?
What if its a reference assignment (or an unset)?
*/
bool SimpleVariable::canKill(bool isref) const {
if (m_globals || m_superGlobal) return false;
always_assert(m_sym);
if (m_sym->isGlobal() || m_sym->isStatic()) {
return isref && !getScope()->inPseudoMain();
}
return
(isref && (m_sym->isHidden() || !getScope()->inPseudoMain())) ||
(isReferencedValid() ? !isReferenced() : !m_sym->isReferenced());
}
void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) {
m_superGlobal = BuiltinSymbols::IsSuperGlobal(m_name);
m_superGlobalType = BuiltinSymbols::GetSuperGlobalType(m_name);
VariableTablePtr variables = getScope()->getVariables();
if (m_superGlobal) {
variables->setAttribute(VariableTable::NeedGlobalPointer);
} else if (m_name == "GLOBALS") {
m_globals = true;
} else {
m_sym = variables->addDeclaredSymbol(m_name, shared_from_this());
}
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
if (FunctionScopePtr func = getFunctionScope()) {
if (m_name == "this" && func->mayContainThis()) {
func->setContainsThis();
m_this = true;
if (!hasContext(ObjectContext)) {
bool unset = hasAllContext(UnsetContext | LValue);
func->setContainsBareThis(
true,
hasAnyContext(RefValue | RefAssignmentLHS) ||
m_sym->isRefClosureVar() || unset);
if (variables->getAttribute(VariableTable::ContainsDynamicVariable)) {
ClassScopePtr cls = getClassScope();
TypePtr t = !cls || cls->isRedeclaring() ?
Type::Variant : Type::CreateObjectType(cls->getName());
variables->add(m_sym, t, true, ar, shared_from_this(),
getScope()->getModifiers());
}
}
}
if (m_sym && !(m_context & AssignmentLHS) &&
!((m_context & UnsetContext) && (m_context & LValue))) {
m_sym->setUsed();
}
}
} else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_sym && !m_this) {
if (!m_sym->isSystem() &&
!(getContext() &
(LValue|RefValue|RefParameter|UnsetContext|ExistContext)) &&
m_sym->getDeclaration().get() == this) {
assert(!m_sym->isParameter());
if (!variables->getAttribute(VariableTable::ContainsLDynamicVariable) &&
!getScope()->is(BlockScope::ClassScope)) {
if (getScope()->inPseudoMain()) {
Compiler::Error(Compiler::UseUndeclaredGlobalVariable,
shared_from_this());
} else if (!m_sym->isClosureVar()) {
Compiler::Error(Compiler::UseUndeclaredVariable,
shared_from_this());
}
}
}
// check function parameter that can occur in lval context
if (m_sym->isParameter() &&
m_context & (LValue | RefValue | DeepReference |
UnsetContext | InvokeArgument | OprLValue |
DeepOprLValue)) {
m_sym->setLvalParam();
}
}
}
}
bool SimpleVariable::canonCompare(ExpressionPtr e) const {
return Expression::canonCompare(e) &&
getName() == static_cast<SimpleVariable*>(e.get())->getName();
}
TypePtr SimpleVariable::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
assert(false);
return TypePtr();
}
bool SimpleVariable::checkUnused() const {
return !m_superGlobal && !m_globals &&
getScope()->getVariables()->checkUnused(m_sym);
}
static inline TypePtr GetAssertedInType(AnalysisResultPtr ar,
TypePtr assertedType,
TypePtr ret) {
assert(assertedType);
if (!ret) return assertedType;
TypePtr res = Type::Inferred(ar, ret, assertedType);
// if the asserted type and the symbol table type are compatible, then use
// the result of Inferred() (which is at least as strict as assertedType).
// otherwise, go with the asserted type
return res ? res : assertedType;
}
TypePtr SimpleVariable::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());
resetTypes();
TypePtr ret;
ConstructPtr construct = shared_from_this();
BlockScopePtr scope = getScope();
VariableTablePtr variables = scope->getVariables();
// check function parameter that can occur in lval context
if (m_sym && m_sym->isParameter() &&
m_context & (LValue | RefValue | DeepReference |
UnsetContext | InvokeArgument | OprLValue | DeepOprLValue)) {
m_sym->setLvalParam();
}
if (coerce && m_sym && type && type->is(Type::KindOfAutoSequence)) {
TypePtr t = m_sym->getType();
if (!t || t->is(Type::KindOfVoid) ||
t->is(Type::KindOfSome) || t->is(Type::KindOfArray)) {
type = Type::Array;
}
}
if (m_this) {
ret = Type::Object;
ClassScopePtr cls = getOriginalClass();
if (cls && (hasContext(ObjectContext) || !cls->derivedByDynamic())) {
ret = Type::CreateObjectType(cls->getName());
}
if (!hasContext(ObjectContext) &&
variables->getAttribute(VariableTable::ContainsDynamicVariable)) {
if (variables->getAttribute(VariableTable::ContainsLDynamicVariable)) {
ret = Type::Variant;
}
ret = variables->add(m_sym, ret, true, ar,
construct, scope->getModifiers());
}
} else if ((m_context & (LValue|Declaration)) &&
!(m_context & (ObjectContext|RefValue))) {
if (m_globals) {
ret = Type::Array;
} else if (m_superGlobal) {
ret = m_superGlobalType;
} else if (m_superGlobalType) { // For system
ret = variables->add(m_sym, m_superGlobalType,
((m_context & Declaration) != Declaration), ar,
construct, scope->getModifiers());
} else {
ret = variables->add(m_sym, type,
((m_context & Declaration) != Declaration), ar,
construct, scope->getModifiers());
}
} else {
if (m_superGlobalType) {
ret = m_superGlobalType;
} else if (m_globals) {
ret = Type::Array;
} else if (scope->is(BlockScope::ClassScope)) {
assert(getClassScope().get() == scope.get());
// ClassVariable expression will come to this block of code
ret = getClassScope()->checkProperty(getScope(), m_sym, type, true, ar);
} else {
TypePtr tmpType = type;
if (m_context & RefValue) {
tmpType = Type::Variant;
coerce = true;
}
ret = variables->checkVariable(m_sym, tmpType, coerce, ar, construct);
if (ret && (ret->is(Type::KindOfSome) || ret->is(Type::KindOfAny))) {
ret = Type::Variant;
}
}
}
// if m_assertedType is set, then this is a type assertion node
TypePtr inType = m_assertedType ?
GetAssertedInType(ar, m_assertedType, ret) : ret;
TypePtr actual = propagateTypes(ar, inType);
setTypes(ar, actual, type);
if (Type::SameType(actual, ret)) {
m_implementedType.reset();
} else {
m_implementedType = ret;
}
return actual;
}
///////////////////////////////////////////////////////////////////////////////
void SimpleVariable::outputCodeModel(CodeGenerator &cg) {
cg.printObjectHeader("SimpleVariableExpression", 2);
cg.printPropertyHeader("variableName");
cg.printValue(m_name);
cg.printPropertyHeader("location");
cg.printLocation(this->getLocation());
cg.printObjectFooter();
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void SimpleVariable::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
cg_printf("$%s", m_name.c_str());
}