afbde6181a
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
334 linhas
12 KiB
C++
334 linhas
12 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/assignment_expression.h"
|
|
#include "hphp/compiler/expression/array_element_expression.h"
|
|
#include "hphp/compiler/expression/object_property_expression.h"
|
|
#include "hphp/compiler/analysis/code_error.h"
|
|
#include "hphp/compiler/expression/constant_expression.h"
|
|
#include "hphp/compiler/expression/simple_variable.h"
|
|
#include "hphp/compiler/analysis/block_scope.h"
|
|
#include "hphp/compiler/analysis/variable_table.h"
|
|
#include "hphp/compiler/analysis/constant_table.h"
|
|
#include "hphp/compiler/analysis/file_scope.h"
|
|
#include "hphp/compiler/expression/unary_op_expression.h"
|
|
#include "hphp/parser/hphp.tab.hpp"
|
|
#include "hphp/compiler/code_model_enums.h"
|
|
#include "hphp/compiler/option.h"
|
|
#include "hphp/compiler/analysis/class_scope.h"
|
|
#include "hphp/compiler/analysis/function_scope.h"
|
|
#include "hphp/compiler/expression/scalar_expression.h"
|
|
#include "hphp/compiler/expression/expression_list.h"
|
|
#include "hphp/compiler/expression/simple_function_call.h"
|
|
#include "hphp/runtime/base/complex-types.h"
|
|
#include "hphp/runtime/base/builtin-functions.h"
|
|
|
|
using namespace HPHP;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// constructors/destructors
|
|
|
|
AssignmentExpression::AssignmentExpression
|
|
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
|
|
ExpressionPtr variable, ExpressionPtr value, bool ref,
|
|
bool rhsFirst /* = false */)
|
|
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(AssignmentExpression)),
|
|
m_variable(variable), m_value(value), m_ref(ref), m_rhsFirst(rhsFirst) {
|
|
assert(!m_ref || !m_rhsFirst);
|
|
m_variable->setContext(Expression::DeepAssignmentLHS);
|
|
m_variable->setContext(Expression::AssignmentLHS);
|
|
m_variable->setContext(Expression::LValue);
|
|
m_variable->setContext(Expression::NoLValueWrapper);
|
|
m_value->setContext(Expression::AssignmentRHS);
|
|
if (ref) {
|
|
m_variable->setContext(Expression::RefAssignmentLHS);
|
|
m_value->setContext(Expression::RefValue);
|
|
|
|
// we have &new special case that's handled in this class
|
|
m_value->setContext(Expression::NoRefWrapper);
|
|
}
|
|
}
|
|
|
|
ExpressionPtr AssignmentExpression::clone() {
|
|
AssignmentExpressionPtr exp(new AssignmentExpression(*this));
|
|
Expression::deepCopy(exp);
|
|
exp->m_variable = Clone(m_variable);
|
|
exp->m_value = Clone(m_value);
|
|
return exp;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// parser functions
|
|
|
|
void AssignmentExpression::onParseRecur(AnalysisResultConstPtr ar,
|
|
ClassScopePtr scope) {
|
|
// This is that much we can do during parse phase.
|
|
TypePtr type;
|
|
if (m_value->is(Expression::KindOfScalarExpression)) {
|
|
type = static_pointer_cast<ScalarExpression>(m_value)->inferenceImpl(
|
|
ar, Type::Some, false);
|
|
} else if (m_value->is(Expression::KindOfUnaryOpExpression)) {
|
|
UnaryOpExpressionPtr uexp =
|
|
dynamic_pointer_cast<UnaryOpExpression>(m_value);
|
|
if (uexp->getOp() == T_ARRAY) {
|
|
type = Type::Array;
|
|
}
|
|
}
|
|
if (!type) type = Type::Some;
|
|
|
|
if (m_variable->is(Expression::KindOfConstantExpression)) {
|
|
// ...as in ClassConstant statement
|
|
// We are handling this one here, not in ClassConstant, purely because
|
|
// we need "value" to store in constant table.
|
|
if (type->is(Type::KindOfArray)) {
|
|
parseTimeFatal(Compiler::NoError,
|
|
"Arrays are not allowed in class constants");
|
|
}
|
|
ConstantExpressionPtr exp =
|
|
dynamic_pointer_cast<ConstantExpression>(m_variable);
|
|
scope->getConstants()->add(exp->getName(), type, m_value, ar, m_variable);
|
|
} else if (m_variable->is(Expression::KindOfSimpleVariable)) {
|
|
SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable);
|
|
scope->getVariables()->add(var->getName(), type, true, ar,
|
|
shared_from_this(), scope->getModifiers());
|
|
var->clearContext(Declaration); // to avoid wrong CodeError
|
|
} else {
|
|
assert(false); // parse phase shouldn't handle anything else
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// static analysis functions
|
|
|
|
int AssignmentExpression::getLocalEffects() const {
|
|
return AssignEffect;
|
|
}
|
|
|
|
void AssignmentExpression::analyzeProgram(AnalysisResultPtr ar) {
|
|
m_variable->analyzeProgram(ar);
|
|
m_value->analyzeProgram(ar);
|
|
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
|
|
if (m_ref && m_variable->is(Expression::KindOfSimpleVariable)) {
|
|
SimpleVariablePtr var =
|
|
dynamic_pointer_cast<SimpleVariable>(m_variable);
|
|
const std::string &name = var->getName();
|
|
VariableTablePtr variables = getScope()->getVariables();
|
|
variables->addUsed(name);
|
|
}
|
|
} else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
|
|
if (m_variable->is(Expression::KindOfConstantExpression)) {
|
|
ConstantExpressionPtr exp =
|
|
dynamic_pointer_cast<ConstantExpression>(m_variable);
|
|
if (!m_value->isScalar()) {
|
|
getScope()->getConstants()->setDynamic(ar, exp->getName(), false);
|
|
}
|
|
} else {
|
|
CheckNeeded(m_variable, m_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
ConstructPtr AssignmentExpression::getNthKid(int n) const {
|
|
switch (m_rhsFirst ? 1 - n : n) {
|
|
case 0:
|
|
return m_variable;
|
|
case 1:
|
|
return m_value;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
return ConstructPtr();
|
|
}
|
|
|
|
int AssignmentExpression::getKidCount() const {
|
|
return 2;
|
|
}
|
|
|
|
void AssignmentExpression::setNthKid(int n, ConstructPtr cp) {
|
|
switch (m_rhsFirst ? 1 - n : n) {
|
|
case 0:
|
|
m_variable = dynamic_pointer_cast<Expression>(cp);
|
|
break;
|
|
case 1:
|
|
m_value = dynamic_pointer_cast<Expression>(cp);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool AssignmentExpression::isSimpleGlobalAssign(StringData **name,
|
|
TypedValue *tv) const {
|
|
if (!m_variable->is(KindOfArrayElementExpression)) return false;
|
|
ArrayElementExpressionPtr ae(
|
|
static_pointer_cast<ArrayElementExpression>(m_variable));
|
|
if (!ae->isSuperGlobal() || ae->isDynamicGlobal()) return false;
|
|
Variant v;
|
|
if (!m_value->getScalarValue(v) || v.is(KindOfArray)) return false;
|
|
if (name) {
|
|
*name = makeStaticString(ae->getGlobalName());
|
|
}
|
|
if (tv) {
|
|
if (v.isString()) {
|
|
v = makeStaticString(v.toCStrRef().get());
|
|
}
|
|
*tv = *v.asTypedValue();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ExpressionPtr AssignmentExpression::optimize(AnalysisResultConstPtr ar) {
|
|
if (m_variable->is(Expression::KindOfSimpleVariable)) {
|
|
SimpleVariablePtr var =
|
|
dynamic_pointer_cast<SimpleVariable>(m_variable);
|
|
if (var->checkUnused() &&
|
|
!CheckNeeded(var, m_value)) {
|
|
if (m_value->getContainedEffects() != getContainedEffects()) {
|
|
recomputeEffects();
|
|
}
|
|
return replaceValue(m_value);
|
|
}
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
ExpressionPtr AssignmentExpression::preOptimize(AnalysisResultConstPtr ar) {
|
|
if (Option::EliminateDeadCode &&
|
|
ar->getPhase() >= AnalysisResult::FirstPreOptimize) {
|
|
// otherwise used & needed flags may not be up to date yet
|
|
ExpressionPtr rep = optimize(ar);
|
|
if (rep) return rep;
|
|
}
|
|
if (m_variable->getContainedEffects() & ~(CreateEffect|AccessorEffect)) {
|
|
return ExpressionPtr();
|
|
}
|
|
ExpressionPtr val = m_value;
|
|
while (val) {
|
|
if (val->is(KindOfExpressionList)) {
|
|
ExpressionListPtr el(static_pointer_cast<ExpressionList>(val));
|
|
val = el->listValue();
|
|
continue;
|
|
}
|
|
if (val->is(KindOfAssignmentExpression)) {
|
|
val = static_pointer_cast<AssignmentExpression>(val)->m_value;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (val && val->isScalar()) {
|
|
if (val != m_value) {
|
|
ExpressionListPtr rep(new ExpressionList(
|
|
getScope(), getLocation(),
|
|
ExpressionList::ListKindWrapped));
|
|
rep->addElement(m_value);
|
|
m_value = val->clone();
|
|
rep->addElement(static_pointer_cast<Expression>(shared_from_this()));
|
|
return replaceValue(rep);
|
|
}
|
|
if (!m_ref && m_variable->is(KindOfArrayElementExpression)) {
|
|
ArrayElementExpressionPtr ae(
|
|
static_pointer_cast<ArrayElementExpression>(m_variable));
|
|
ExpressionPtr avar(ae->getVariable());
|
|
ExpressionPtr aoff(ae->getOffset());
|
|
if (!aoff || aoff->isScalar()) {
|
|
avar = avar->getCanonLVal();
|
|
while (avar) {
|
|
if (avar->isScalar()) {
|
|
Variant v,o,r;
|
|
if (!avar->getScalarValue(v)) break;
|
|
if (!val->getScalarValue(r)) break;
|
|
try {
|
|
g_context->setThrowAllErrors(true);
|
|
if (aoff) {
|
|
if (!aoff->getScalarValue(o)) break;
|
|
if (v.isString()) {
|
|
if (!o.isInteger() ||
|
|
o.toInt64Val() < 0 ||
|
|
o.toInt64Val() >= v.toCStrRef().size()) {
|
|
// warnings should be raised...
|
|
break;
|
|
}
|
|
}
|
|
v.set(o, r);
|
|
} else {
|
|
v.append(r);
|
|
}
|
|
g_context->setThrowAllErrors(false);
|
|
} catch (...) {
|
|
break;
|
|
}
|
|
ExpressionPtr rep(
|
|
new AssignmentExpression(
|
|
getScope(), getLocation(),
|
|
m_variable->replaceValue(Clone(ae->getVariable())),
|
|
makeScalarExpression(ar, v), false));
|
|
if (!isUnused()) {
|
|
ExpressionListPtr el(
|
|
new ExpressionList(
|
|
getScope(), getLocation(),
|
|
ExpressionList::ListKindWrapped));
|
|
el->addElement(rep);
|
|
el->addElement(val);
|
|
rep = el;
|
|
}
|
|
return replaceValue(rep);
|
|
}
|
|
avar = avar->getCanonPtr();
|
|
}
|
|
g_context->setThrowAllErrors(false);
|
|
}
|
|
}
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
ExpressionPtr AssignmentExpression::postOptimize(AnalysisResultConstPtr ar) {
|
|
return optimize(ar);
|
|
}
|
|
|
|
TypePtr AssignmentExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
|
|
bool coerce) {
|
|
|
|
return inferAssignmentTypes(ar, type, coerce, m_variable, m_value);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void AssignmentExpression::outputCodeModel(CodeGenerator &cg) {
|
|
cg.printObjectHeader("BinaryOpExpression", 4);
|
|
cg.printPropertyHeader("expression1");
|
|
m_variable->outputCodeModel(cg);
|
|
cg.printPropertyHeader("expression2");
|
|
cg.printExpression(m_value, m_ref);
|
|
cg.printPropertyHeader("operation");
|
|
cg.printValue(PHP_ASSIGNMENT);
|
|
cg.printPropertyHeader("location");
|
|
cg.printLocation(this->getLocation());
|
|
cg.printObjectFooter();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// code generation functions
|
|
|
|
void AssignmentExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
m_variable->outputPHP(cg, ar);
|
|
cg_printf(" = ");
|
|
if (m_ref) cg_printf("&");
|
|
m_value->outputPHP(cg, ar);
|
|
}
|