Arquivos
hhvm/hphp/tools/bootstrap/gen-class-map.cpp
T
Owen Yamauchi 49269952c8 Move everything out of GlobalNameValueTableWrapper
This is to clear the runway for getting rid of
GlobalNameValueTableWrapper. It moves aside these three items that were
in there for no particular reason other than convenience. I moved them
aside into another struct that I arena-allocate and initialize at the
same time as the global VarEnv (which initializes the GlobalNVTW).

I called the struct where these live "EnvConstants" since they look like
constants to PHP but their values are determined at startup time (by the
environment, like whether we're in server mode). lvalProxy doesn't fit
that mold, but oh well.
2013-06-18 16:23:27 -07:00

400 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 <algorithm>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include <unordered_set>
#include "folly/FBString.h"
#include "folly/FBVector.h"
#include "hphp/tools/bootstrap/idl.h"
using folly::fbstring;
using folly::fbvector;
using namespace HPHP::IDL;
using namespace HPHP;
#define VISIBILITY_MASK (IsPublic|IsProtected|IsPrivate)
/////////////////////////////////////////////////////////////////////////////
// Helpers
static inline fbstring castLong(unsigned long lval, bool hex = false) {
if (hex) {
char buffer[10];
snprintf(buffer, sizeof(buffer), "%lx", lval & 0xffffffff);
return folly::to<fbstring>("(const char *)0x", buffer);
}
return folly::to<fbstring>("(const char*)", lval);
}
/////////////////////////////////////////////////////////////////////////////
// Generated doc comments
static fbstring formatDocComment(const fbstring& comment) {
const char *p = comment.c_str();
const char *s;
const char *e = p + comment.size();
fbstring ret("/**\n");
while ((s = strchr(p, '\n'))) {
ret += " *";
if ((s - p) > 1) {
ret += " " + fbstring(p, (s - p));
}
ret += "\n";
p = s + 1;
}
if (p < e) {
ret += " * " + fbstring(p) + "\n";
}
return ret + " */";
}
static fbstring genDocCommentPreamble(const fbstring& name,
const fbstring& desc,
long flags,
const fbstring& classname) {
fbstring ret;
if (flags & HipHopSpecific) {
ret = "( HipHop specific )";
} else {
ret = "( excerpt from http://php.net/manual/en/";
if (classname.size()) {
ret += classname + ".";
} else {
ret += "function.";
}
ret += name + ".php )";
}
if (desc.size()) {
ret += "\n" + desc;
}
return ret + "\n\n";
}
static fbstring genDocComment(const PhpFunc& func,
const fbstring& classname) {
fbstring ret(genDocCommentPreamble(func.name(), func.getDesc(),
func.flags(), classname));
for (auto &param : func.params()) {
ret += "@" + param.name() + " " + param.getPhpType() + " ";
if (param.isRef()) {
ret += "(output) ";
}
ret += param.getDesc() + "\n";
}
if (func.numParams() > 0) {
ret += "\n";
}
auto rko = func.returnKindOf();
if ((rko != KindOfNull) && (rko != KindOfInvalid)) {
ret += "@return " + func.returnPhpType() + " ";
if (func.isReturnRef()) {
ret += "(output) ";
}
ret += func.returnDesc() + "\n";
}
return formatDocComment(ret);
}
static fbstring genDocComment(const PhpClass& cls) {
return formatDocComment(
genDocCommentPreamble(cls.name(), cls.getDesc(), cls.flags(), "")
);
}
/////////////////////////////////////////////////////////////////////////////
// System constants
static void declareConstants(std::ostream &out,
const fbvector<PhpConst>& consts,
bool extrn) {
for (auto c : consts) {
if (!c.hasValue()) {
continue;
}
if (extrn) {
out << "extern const " << c.getCppType() << " " << c.varname() << ";\n";
} else if (c.kindOf() == KindOfString) {
fbstring val = c.value();
out << "extern const StaticString " << c.varname()
<< "(\"" << escapeCpp(val) << "\"," << val.size() << ");\n";
} else {
out << "const " << c.getCppType() << " " << c.varname()
<< " = " << escapeCpp(c.value()) << ";\n";
}
}
}
static void outputConstants(const char *outputfn,
const fbvector<PhpConst>& consts) {
std::ofstream out(outputfn);
out << "// @" "generated by gen-class-map.cpp\n"
<< "#ifndef _H_SYSTEM_CONSTANTS\n"
<< "#define _H_SYSTEM_CONSTANTS\n"
<< "namespace HPHP {\n"
<< "class StaticString;\n"
<< "class Variant;\n";
declareConstants(out, consts, true);
out << "} // namespace HPHP\n"
<< "#endif // _H_SYSTEM_CONSTANTS\n";
}
/////////////////////////////////////////////////////////////////////////////
// Class Map
#define FUNC_FLAG_MASK (IsProtected|IsPrivate|IsPublic|\
IsAbstract|IsStatic|IsFinal|HasDocComment|\
AllowIntercept|NoProfile|ContextSensitive|\
HipHopSpecific|VariableArguments|\
RefVariableArguments|MixedVariableArguments|\
NeedsActRec|FunctionIsFoldable|\
NoInjection|NoEffect|HasOptFunction)
static void writeFunction(std::ostream& out, const PhpFunc& func) {
auto flags = (func.flags() & FUNC_FLAG_MASK) | IsSystem | IsNothing;
if (flags & RefVariableArguments) {
flags |= VariableArguments;
}
if (flags & MixedVariableArguments) {
flags |= RefVariableArguments | VariableArguments;
}
if (!func.isMethod() || !(flags & VISIBILITY_MASK)) {
flags |= IsPublic;
}
if (func.isReturnRef()) {
flags |= IsReference;
}
out << " " << castLong(flags, true) << ", \"" << func.name() << "\", "
<< "\"\", "
<< castLong(0) << ", "
<< castLong(0) << ",\n";
if (flags & HasDocComment) {
out << " \""
<< escapeCpp(genDocComment(func, func.className()))
<< "\",\n";
}
DataType rko = func.returnKindOf();
if (rko == KindOfAny) {
// ClassInfo::MethodInfo expects this for Any/Variant
// TODO: Fix that broken assumption
rko = KindOfInvalid;
}
out << " " << castLong(rko, true) << ",\n";
for (auto &p : func.params()) {
long attr = IsNothing;
DataType ko = p.kindOf();
if (p.isRef()) {
// We don't declare param type as KindOfRef
// as then the caller will try to cast it as such
attr |= IsReference;
ko = KindOfAny;
}
if (ko == KindOfAny) {
// TODO: See above
ko = KindOfInvalid;
}
out << " "
<< castLong(attr, true) << ", "
<< "\"" << escapeCpp(p.name()) << "\", \"\", "
<< castLong(ko, true) << ",\n";
auto ser = p.getDefaultSerialized();
auto val = p.getDefaultPhp();
out << " "
<< "\"" << escapeCpp(ser) << "\", " << castLong(ser.size()) << ", "
<< "\"" << escapeCpp(val) << "\", " << castLong(val.size()) << ", "
<< "NULL,\n";
}
out << " NULL,\n"
<< " NULL,\n"
<< " NULL,\n";
}
static void writeConstant(std::ostream& out, const PhpConst& cns) {
auto name = cns.name();
out << " \"" << escapeCpp(name) << "\", ";
if (cns.hasValue()) {
auto ser = cns.serialize();
out << castLong(ser.size())
<< ", \"" << escapeCpp(ser) << "\",\n";
return;
}
if (cns.isSystem()) {
// Special "magic" constants
if ((name == "SID") || (name == "PHP_SAPI")) {
out << "(const char *)((offsetof(EnvConstants, k_" << name << ") - "
<< "offsetof(EnvConstants, stgv_Variant)) / sizeof(Variant)), "
<< castLong(1) << ",\n";
return;
}
if ((name == "STDIN") || (name == "STDOUT") || (name == "STDERR")) {
out << "(const char *)&BuiltinFiles::Get" << name << ", NULL,\n";
return;
}
}
out << "(const char *)&" << cns.varname() << ", "
<< castLong((int)cns.kindOf() + 2) << ",\n";
}
#define CLASS_FLAG_MASK (IsAbstract|IsFinal|NoDefaultSweep|\
HipHopSpecific|HasDocComment)
#define PROP_FLAG_MASK (IsProtected|IsPrivate|IsPublic|IsStatic)
static void writeClass(std::ostream& out, const PhpClass& cls) {
auto flags = (cls.flags() & CLASS_FLAG_MASK) | IsSystem | IsNothing;
out << " " << castLong(flags, true) << ", "
<< "\"" << escapeCpp(cls.name()) << "\", "
<< "\"" << escapeCpp(strtolower(cls.parent())) << "\", "
<< "\"\", "
<< castLong(0) << ", "
<< castLong(0) << ",\n";
if (flags & HasDocComment) {
out << " \"" << escapeCpp(genDocComment(cls)) << "\",\n";
}
out << " ";
for (auto &iface : cls.ifaces()) {
out << "\"" << escapeCpp(strtolower(iface)) << "\", ";
}
out << "NULL,\n";
for (auto &method : cls.methods()) {
writeFunction(out, method);
}
out << " NULL,\n";
for (auto &prop : cls.properties()) {
auto propflag = (prop.flags() & PROP_FLAG_MASK) | IsNothing;
if (!(propflag & VISIBILITY_MASK)) {
propflag |= IsPublic;
}
out << " " << castLong(propflag, true) << ", "
<< "\"" << escapeCpp(prop.name()) << "\", "
<< castLong((int)prop.kindOf(), true) << ",\n";
}
out << " NULL,\n";
for (auto &cns : cls.constants()) {
writeConstant(out, cns);
}
out << " NULL,\n";
out << " NULL,\n"; // no attributes
}
static void outputClassMap(const char *outputfn, const char *classmap_name,
const fbvector<PhpClass>& classes,
const fbvector<PhpFunc>& funcs,
const fbvector<PhpConst>& consts) {
std::ofstream out(outputfn);
out << "// @" "generated by gen-class-map.cpp\n"
<< "#include \"hphp/runtime/base/base_includes.h\"\n"
<< "#include \"hphp/runtime/ext/ext.h\"\n"
<< "namespace HPHP {\n";
declareConstants(out, consts, false);
out << "const char *" << classmap_name << "[] = {\n"
<< " (const char *)ClassInfo::IsSystem, NULL, "
<< "\"\", \"\", NULL, NULL, NULL,\n";
for (auto &f : funcs) {
writeFunction(out, f);
}
out << " NULL,\n" // End of functions
<< " NULL,\n"; // End of system "properties"
for (auto &c : consts) {
writeConstant(out, c);
}
out << " NULL,\n" // End of constants
<< " NULL,\n"; // End of system "attributes"
for (auto &c : classes) {
writeClass(out, c);
}
out << " NULL,\n" // End of classes
<< " NULL,\n" // End of classmap
<< "};\n"
<< "} // namespace HPHP\n";
}
void print_usage(const char* program_name) {
std::cout << "Usage:\n\n"
<< " " << program_name << "\n"
<< " --system\n"
<< " <class map output file>\n"
<< " <constants output file>\n"
<< " <*.idl.json>...\n\n"
<< " " << program_name << "\n"
<< " <class map name>\n"
<< " <class map output file>\n"
<< " <*.idl.json>...\n\n";
}
/////////////////////////////////////////////////////////////////////////////
int main(int argc, const char* argv[]) {
if (argc < 3) {
print_usage(argv[0]);
return 0;
}
bool system = !strcmp(argv[1], "--system");
if (system && argc < 4) {
print_usage(argv[0]);
return 0;
}
fbvector<PhpFunc> funcs;
fbvector<PhpClass> classes;
fbvector<PhpConst> consts;
for (int i = (system ? 4 : 3); i < argc; ++i) {
try {
parseIDL(argv[i], funcs, classes, consts);
} catch (const std::exception& exc) {
std::cerr << argv[i] << ": " << exc.what() << "\n";
return 1;
}
}
const char* path = argv[2];
const char* name = (system ? "g_class_map" : argv[1]);
outputClassMap(path, name, classes, funcs, consts);
if (system) {
path = argv[3];
outputConstants(path, consts);
}
return 0;
}