Arquivos
hhvm/hphp/compiler/builtin_symbols.cpp
T

611 linhas
19 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/builtin_symbols.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/statement/statement_list.h>
#include <compiler/analysis/type.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/expression/modifier_expression.h>
#include <compiler/option.h>
#include <compiler/parser/parser.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/analysis/constant_table.h>
#include <util/parser/hphp.tab.hpp>
#include <runtime/base/class_info.h>
#include <runtime/base/program_functions.h>
#include <runtime/base/array/array_iterator.h>
#include <util/logger.h>
#include <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;
bool BuiltinSymbols::NoSuperGlobals = false;
StringBag BuiltinSymbols::s_strings;
namespace HPHP {
#define EXT_TYPE 4
#include <system/ext.inc>
#undef EXT_TYPE
}
const char *BuiltinSymbols::ExtensionFunctions[] = {
#define S(n) (const char *)n
#define T(t) (const char *)Type::KindOf ## t
#define EXT_TYPE 0
#include <system/ext.inc>
nullptr,
};
#undef EXT_TYPE
const char *BuiltinSymbols::ExtensionConsts[] = {
#define EXT_TYPE 1
#include <system/ext.inc>
nullptr,
};
#undef EXT_TYPE
const char *BuiltinSymbols::ExtensionClasses[] = {
#define EXT_TYPE 2
#include <system/ext.inc>
nullptr,
};
#undef EXT_TYPE
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;
void *BuiltinSymbols::s_handle_main = nullptr;
///////////////////////////////////////////////////////////////////////////////
int BuiltinSymbols::NumGlobalNames() {
return sizeof(BuiltinSymbols::GlobalNames) /
sizeof(BuiltinSymbols::GlobalNames[0]);
}
void BuiltinSymbols::ParseExtFunctions(AnalysisResultPtr ar, const char **p,
bool sep) {
while (*p) {
FunctionScopePtr f = ParseExtFunction(ar, p);
if (sep) {
f->setSepExtension();
}
assert(!s_functions[f->getName()]);
s_functions[f->getName()] = f;
}
}
void BuiltinSymbols::ParseExtConsts(AnalysisResultPtr ar, const char **p,
bool sep) {
while (*p) {
const char *name = *p++;
TypePtr type = ParseType(p);
s_constants->add(name, type, ExpressionPtr(), ar, ConstructPtr());
if (sep) {
s_constants->setSepExtension(name);
}
}
}
TypePtr BuiltinSymbols::ParseType(const char **&p) {
const char *clsname = nullptr;
Type::KindOf ktype = (Type::KindOf)(long)(*p++);
if (ktype == CLASS_TYPE) {
clsname = *p++;
}
TypePtr type;
if (clsname) {
type = Type::CreateObjectType(clsname);
} else if (ktype != Type::KindOfVoid) {
type = Type::GetType(ktype);
}
return type;
}
void BuiltinSymbols::ParseExtClasses(AnalysisResultPtr ar, const char **p,
bool sep) {
while (*p) {
// Parse name
const char *cname = *p++;
// Parse parent
const char *cparent = *p++;
if (!cparent) cparent = "";
// Parse list of interfaces
vector<string> ifaces;
while (*p) ifaces.push_back(*p++);
p++;
// Parse methods
FunctionScopePtrVec methods;
while (*p) {
FunctionScopePtr fs = ParseExtFunction(ar, p, true);
if (sep) {
fs->setSepExtension();
}
int flags = (int)(int64_t)(*p++);
if (flags & ClassInfo::IsAbstract) {
fs->addModifier(T_ABSTRACT);
}
int vismod = 0;
if (flags & ClassInfo::IsProtected) {
vismod = T_PROTECTED;
} else if (flags & ClassInfo::IsPrivate) {
vismod = T_PRIVATE;
}
fs->addModifier(vismod);
if (flags & ClassInfo::IsStatic) {
fs->addModifier(T_STATIC);
}
methods.push_back(fs);
}
if (cparent && *cparent && (ifaces.empty() || ifaces[0] != cparent)) {
ifaces.insert(ifaces.begin(), cparent);
}
ClassScopePtr cl(new ClassScope(ar, cname, cparent, ifaces, methods));
for (uint i = 0; i < methods.size(); ++i) {
methods[i]->setOuterScope(cl);
}
p++;
// Parse properties
while (*p) {
int flags = (int)(int64_t)(*p++);
ModifierExpressionPtr modifiers(
new ModifierExpression(BlockScopePtr(), LocationPtr()));
if (flags & ClassInfo::IsProtected) {
modifiers->add(T_PROTECTED);
} else if (flags & ClassInfo::IsPrivate) {
modifiers->add(T_PRIVATE);
}
if (flags & ClassInfo::IsStatic) {
modifiers->add(T_STATIC);
}
const char *name = *p++;
TypePtr type = ParseType(p);
cl->getVariables()->add(name, type, false, ar, ExpressionPtr(), modifiers);
}
p++;
// Parse consts
while (*p) {
const char *name = *p++;
TypePtr type = ParseType(p);
cl->getConstants()->add(name, type, ExpressionPtr(), ar, ConstructPtr());
}
p++;
int flags = (int)(int64_t)(*p++);
cl->setClassInfoAttribute(flags);
if (flags & ClassInfo::HasDocComment) {
cl->setDocComment(*p++);
}
cl->setSystem();
if (sep) {
cl->setSepExtension();
}
s_classes[cl->getName()] = cl;
}
}
FunctionScopePtr BuiltinSymbols::ParseExtFunction(AnalysisResultPtr ar,
const char** &p, bool method /* = false */) {
const char *name = *p++;
TypePtr retType = ParseType(p);
bool reference = *p++;
int minParam = -1;
int maxParam = 0;
const char **arg = p;
while (*arg) {
/* name */ arg++;
ParseType(arg);
const char *argDefault = *arg++;
/* const char *argDefaultLen = */ arg++;
/* const char *argDefaultText = */ arg++;
/* bool argReference = */ arg++;
if (argDefault && minParam < 0) {
minParam = maxParam;
}
maxParam++;
}
if (minParam < 0) minParam = maxParam;
FunctionScopePtr f(new FunctionScope(method, name, reference));
f->setParamCounts(ar, minParam, maxParam);
if (retType) {
f->setReturnType(ar, retType);
}
int index = 0;
const char *paramName = nullptr;
while ((paramName = *p++ /* argName */)) {
TypePtr argType = ParseType(p);
const char *argDefault = *p++;
const char *argDefaultLen = *p++;
const char *argDefaultText = *p++;
bool argReference = *p++;
f->setParamName(index, paramName);
if (argReference) f->setRefParam(index);
f->setParamType(ar, index, argType);
if (argDefault) f->setParamDefault(index, argDefault,
(int64_t)argDefaultLen,
argDefaultText);
index++;
}
int flags = (int)(int64_t)(*p++);
f->setClassInfoAttribute(flags);
if (flags & ClassInfo::HasDocComment) {
f->setDocComment(*p++);
}
if (flags & ClassInfo::HasOptFunction) {
f->setOptFunction((FunctionOptPtr)(*p++));
}
// This block of code is not needed, if BlockScope directly takes flags.
if (flags & ClassInfo::MixedVariableArguments) {
f->setVariableArgument(-1);
} else if (flags & ClassInfo::RefVariableArguments) {
f->setVariableArgument(1);
} else if (flags & ClassInfo::VariableArguments) {
f->setVariableArgument(0);
}
if (flags & ClassInfo::NoEffect) {
f->setNoEffect();
}
if (flags & ClassInfo::FunctionIsFoldable) {
f->setIsFoldable();
}
if (flags & ClassInfo::ContextSensitive) {
f->setContextSensitive(true);
}
if (flags & ClassInfo::NeedsActRec) {
f->setNeedsActRec();
}
if ((flags & ClassInfo::IgnoreRedefinition) && !method) {
f->setIgnoreRedefinition();
}
return f;
}
FunctionScopePtr BuiltinSymbols::ParseHelperFunction(AnalysisResultPtr ar,
const char** &p) {
FunctionScopePtr f = ParseExtFunction(ar, p);
f->setHelperFunction();
return f;
}
bool BuiltinSymbols::LoadSepExtensionSymbols(AnalysisResultPtr ar,
const std::string &name,
const std::string &soname) {
string mapname = name + "_map";
const char ***symbols = nullptr;
// If we linked with .a, the symbol is already in main program.
if (s_handle_main == nullptr) {
s_handle_main = dlopen(nullptr, RTLD_NOW | RTLD_GLOBAL);
if (!s_handle_main) {
const char *error = dlerror();
Logger::Error("Unable to load main program's symbols: %s",
error ? error : "(unknown)");
}
}
if (s_handle_main) {
symbols = (const char ***)dlsym(s_handle_main, mapname.c_str());
}
// Otherwise, look for .so to load it.
void *handle = nullptr;
if (!symbols) {
handle = dlopen(soname.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
const char *error = dlerror();
Logger::Error("Unable to load %s: %s", soname.c_str(),
error ? error : "(unknown)");
return false;
}
symbols = (const char ***)dlsym(handle, mapname.c_str());
if (!symbols) {
Logger::Error("Unable to find %s in %s", mapname.c_str(),
soname.c_str());
dlclose(handle);
return false;
}
}
ParseExtFunctions(ar, symbols[0], true);
ParseExtConsts (ar, symbols[1], true);
ParseExtClasses (ar, symbols[2], true);
if (handle) {
/*
Not closing for now, because it may have set an object allocator,
which would then fail next time its used.
I think the object allocators should be fixed instead - one per size,
rather than one per class, then this issue wouldnt occur
*/
// dlclose(handle);
}
return true;
}
void BuiltinSymbols::Parse(AnalysisResultPtr ar,
const std::string& phpBaseName,
const std::string& phpFileName) {
const char *baseName = s_strings.add(phpBaseName.c_str());
const char *fileName = s_strings.add(phpFileName.c_str());
try {
Scanner scanner(fileName, Option::ScannerType);
Compiler::Parser parser(scanner, baseName, ar);
if (!parser.parse()) {
Logger::Error("Unable to parse file %s: %s", fileName,
parser.getMessage().c_str());
assert(false);
}
} catch (FileOpenException &e) {
Logger::Error("%s", e.getMessage().c_str());
}
}
bool BuiltinSymbols::Load(AnalysisResultPtr ar, bool extOnly /* = false */) {
if (Loaded) return true;
Loaded = true;
// load extension functions first, so system/classes may call them
ParseExtFunctions(ar, ExtensionFunctions, false);
AnalysisResultPtr ar2 = AnalysisResultPtr(new AnalysisResult());
s_variables = VariableTablePtr(new VariableTable(*ar2.get()));
s_constants = ConstantTablePtr(new ConstantTable(*ar2.get()));
// parse all PHP files under system/classes
if (!extOnly) {
ar = AnalysisResultPtr(new AnalysisResult());
ar->loadBuiltinFunctions();
string slib = systemlib_path();
if (slib.empty()) {
for (const char **cls = SystemClasses; *cls; cls++) {
string phpBaseName = "/system/classes/";
phpBaseName += *cls;
phpBaseName += ".php";
Parse(ar, phpBaseName, Option::GetSystemRoot() + phpBaseName);
}
} else {
Parse(ar, slib, slib);
}
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];
}
}
} else {
NoSuperGlobals = true;
}
// load extension constants, classes and dynamics
ParseExtConsts(ar, ExtensionConsts, false);
ParseExtClasses(ar, ExtensionClasses, false);
for (unsigned int i = 0; i < Option::SepExtensions.size(); i++) {
Option::SepExtensionOptions &options = Option::SepExtensions[i];
string soname = options.soname;
if (soname.empty()) {
soname = string("lib") + options.name + ".so";
}
if (!options.lib_path.empty()) {
soname = options.lib_path + "/" + soname;
}
if (!LoadSepExtensionSymbols(ar, options.name, soname)) {
return false;
}
}
if (!extOnly) {
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);
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::ScannerType);
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);
for (StringToFunctionScopePtrMap::const_iterator it = s_functions.begin();
it != s_functions.end(); ++it) {
if (functions.find(it->first) == functions.end()) {
functions[it->first] = it->second;
FunctionScope::RecordFunctionInfo(it->first, it->second);
}
}
}
void BuiltinSymbols::LoadClasses(AnalysisResultPtr ar,
StringToClassScopePtrMap &classes) {
assert(Loaded);
classes.insert(s_classes.begin(), s_classes.end());
// we are adding these builtin functions, so that user-defined functions
// will not overwrite them with their own file and line number information
for (StringToClassScopePtrMap::const_iterator iter =
s_classes.begin(); iter != s_classes.end(); ++iter) {
const StringToFunctionScopePtrMap &funcs = iter->second->getFunctions();
for (StringToFunctionScopePtrMap::const_iterator iter =
funcs.begin(); iter != funcs.end(); ++iter) {
FunctionScope::RecordFunctionInfo(iter->first, iter->second);
}
}
}
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) {
if (NoSuperGlobals) return false;
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();
}