9b6bf426b9
Also rename array_inline.h to array_data-inl.h
490 linhas
16 KiB
C++
490 linhas
16 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/builtin_symbols.h"
|
|
#include "hphp/compiler/analysis/analysis_result.h"
|
|
#include "hphp/compiler/statement/statement_list.h"
|
|
#include "hphp/compiler/analysis/type.h"
|
|
#include "hphp/compiler/analysis/function_scope.h"
|
|
#include "hphp/compiler/analysis/class_scope.h"
|
|
#include "hphp/compiler/expression/modifier_expression.h"
|
|
#include "hphp/compiler/expression/simple_function_call.h"
|
|
#include "hphp/compiler/option.h"
|
|
#include "hphp/compiler/parser/parser.h"
|
|
#include "hphp/compiler/analysis/file_scope.h"
|
|
#include "hphp/compiler/analysis/variable_table.h"
|
|
#include "hphp/compiler/analysis/constant_table.h"
|
|
#include "hphp/util/parser/hphp.tab.hpp"
|
|
#include "hphp/runtime/base/class_info.h"
|
|
#include "hphp/runtime/base/program_functions.h"
|
|
#include "hphp/runtime/base/array_iterator.h"
|
|
#include "hphp/runtime/base/execution_context.h"
|
|
#include "hphp/runtime/base/thread_init_fini.h"
|
|
#include "hphp/util/logger.h"
|
|
#include "hphp/util/util.h"
|
|
#include <dlfcn.h>
|
|
|
|
using namespace HPHP;
|
|
|
|
#define BF_COLUMN_COUNT 3
|
|
#define BF_COLUMN_NAME 0
|
|
#define BF_COLUMN_RETURN 1
|
|
#define BF_COLUMN_PARAMS 2
|
|
|
|
#define CLASS_TYPE 999
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool BuiltinSymbols::Loaded = false;
|
|
StringBag BuiltinSymbols::s_strings;
|
|
|
|
StringToFunctionScopePtrMap BuiltinSymbols::s_functions;
|
|
|
|
const char *const BuiltinSymbols::GlobalNames[] = {
|
|
"HTTP_RAW_POST_DATA",
|
|
"_COOKIE",
|
|
"_ENV",
|
|
"_FILES",
|
|
"_GET",
|
|
"_POST",
|
|
"_REQUEST",
|
|
"_SERVER",
|
|
"_SESSION",
|
|
"argc",
|
|
"argv",
|
|
"http_response_header",
|
|
};
|
|
|
|
const char *BuiltinSymbols::SystemClasses[] = {
|
|
"stdclass",
|
|
"exception",
|
|
"arrayaccess",
|
|
"iterator",
|
|
"collections",
|
|
"reflection",
|
|
"splobjectstorage",
|
|
"directory",
|
|
"splfile",
|
|
"debugger",
|
|
"xhprof",
|
|
"directoryiterator",
|
|
"soapfault",
|
|
"fbmysqllexer",
|
|
nullptr
|
|
};
|
|
|
|
StringToClassScopePtrMap BuiltinSymbols::s_classes;
|
|
VariableTablePtr BuiltinSymbols::s_variables;
|
|
ConstantTablePtr BuiltinSymbols::s_constants;
|
|
StringToTypePtrMap BuiltinSymbols::s_superGlobals;
|
|
AnalysisResultPtr BuiltinSymbols::s_systemAr;
|
|
void *BuiltinSymbols::s_handle_main = nullptr;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
int BuiltinSymbols::NumGlobalNames() {
|
|
return sizeof(BuiltinSymbols::GlobalNames) /
|
|
sizeof(BuiltinSymbols::GlobalNames[0]);
|
|
}
|
|
|
|
static TypePtr typePtrFromDataType(DataType dt, TypePtr unknown) {
|
|
switch (dt) {
|
|
case KindOfNull: return Type::Null;
|
|
case KindOfBoolean: return Type::Boolean;
|
|
case KindOfInt64: return Type::Int64;
|
|
case KindOfDouble: return Type::Double;
|
|
case KindOfString: return Type::String;
|
|
case KindOfArray: return Type::Array;
|
|
case KindOfObject: return Type::Object;
|
|
case KindOfUnknown:
|
|
default:
|
|
return unknown;
|
|
}
|
|
}
|
|
|
|
FunctionScopePtr BuiltinSymbols::ImportFunctionScopePtr(AnalysisResultPtr ar,
|
|
ClassInfo *cls, ClassInfo::MethodInfo *method) {
|
|
int attrs = method->attribute;
|
|
bool isMethod = cls != ClassInfo::GetSystem();
|
|
FunctionScopePtr f(new FunctionScope(isMethod,
|
|
method->name.data(),
|
|
attrs & ClassInfo::IsReference));
|
|
|
|
int reqCount = 0, totalCount = 0;
|
|
for(auto it = method->parameters.begin();
|
|
it != method->parameters.end(); ++it) {
|
|
const ClassInfo::ParameterInfo *pinfo = *it;
|
|
if (!pinfo->value || !pinfo->value[0]) {
|
|
++reqCount;
|
|
}
|
|
++totalCount;
|
|
}
|
|
f->setParamCounts(ar, reqCount, totalCount);
|
|
|
|
int idx = 0;
|
|
for(auto it = method->parameters.begin();
|
|
it != method->parameters.end(); ++it, ++idx) {
|
|
const ClassInfo::ParameterInfo *pinfo = *it;
|
|
f->setParamName(idx, pinfo->name);
|
|
if (pinfo->attribute & ClassInfo::IsReference) {
|
|
f->setRefParam(idx);
|
|
}
|
|
f->setParamType(ar, idx, typePtrFromDataType(pinfo->argType, Type::Any));
|
|
if (pinfo->valueLen) {
|
|
f->setParamDefault(idx, pinfo->value, pinfo->valueLen,
|
|
std::string(pinfo->valueText, pinfo->valueTextLen));
|
|
}
|
|
}
|
|
|
|
if (method->returnType != KindOfNull) {
|
|
f->setReturnType(ar, typePtrFromDataType(method->returnType,
|
|
Type::Variant));
|
|
}
|
|
|
|
f->setClassInfoAttribute(attrs);
|
|
if (attrs & ClassInfo::HasDocComment) {
|
|
f->setDocComment(method->docComment);
|
|
}
|
|
|
|
if (!isMethod && (attrs & ClassInfo::HasOptFunction)) {
|
|
// Legacy optimization functions
|
|
if (method->name.same("fb_call_user_func_safe") ||
|
|
method->name.same("fb_call_user_func_safe_return") ||
|
|
method->name.same("fb_call_user_func_array_safe")) {
|
|
f->setOptFunction(hphp_opt_fb_call_user_func);
|
|
} else if (method->name.same("is_callable")) {
|
|
f->setOptFunction(hphp_opt_is_callable);
|
|
} else if (method->name.same("call_user_func_array")) {
|
|
f->setOptFunction(hphp_opt_call_user_func);
|
|
}
|
|
}
|
|
|
|
if (isMethod) {
|
|
if (attrs & ClassInfo::IsProtected) {
|
|
f->addModifier(T_PROTECTED);
|
|
} else if (attrs & ClassInfo::IsPrivate) {
|
|
f->addModifier(T_PRIVATE);
|
|
}
|
|
if (attrs & ClassInfo::IsStatic) {
|
|
f->addModifier(T_STATIC);
|
|
}
|
|
}
|
|
|
|
// This block of code is not needed, if BlockScope directly takes flags.
|
|
if (attrs & ClassInfo::MixedVariableArguments) {
|
|
f->setVariableArgument(-1);
|
|
} else if (attrs & ClassInfo::RefVariableArguments) {
|
|
f->setVariableArgument(1);
|
|
} else if (attrs & ClassInfo::VariableArguments) {
|
|
f->setVariableArgument(0);
|
|
}
|
|
if (attrs & ClassInfo::NoEffect) {
|
|
f->setNoEffect();
|
|
}
|
|
if (attrs & ClassInfo::FunctionIsFoldable) {
|
|
f->setIsFoldable();
|
|
}
|
|
if (attrs & ClassInfo::ContextSensitive) {
|
|
f->setContextSensitive(true);
|
|
}
|
|
if (attrs & ClassInfo::NeedsActRec) {
|
|
f->setNeedsActRec();
|
|
}
|
|
if ((attrs & ClassInfo::AllowOverride) && !isMethod) {
|
|
f->setAllowOverride();
|
|
}
|
|
|
|
FunctionScope::RecordFunctionInfo(f->getName(), f);
|
|
return f;
|
|
}
|
|
|
|
void BuiltinSymbols::ImportExtFunctions(AnalysisResultPtr ar,
|
|
StringToFunctionScopePtrMap &map,
|
|
ClassInfo *cls) {
|
|
const ClassInfo::MethodVec &methods = cls->getMethodsVec();
|
|
for (auto it = methods.begin(); it != methods.end(); ++it) {
|
|
FunctionScopePtr f = ImportFunctionScopePtr(ar, cls, *it);
|
|
assert(!map[f->getName()]);
|
|
map[f->getName()] = f;
|
|
}
|
|
}
|
|
|
|
void BuiltinSymbols::ImportExtFunctions(AnalysisResultPtr ar,
|
|
FunctionScopePtrVec &vec,
|
|
ClassInfo *cls) {
|
|
const ClassInfo::MethodVec &methods = cls->getMethodsVec();
|
|
for (auto it = methods.begin(); it != methods.end(); ++it) {
|
|
FunctionScopePtr f = ImportFunctionScopePtr(ar, cls, *it);
|
|
vec.push_back(f);
|
|
}
|
|
}
|
|
|
|
void BuiltinSymbols::ImportExtProperties(AnalysisResultPtr ar,
|
|
VariableTablePtr dest,
|
|
ClassInfo *cls) {
|
|
ClassInfo::PropertyVec src = cls->getPropertiesVec();
|
|
for (auto it = src.begin(); it != src.end(); ++it) {
|
|
ClassInfo::PropertyInfo *pinfo = *it;
|
|
int attrs = pinfo->attribute;
|
|
ModifierExpressionPtr modifiers(
|
|
new ModifierExpression(BlockScopePtr(), LocationPtr()));
|
|
if (attrs & ClassInfo::IsPrivate) {
|
|
modifiers->add(T_PRIVATE);
|
|
} else if (attrs & ClassInfo::IsProtected) {
|
|
modifiers->add(T_PROTECTED);
|
|
}
|
|
if (attrs & ClassInfo::IsStatic) {
|
|
modifiers->add(T_STATIC);
|
|
}
|
|
|
|
dest->add(pinfo->name.data(),
|
|
typePtrFromDataType(pinfo->type, Type::Variant),
|
|
false, ar, ExpressionPtr(), modifiers);
|
|
}
|
|
}
|
|
|
|
void BuiltinSymbols::ImportExtConstants(AnalysisResultPtr ar,
|
|
ConstantTablePtr dest,
|
|
ClassInfo *cls) {
|
|
ClassInfo::ConstantVec src = cls->getConstantsVec();
|
|
for (auto it = src.begin(); it != src.end(); ++it) {
|
|
// We make an assumption that if the constant is a callback type
|
|
// (e.g. STDIN, STDOUT, STDERR) then it will return an Object.
|
|
// And that if it's deferred (SID, PHP_SAPI) it'll be a String.
|
|
ClassInfo::ConstantInfo *cinfo = *it;
|
|
dest->add(cinfo->name.data(),
|
|
cinfo->isDeferred() ?
|
|
(cinfo->isCallback() ? Type::Object : Type::String) :
|
|
typePtrFromDataType(cinfo->getValue().getType(), Type::Variant),
|
|
ExpressionPtr(), ar, ConstructPtr());
|
|
}
|
|
}
|
|
|
|
ClassScopePtr BuiltinSymbols::ImportClassScopePtr(AnalysisResultPtr ar,
|
|
ClassInfo *cls) {
|
|
FunctionScopePtrVec methods;
|
|
ImportExtFunctions(ar, methods, cls);
|
|
|
|
ClassInfo::InterfaceVec ifaces = cls->getInterfacesVec();
|
|
String parent = cls->getParentClass();
|
|
std::vector<std::string> stdIfaces;
|
|
if (!parent.empty() && (ifaces.empty() || ifaces[0] != parent)) {
|
|
stdIfaces.push_back(parent.data());
|
|
}
|
|
for (auto it = ifaces.begin(); it != ifaces.end(); ++it) {
|
|
stdIfaces.push_back(it->data());
|
|
}
|
|
|
|
ClassScopePtr cl(new ClassScope(ar, cls->getName().data(), parent.data(),
|
|
stdIfaces, methods));
|
|
for (uint i = 0; i < methods.size(); ++i) {
|
|
methods[i]->setOuterScope(cl);
|
|
}
|
|
|
|
ImportExtProperties(ar, cl->getVariables(), cls);
|
|
ImportExtConstants(ar, cl->getConstants(), cls);
|
|
int attrs = cls->getAttribute();
|
|
cl->setClassInfoAttribute(attrs);
|
|
if (attrs & ClassInfo::HasDocComment) {
|
|
cl->setDocComment(cls->getDocComment());
|
|
}
|
|
cl->setSystem();
|
|
return cl;
|
|
}
|
|
|
|
void BuiltinSymbols::ImportExtClasses(AnalysisResultPtr ar) {
|
|
const ClassInfo::ClassMap &classes = ClassInfo::GetClassesMap();
|
|
for (auto it = classes.begin(); it != classes.end(); ++it) {
|
|
ClassScopePtr cl = ImportClassScopePtr(ar, it->second);
|
|
assert(!s_classes[cl->getName()]);
|
|
s_classes[cl->getName()] = cl;
|
|
}
|
|
}
|
|
|
|
bool BuiltinSymbols::Load(AnalysisResultPtr ar) {
|
|
if (Loaded) return true;
|
|
Loaded = true;
|
|
|
|
if (g_context.isNull()) init_thread_locals();
|
|
ClassInfo::Load();
|
|
|
|
// load extension functions first, so system/php may call them
|
|
ImportExtFunctions(ar, s_functions, ClassInfo::GetSystem());
|
|
AnalysisResultPtr ar2 = AnalysisResultPtr(new AnalysisResult());
|
|
s_variables = VariableTablePtr(new VariableTable(*ar2.get()));
|
|
s_constants = ConstantTablePtr(new ConstantTable(*ar2.get()));
|
|
|
|
// load extension constants, classes and dynamics
|
|
ImportExtConstants(ar, s_constants, ClassInfo::GetSystem());
|
|
ImportExtClasses(ar);
|
|
|
|
Array constants = ClassInfo::GetSystemConstants();
|
|
LocationPtr loc(new Location);
|
|
for (ArrayIter it = constants.begin(); it; ++it) {
|
|
CVarRef key = it.first();
|
|
if (!key.isString()) continue;
|
|
std::string name = key.toCStrRef().data();
|
|
if (s_constants->getSymbol(name)) continue;
|
|
if (name == "true" || name == "false" || name == "null") continue;
|
|
CVarRef value = it.secondRef();
|
|
if (!value.isInitialized() || value.isObject()) continue;
|
|
ExpressionPtr e = Expression::MakeScalarExpression(ar2, ar2, loc, value);
|
|
TypePtr t =
|
|
value.isNull() ? Type::Null :
|
|
value.isBoolean() ? Type::Boolean :
|
|
value.isInteger() ? Type::Int64 :
|
|
value.isDouble() ? Type::Double :
|
|
value.isArray() ? Type::Array : Type::Variant;
|
|
|
|
s_constants->add(key.toCStrRef().data(), t, e, ar2, e);
|
|
}
|
|
s_variables = ar2->getVariables();
|
|
for (int i = 0, n = NumGlobalNames(); i < n; ++i) {
|
|
s_variables->add(GlobalNames[i], Type::Variant, false, ar,
|
|
ConstructPtr(), ModifierExpressionPtr());
|
|
}
|
|
|
|
s_constants->setDynamic(ar, "SID", true);
|
|
s_constants->setDynamic(ar, "PHP_SAPI", true);
|
|
|
|
// parse all PHP files under system/php
|
|
s_systemAr = ar = AnalysisResultPtr(new AnalysisResult());
|
|
ar->loadBuiltins();
|
|
string slib = get_systemlib();
|
|
|
|
Scanner scanner(slib.c_str(), slib.size(),
|
|
Option::GetScannerType(), "systemlib.php");
|
|
Compiler::Parser parser(scanner, "systemlib.php", ar);
|
|
if (!parser.parse()) {
|
|
Logger::Error("Unable to parse systemlib.php: %s",
|
|
parser.getMessage().c_str());
|
|
assert(false);
|
|
}
|
|
|
|
ar->analyzeProgram(true);
|
|
ar->inferTypes();
|
|
const StringToFileScopePtrMap &files = ar->getAllFiles();
|
|
for (StringToFileScopePtrMap::const_iterator iterFile = files.begin();
|
|
iterFile != files.end(); iterFile++) {
|
|
const StringToClassScopePtrVecMap &classes =
|
|
iterFile->second->getClasses();
|
|
for (StringToClassScopePtrVecMap::const_iterator iter = classes.begin();
|
|
iter != classes.end(); ++iter) {
|
|
assert(iter->second.size() == 1);
|
|
iter->second[0]->setSystem();
|
|
assert(!s_classes[iter->first]);
|
|
s_classes[iter->first] = iter->second[0];
|
|
}
|
|
|
|
const StringToFunctionScopePtrMap &functions =
|
|
iterFile->second->getFunctions();
|
|
for (StringToFunctionScopePtrMap::const_iterator iter = functions.begin();
|
|
iter != functions.end(); ++iter) {
|
|
iter->second->setSystem();
|
|
s_functions[iter->first] = iter->second;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
AnalysisResultPtr BuiltinSymbols::LoadGlobalSymbols(const char *fileName) {
|
|
AnalysisResultPtr ar(new AnalysisResult());
|
|
string phpBaseName = "/system/globals/";
|
|
phpBaseName += fileName;
|
|
string phpFileName = Option::GetSystemRoot() + phpBaseName;
|
|
const char *baseName = s_strings.add(phpBaseName.c_str());
|
|
fileName = s_strings.add(phpFileName.c_str());
|
|
|
|
try {
|
|
Scanner scanner(fileName, Option::GetScannerType());
|
|
Compiler::Parser parser(scanner, baseName, ar);
|
|
if (!parser.parse()) {
|
|
assert(false);
|
|
Logger::Error("Unable to parse file %s: %s", fileName,
|
|
parser.getMessage().c_str());
|
|
}
|
|
} catch (FileOpenException &e) {
|
|
Logger::Error("%s", e.getMessage().c_str());
|
|
}
|
|
ar->analyzeProgram(true);
|
|
ar->inferTypes();
|
|
return ar;
|
|
}
|
|
|
|
void BuiltinSymbols::LoadFunctions(AnalysisResultPtr ar,
|
|
StringToFunctionScopePtrMap &functions) {
|
|
assert(Loaded);
|
|
functions.insert(s_functions.begin(), s_functions.end());
|
|
}
|
|
|
|
void BuiltinSymbols::LoadClasses(AnalysisResultPtr ar,
|
|
StringToClassScopePtrMap &classes) {
|
|
assert(Loaded);
|
|
classes.insert(s_classes.begin(), s_classes.end());
|
|
}
|
|
|
|
void BuiltinSymbols::LoadVariables(AnalysisResultPtr ar,
|
|
VariableTablePtr variables) {
|
|
assert(Loaded);
|
|
if (s_variables) {
|
|
variables->import(s_variables);
|
|
}
|
|
}
|
|
|
|
void BuiltinSymbols::LoadConstants(AnalysisResultPtr ar,
|
|
ConstantTablePtr constants) {
|
|
assert(Loaded);
|
|
if (s_constants) {
|
|
constants->import(s_constants);
|
|
}
|
|
}
|
|
|
|
ConstantTablePtr BuiltinSymbols::LoadSystemConstants() {
|
|
AnalysisResultPtr ar = LoadGlobalSymbols("constants.php");
|
|
const auto &fileScopes = ar->getAllFilesVector();
|
|
if (!fileScopes.empty()) {
|
|
return fileScopes[0]->getConstants();
|
|
}
|
|
throw std::runtime_error("LoadSystemConstants failed");
|
|
}
|
|
|
|
void BuiltinSymbols::LoadSuperGlobals() {
|
|
if (s_superGlobals.empty()) {
|
|
s_superGlobals["_SERVER"] = Type::Variant;
|
|
s_superGlobals["_GET"] = Type::Variant;
|
|
s_superGlobals["_POST"] = Type::Variant;
|
|
s_superGlobals["_COOKIE"] = Type::Variant;
|
|
s_superGlobals["_FILES"] = Type::Variant;
|
|
s_superGlobals["_ENV"] = Type::Variant;
|
|
s_superGlobals["_REQUEST"] = Type::Variant;
|
|
s_superGlobals["_SESSION"] = Type::Variant;
|
|
s_superGlobals["http_response_header"] = Type::Variant;
|
|
}
|
|
}
|
|
|
|
bool BuiltinSymbols::IsSuperGlobal(const std::string &name) {
|
|
return s_superGlobals.find(name) != s_superGlobals.end();
|
|
}
|
|
|
|
TypePtr BuiltinSymbols::GetSuperGlobalType(const std::string &name) {
|
|
StringToTypePtrMap::const_iterator iter = s_superGlobals.find(name);
|
|
if (iter != s_superGlobals.end()) {
|
|
return iter->second;
|
|
}
|
|
return TypePtr();
|
|
}
|