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
298 linhas
9.2 KiB
C++
298 linhas
9.2 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/include_expression.h"
|
|
#include "hphp/parser/hphp.tab.hpp"
|
|
#include "hphp/compiler/analysis/code_error.h"
|
|
#include "hphp/compiler/analysis/file_scope.h"
|
|
#include "hphp/compiler/analysis/function_scope.h"
|
|
#include "hphp/compiler/statement/statement_list.h"
|
|
#include "hphp/compiler/option.h"
|
|
#include "hphp/compiler/expression/expression_list.h"
|
|
#include "hphp/compiler/expression/binary_op_expression.h"
|
|
#include "hphp/compiler/analysis/class_scope.h"
|
|
#include "hphp/compiler/parser/parser.h"
|
|
#include "hphp/compiler/analysis/variable_table.h"
|
|
#include "hphp/compiler/expression/scalar_expression.h"
|
|
#include "hphp/util/util.h"
|
|
|
|
using namespace HPHP;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// constructors/destructors
|
|
|
|
IncludeExpression::IncludeExpression
|
|
(EXPRESSION_CONSTRUCTOR_PARAMETERS, ExpressionPtr exp, int op)
|
|
: UnaryOpExpression(
|
|
EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(IncludeExpression),
|
|
exp, op, true),
|
|
m_documentRoot(false), m_depsSet(false) {
|
|
}
|
|
|
|
ExpressionPtr IncludeExpression::clone() {
|
|
IncludeExpressionPtr exp(new IncludeExpression(*this));
|
|
Expression::deepCopy(exp);
|
|
exp->m_exp = Clone(m_exp);
|
|
exp->m_depsSet = false;
|
|
return exp;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// parser functions
|
|
|
|
static string get_include_file_path(const string &source,
|
|
const string &var, const string &lit,
|
|
bool documentRoot) {
|
|
if (var.empty()) {
|
|
// absolute path
|
|
if (!lit.empty() && lit[0] == '/') {
|
|
return lit;
|
|
}
|
|
|
|
// relative path to document root
|
|
if (documentRoot) {
|
|
return lit;
|
|
}
|
|
|
|
struct stat sb;
|
|
// relative path to containing file's directory
|
|
if (source.empty() && (stat(lit.c_str(), &sb) == 0)) {
|
|
return lit;
|
|
}
|
|
|
|
size_t pos = source.rfind('/');
|
|
string resolved;
|
|
if (pos != string::npos) {
|
|
resolved = source.substr(0, pos + 1) + lit;
|
|
if (stat(resolved.c_str(), &sb) == 0) {
|
|
return resolved;
|
|
}
|
|
}
|
|
|
|
// if file cannot be found, resolve it using search paths
|
|
for (unsigned int i = 0; i < Option::IncludeSearchPaths.size(); i++) {
|
|
string filename = Option::IncludeSearchPaths[i] + "/" + lit;
|
|
struct stat sb;
|
|
if (stat(filename.c_str(), &sb) == 0) {
|
|
return filename;
|
|
}
|
|
}
|
|
|
|
// try still use relative path to containing file's directory
|
|
if (!resolved.empty()) {
|
|
return resolved;
|
|
}
|
|
|
|
return lit;
|
|
}
|
|
|
|
// [IncludeRoot] . 'string'
|
|
std::map<string, string>::const_iterator iter =
|
|
Option::IncludeRoots.find(var);
|
|
|
|
if (iter != Option::IncludeRoots.end()) {
|
|
string includeRoot = iter->second;
|
|
if (!includeRoot.empty()) {
|
|
if (includeRoot[0] == '/') includeRoot = includeRoot.substr(1);
|
|
if (includeRoot.empty() ||
|
|
includeRoot[includeRoot.size()-1] != '/') {
|
|
includeRoot += "/";
|
|
}
|
|
}
|
|
if (!lit.empty() && lit[0] == '/') {
|
|
includeRoot += lit.substr(1);
|
|
} else {
|
|
includeRoot += lit;
|
|
}
|
|
return includeRoot;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
static void parse_string_arg(ExpressionPtr exp, string &var, string &lit) {
|
|
if (exp->is(Expression::KindOfUnaryOpExpression)) {
|
|
UnaryOpExpressionPtr u(static_pointer_cast<UnaryOpExpression>(exp));
|
|
if (u->getOp() == '(') {
|
|
parse_string_arg(u->getExpression(), var, lit);
|
|
return;
|
|
}
|
|
} else if (exp->is(Expression::KindOfBinaryOpExpression)) {
|
|
BinaryOpExpressionPtr b(static_pointer_cast<BinaryOpExpression>(exp));
|
|
if (b->getOp() == '.') {
|
|
string v, l;
|
|
parse_string_arg(b->getExp2(), v, l);
|
|
if (v.empty()) {
|
|
parse_string_arg(b->getExp1(), var, lit);
|
|
lit += l;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (exp->isLiteralString()) {
|
|
var = "";
|
|
lit = exp->getLiteralString();
|
|
return;
|
|
}
|
|
var = exp->getText();
|
|
lit = "";
|
|
return;
|
|
}
|
|
|
|
string IncludeExpression::CheckInclude(ConstructPtr includeExp,
|
|
ExpressionPtr fileExp,
|
|
bool &documentRoot) {
|
|
string container = includeExp->getLocation()->file;
|
|
string var, lit;
|
|
parse_string_arg(fileExp, var, lit);
|
|
if (lit.empty()) return lit;
|
|
|
|
if (var == "__DIR__") {
|
|
var = "";
|
|
// get_include_file_path will check relative to the current file's dir
|
|
// as long as the first char isn't a /
|
|
if (lit[0] == '/') {
|
|
lit = lit.substr(1);
|
|
}
|
|
}
|
|
|
|
string included = get_include_file_path(container, var, lit, documentRoot);
|
|
if (!included.empty()) {
|
|
if (included == container) {
|
|
Compiler::Error(Compiler::BadPHPIncludeFile, includeExp);
|
|
}
|
|
included = Util::canonicalize(included);
|
|
if (!var.empty()) documentRoot = true;
|
|
}
|
|
return included;
|
|
}
|
|
|
|
void IncludeExpression::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) {
|
|
/* m_documentRoot is a bitfield */
|
|
bool dr = m_documentRoot;
|
|
m_include = CheckInclude(shared_from_this(), m_exp, dr);
|
|
m_documentRoot = dr;
|
|
if (!m_include.empty()) ar->parseOnDemand(m_include);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// static analysis functions
|
|
|
|
FileScopeRawPtr IncludeExpression::getIncludedFile(
|
|
AnalysisResultConstPtr ar) const {
|
|
if (m_include.empty()) return FileScopeRawPtr();
|
|
return ar->findFileScope(m_include);
|
|
}
|
|
|
|
std::string IncludeExpression::includePath() const {
|
|
return m_include;
|
|
}
|
|
|
|
bool IncludeExpression::isReqLit() const {
|
|
return !m_include.empty() &&
|
|
m_op == T_REQUIRE_ONCE && isDocumentRoot();
|
|
}
|
|
|
|
bool IncludeExpression::analyzeInclude(AnalysisResultConstPtr ar,
|
|
const std::string &include) {
|
|
ConstructPtr self = shared_from_this();
|
|
FileScopePtr file = ar->findFileScope(include);
|
|
if (!file) {
|
|
Compiler::Error(Compiler::PHPIncludeFileNotFound, self);
|
|
return false;
|
|
}
|
|
|
|
FunctionScopePtr func = getFunctionScope();
|
|
if (func && file->getPseudoMain()) {
|
|
file->getPseudoMain()->addUse(func, BlockScope::UseKindInclude);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void IncludeExpression::analyzeProgram(AnalysisResultPtr ar) {
|
|
if (!m_include.empty()) {
|
|
if (ar->getPhase() == AnalysisResult::AnalyzeAll ||
|
|
ar->getPhase() == AnalysisResult::AnalyzeFinal) {
|
|
if (analyzeInclude(ar, m_include)) {
|
|
FunctionScopePtr func = getFunctionScope();
|
|
getFileScope()->addIncludeDependency(ar, m_include,
|
|
func && func->isInlined());
|
|
}
|
|
}
|
|
}
|
|
|
|
VariableTablePtr var = getScope()->getVariables();
|
|
var->setAttribute(VariableTable::ContainsLDynamicVariable);
|
|
var->forceVariants(ar, VariableTable::AnyVars);
|
|
|
|
UnaryOpExpression::analyzeProgram(ar);
|
|
}
|
|
|
|
ExpressionPtr IncludeExpression::preOptimize(AnalysisResultConstPtr ar) {
|
|
if (ar->getPhase() >= AnalysisResult::FirstPreOptimize) {
|
|
if (m_include.empty()) {
|
|
bool dr = m_documentRoot;
|
|
m_include = CheckInclude(shared_from_this(), m_exp, dr);
|
|
m_documentRoot = dr;
|
|
m_depsSet = false;
|
|
}
|
|
if (!m_depsSet && !m_include.empty()) {
|
|
analyzeInclude(ar, m_include);
|
|
m_depsSet = true;
|
|
}
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
ExpressionPtr IncludeExpression::postOptimize(AnalysisResultConstPtr ar) {
|
|
if (!m_include.empty()) {
|
|
if (!m_depsSet) {
|
|
analyzeInclude(ar, m_include);
|
|
m_depsSet = true;
|
|
}
|
|
FileScopePtr fs = ar->findFileScope(m_include);
|
|
if (fs && fs->getPseudoMain()) {
|
|
if (!Option::KeepStatementsWithNoEffect) {
|
|
if (ExpressionPtr rep = fs->getEffectiveImpl(ar)) {
|
|
recomputeEffects();
|
|
return replaceValue(rep->clone());
|
|
}
|
|
}
|
|
} else {
|
|
m_include = "";
|
|
}
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
TypePtr IncludeExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
|
|
bool coerce) {
|
|
return UnaryOpExpression::inferTypes(ar, type, coerce);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void IncludeExpression::outputCodeModel(CodeGenerator &cg) {
|
|
UnaryOpExpression::outputCodeModel(cg);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// code generation functions
|
|
|
|
void IncludeExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
UnaryOpExpression::outputPHP(cg, ar);
|
|
}
|