Arquivos
hhvm/hphp/tools/bootstrap/gen-class-map.cpp
T
Paul Tarjan 506f21c4b5 Allow extension functions to match zend calling convention
Introducing `ZendParamMode` to as a idl flag. We are not consistent with zend on how they do their params for builtins. We cast to the expected data type. They do some checks, and if the checks don't pass they issue a warning and return (usually) `null`. This diff starts us down that path.

I'm introducing the param and using it in the places where we were emulating the calling convention in the `f_foo` functions. I'm going to follow up with converting as many as I can and then eventually this becomes the default. I also want this to be applied to php files in systemlib.

Many of the conversions are from https://github.com/php/php-src/blob/master/Zend/zend_API.c#L305
2013-06-25 13:19:04 -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|ZendParamMode)
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;
}