/* +----------------------------------------------------------------------+ | 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 #include #include #include #include #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("(const char *)0x", buffer); } return folly::to("(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 ¶m : 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& 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& 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& classes, const fbvector& funcs, const fbvector& consts, const fbvector& exts) { 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"; for (auto &e : exts) { auto sym = e.symbol(); if (!sym.empty()) { out << " (const char*)&" << sym << ",\n"; } } out << " NULL,\n"; // End of extensions out << " (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" << " \n" << " \n" << " <*.idl.json>...\n\n" << " " << program_name << "\n" << " \n" << " \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 funcs; fbvector classes; fbvector consts; fbvector exts; for (int i = (system ? 4 : 3); i < argc; ++i) { try { parseIDL(argv[i], funcs, classes, consts, exts); } 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, exts); if (system) { path = argv[3]; outputConstants(path, consts); } return 0; }