8283969d81
Most CPP extension classes don’t think about serialization, and some require more initialization than the default contractor provides. The result is that on deserialization we end up with a partially initialized object, and undefined behavior after that. Modified the serializer to emit __PHP_Unserializable_Class instead, and just the class name for the debugger case. Modified the deserializer to create a __PHP_Unserializable_Class, too, if we are given a string with a CPP extension instance in it, so we're safe on both sides. I do not raise a warning; the results are pretty clear, I think, and it's also unlikely that most such warnings could (or should) be corrected. Finally, I also removed the odd behavior of emitting "dummy" classes for Closure and Continuation. I found no use of these in any source tree. Closure had no implementation at all, and Continuation had an implementation that simply raised a fatal on any method call. Thus we can safely assume that even if someone was serializing one of these types, they were receiving an object that was useless to them, or would fatal. So I believe this transformation is safe.
786 linhas
21 KiB
C++
786 linhas
21 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/tools/bootstrap/idl.h"
|
|
|
|
#include <fstream>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
|
|
#include "folly/Format.h"
|
|
#include "folly/json.h"
|
|
|
|
#ifdef __APPLE__
|
|
#define INT64_TYPE "long long"
|
|
#else
|
|
#define INT64_TYPE "long"
|
|
#endif
|
|
|
|
namespace HPHP { namespace IDL {
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
static const std::unordered_map<fbstring, DataType> g_kindOfMap =
|
|
{
|
|
{"Boolean", KindOfBoolean},
|
|
{"Int32", KindOfInt64},
|
|
{"Int64", KindOfInt64},
|
|
{"Double", KindOfDouble},
|
|
{"String", KindOfString},
|
|
{"Int64Vec", KindOfArray},
|
|
{"StringVec", KindOfArray},
|
|
{"VariantVec", KindOfArray},
|
|
{"Int64Map", KindOfArray},
|
|
{"StringMap", KindOfArray},
|
|
{"VariantMap", KindOfArray},
|
|
{"Object", KindOfObject},
|
|
{"Resource", KindOfObject},
|
|
{"Variant", KindOfAny},
|
|
{"Numeric", KindOfAny},
|
|
{"Primitive", KindOfAny},
|
|
{"PlusOperand", KindOfAny},
|
|
{"Sequence", KindOfAny},
|
|
{"Any", KindOfAny},
|
|
};
|
|
|
|
static const std::unordered_map<int, fbstring> g_typeMap =
|
|
{
|
|
{(int)KindOfInvalid, "void"},
|
|
{(int)KindOfNull, "HPHP::Variant"},
|
|
{(int)KindOfBoolean, "bool"},
|
|
{(int)KindOfInt64, INT64_TYPE},
|
|
{(int)KindOfDouble, "double"},
|
|
{(int)KindOfString, "HPHP::String"},
|
|
{(int)KindOfArray, "HPHP::Array"},
|
|
{(int)KindOfObject, "HPHP::Object"},
|
|
{(int)KindOfAny, "HPHP::Variant"},
|
|
};
|
|
|
|
static const std::unordered_map<int, fbstring> g_phpTypeMap =
|
|
{
|
|
{(int)KindOfInvalid, "void"},
|
|
{(int)KindOfNull, "void"},
|
|
{(int)KindOfBoolean, "bool"},
|
|
{(int)KindOfInt64, INT64_TYPE},
|
|
{(int)KindOfDouble, "double"},
|
|
{(int)KindOfString, "String"},
|
|
{(int)KindOfArray, "Array"},
|
|
{(int)KindOfObject, "Object"},
|
|
{(int)KindOfAny, "mixed"},
|
|
};
|
|
|
|
static const std::unordered_map<fbstring, FuncFlags> g_flagsMap =
|
|
{
|
|
{"ZendParamMode", ZendParamMode},
|
|
{"IsAbstract", IsAbstract},
|
|
{"IsFinal", IsFinal},
|
|
{"IsPublic", IsPublic},
|
|
{"IsProtected", IsProtected},
|
|
{"IsPrivate", IsPrivate},
|
|
{"IgnoreRedefinition", IgnoreRedefinition},
|
|
{"IsStatic", IsStatic},
|
|
{"IsCppAbstract", IsCppAbstract},
|
|
{"IsReference", IsReference},
|
|
{"IsConstructor", IsConstructor},
|
|
{"IsNothing", IsNothing},
|
|
{"HasDocComment", HasDocComment},
|
|
{"IsCppSerializable", IsCppSerializable},
|
|
{"HipHopSpecific", HipHopSpecific},
|
|
{"VariableArguments", VariableArguments},
|
|
{"RefVariableArguments", RefVariableArguments},
|
|
{"MixedVariableArguments", MixedVariableArguments},
|
|
{"FunctionIsFoldable", FunctionIsFoldable},
|
|
{"NoEffect", NoEffect},
|
|
{"NoInjection", NoInjection},
|
|
{"HasOptFunction", HasOptFunction},
|
|
{"AllowIntercept", AllowIntercept},
|
|
{"NoProfile", NoProfile},
|
|
{"ContextSensitive", ContextSensitive},
|
|
{"NoDefaultSweep", NoDefaultSweep},
|
|
{"IsSystem", IsSystem},
|
|
{"IsTrait", IsTrait},
|
|
{"NeedsActRec", NeedsActRec},
|
|
};
|
|
|
|
static const std::unordered_set<fbstring> g_knownStringConstants =
|
|
{ "k_HPHP_TRIM_CHARLIST" };
|
|
|
|
bool isKindOfIndirect(DataType kindof) {
|
|
return (kindof != KindOfBoolean) &&
|
|
(kindof != KindOfInt64) &&
|
|
(kindof != KindOfDouble) &&
|
|
(kindof != KindOfInvalid) &&
|
|
(kindof != KindOfNull);
|
|
}
|
|
|
|
// Parse type from a descriptive string, e.g. "int", "bool", etc...
|
|
static DataType kindOfFromDynamic(const folly::dynamic& t) {
|
|
if (!t.isString()) {
|
|
return KindOfInvalid;
|
|
}
|
|
auto it = g_kindOfMap.find(t.asString());
|
|
if (it == g_kindOfMap.end()) {
|
|
return KindOfObject;
|
|
}
|
|
|
|
return it->second;
|
|
}
|
|
|
|
// Infer type from an actual value, e.g. 123, "foo", null, true, etc...
|
|
static DataType kindOfFromValue(const folly::dynamic& v) {
|
|
if (v.isNull()) {
|
|
return KindOfNull;
|
|
}
|
|
if (v.isBool()) {
|
|
return KindOfBoolean;
|
|
}
|
|
if (v.isInt()) {
|
|
return KindOfInt64;
|
|
}
|
|
if (v.isDouble()) {
|
|
return KindOfDouble;
|
|
}
|
|
if (v.isString()) {
|
|
return KindOfString;
|
|
}
|
|
if (v.isArray()) {
|
|
return KindOfArray;
|
|
}
|
|
if (v.isObject()) {
|
|
return KindOfObject;
|
|
}
|
|
return KindOfInvalid;
|
|
}
|
|
|
|
static fbstring phpTypeFromDataType(DataType dt) {
|
|
auto it = g_phpTypeMap.find((int)dt);
|
|
if (it == g_phpTypeMap.end()) {
|
|
return "mixed";
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
static fbstring typeString(const folly::dynamic& typeNode, bool isReturnType) {
|
|
if (typeNode == "Int32") {
|
|
return "int";
|
|
}
|
|
|
|
DataType kindof = kindOfFromDynamic(typeNode);
|
|
auto it = g_typeMap.find((int)kindof);
|
|
assert(it != g_typeMap.end());
|
|
|
|
auto& type = it->second;
|
|
if (!isReturnType && isKindOfIndirect(kindof)) {
|
|
return type + " const&";
|
|
} else {
|
|
return type;
|
|
}
|
|
}
|
|
|
|
static unsigned long parseFlags(const folly::dynamic &flags) {
|
|
if (flags.isNull()) {
|
|
return 0;
|
|
}
|
|
if (!flags.isArray()) {
|
|
throw std::logic_error("'flags' field must be an array");
|
|
}
|
|
|
|
unsigned long ret = 0;
|
|
for (auto &flag : flags) {
|
|
auto f = g_flagsMap.find(flag.asString());
|
|
if (f == g_flagsMap.end()) {
|
|
throw std::logic_error(
|
|
folly::format("Unknown flag '{0}' specified", flag.asString()).str()
|
|
);
|
|
}
|
|
ret |= f->second;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const std::unordered_map<fbstring,fbstring> g_serializedDefaults = {
|
|
{"true", "b:1;"},
|
|
{"false", "b:0;"},
|
|
{"null", "N;"},
|
|
{"empty_array", "a:0:{}"},
|
|
{"null_string", "N;"},
|
|
{"null_array", "N;"},
|
|
{"null_object", "N;"},
|
|
{"null_resource", "N;"},
|
|
{"null_variant", "N;"},
|
|
{"INT_MAX", "i:2147483647;"}, // (1 << 31) - 1
|
|
};
|
|
|
|
static const std::unordered_map<fbstring,fbstring> g_phpDefaults = {
|
|
{"true", "true"},
|
|
{"false", "false"},
|
|
{"null", "null"},
|
|
{"empty_array", "array()"},
|
|
{"null_string", "null"},
|
|
{"null_array", "null"},
|
|
{"null_object", "null"},
|
|
{"null_resource", "null"},
|
|
{"null_variant", "null"},
|
|
{"INT_MAX", "null"},
|
|
};
|
|
|
|
static fbstring unescapeString(fbstring val) {
|
|
fbstring s = "";
|
|
for (int i = 0; i < val.size(); ) {
|
|
int ch = val[i++];
|
|
if (ch == '\\') {
|
|
if (i == val.size()) {
|
|
throw std::logic_error(
|
|
folly::format("Malformed string: '{0}'", val).str());
|
|
}
|
|
ch = val[i++];
|
|
switch (ch) {
|
|
case 'n': ch = '\n'; break;
|
|
case 'r': ch = '\r'; break;
|
|
case 't': ch = '\t'; break;
|
|
case '/':
|
|
case '"':
|
|
case '\'':
|
|
case '\\':break;
|
|
case '0':
|
|
ch = 0;
|
|
if (i == val.size() ||
|
|
(!isdigit(val[i]) && val[i] != 'x' && val[i] != 'X')) {
|
|
break;
|
|
}
|
|
// fall through
|
|
default:
|
|
throw std::logic_error(
|
|
folly::format("Malformed string: '{0}'", val).str());
|
|
break;
|
|
}
|
|
}
|
|
s += (char)ch;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* From idl/base.php:get_serialized_default()
|
|
*/
|
|
fbstring PhpParam::getDefaultSerialized() const {
|
|
auto valIt = m_param.find("value");
|
|
if (valIt == m_param.items().end()) {
|
|
return ""; // No default
|
|
}
|
|
auto dval = valIt->second;
|
|
if (!dval.isString()) {
|
|
throw std::logic_error(
|
|
folly::format("Parameter '{0}' default value is non-string",
|
|
m_name).str()
|
|
);
|
|
}
|
|
auto val = dval.asString();
|
|
if (!val.size()) {
|
|
throw std::logic_error(
|
|
folly::format("Parameter '{0}' default value malformed (empty string), "
|
|
"specify \"\" as default value for actual empty string",
|
|
m_name).str()
|
|
);
|
|
}
|
|
|
|
// Function calls "foo()" or "foo::bar()" to C/C++ functions/static methods,
|
|
// a constant, or a bitmask of constants
|
|
//
|
|
// Used by ext_reflection to resolve the value at runtime or
|
|
// represent the function/method call.
|
|
if ((val.size() > 2) &&
|
|
(!strncmp(val.c_str(), "k_", 2) ||
|
|
!strncmp(val.c_str(), "q_", 2) ||
|
|
!strcmp(val.c_str() + val.size() - 2, "()"))) {
|
|
return "\x01";
|
|
}
|
|
|
|
// Fixed substitutions
|
|
auto it = g_serializedDefaults.find(val);
|
|
if (it != g_serializedDefaults.end()) {
|
|
return it->second;
|
|
}
|
|
|
|
if (val == "RAND_MAX") {
|
|
return folly::to<fbstring>("i:", RAND_MAX, ";");
|
|
}
|
|
|
|
// Quoted string: "foo"
|
|
if ((val.size() >= 2) && (val[0] == '"') && (val[val.size()-1] == '"')) {
|
|
auto s = unescapeString(val.substr(1, val.size() - 2));
|
|
return phpSerialize(s);
|
|
}
|
|
|
|
// Integers and Floats
|
|
if (strchr(val.c_str(), '.')) {
|
|
// Decimal float?
|
|
char *e = nullptr;
|
|
double dval = strtod(val.c_str(), &e);
|
|
if (e && !*e) {
|
|
return folly::to<fbstring>("d:", dval, ";");
|
|
}
|
|
}
|
|
|
|
if (val[0] == '0') {
|
|
if ((val.size() > 1) && (val[1] == 'x')) {
|
|
// Hex?
|
|
char *e = nullptr;
|
|
long lval = strtol(val.c_str() + 2, &e, 16);
|
|
if (e && !*e) {
|
|
return folly::to<fbstring>("i:", lval, ";");
|
|
}
|
|
} else {
|
|
// Octal?
|
|
char *e = nullptr;
|
|
long lval = strtol(val.c_str() + 1, &e, 8);
|
|
if (e && !*e) {
|
|
return folly::to<fbstring>("i:", lval, ";");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decimal?
|
|
char *e = nullptr;
|
|
long lval = strtol(val.c_str(), &e, 10);
|
|
if (e && !*e) {
|
|
return folly::to<fbstring>("i:", lval, ";");
|
|
}
|
|
|
|
throw std::logic_error(
|
|
folly::format("'{0}' is not a valid default arg value", val).str()
|
|
);
|
|
}
|
|
|
|
static fbstring transformConstants(const fbstring val) {
|
|
fbstring ret = val;
|
|
int i = 0;
|
|
int len = ret.size();
|
|
|
|
while (i < len) {
|
|
while ((i < len) && (ret[i] == ' ')) i++;
|
|
if ((len - i) < 2) break;
|
|
|
|
if ((ret[i+1] == '_') &&
|
|
((ret[i] == 'k') || (ret[i] == 'q'))) {
|
|
ret[i] = ret[i+1] = ' ';
|
|
}
|
|
while ((i < len) && (ret[i] != '|')) {
|
|
if (ret[i] == '$') {
|
|
ret[i] = ':';
|
|
}
|
|
i++;
|
|
}
|
|
i++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
fbstring PhpParam::getDefaultPhp() const {
|
|
fbstring val = getDefault();
|
|
if (!val.size()) {
|
|
return "";
|
|
}
|
|
|
|
auto it = g_phpDefaults.find(val);
|
|
if (it != g_phpDefaults.end()) {
|
|
return it->second;
|
|
}
|
|
|
|
if (val == "RAND_MAX") {
|
|
return folly::to<fbstring>(RAND_MAX);
|
|
}
|
|
|
|
if ((val.size() > 2) && (val[1] == '_') &&
|
|
((val[0] == 'k') || (val[0] == 'q'))) {
|
|
return transformConstants(val);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
fbstring phpSerialize(const folly::dynamic& d) {
|
|
if (d.isNull()) {
|
|
return "N;";
|
|
}
|
|
if (d.isBool()) {
|
|
return d.asBool() ? "b:1;" : "b:0;";
|
|
}
|
|
if (d.isInt()) {
|
|
return "i:" + d.asString() + ";";
|
|
}
|
|
if (d.isDouble()) {
|
|
return "d:" + d.asString() + ";";
|
|
}
|
|
if (d.isString()) {
|
|
auto str = d.asString();
|
|
return folly::to<fbstring>("s:", str.size(), ":\"", str, "\";");
|
|
}
|
|
if (d.isArray()) {
|
|
fbstring ret = folly::to<fbstring>("a:", d.size(), ":{");
|
|
int i = 0;
|
|
for (auto &v : d) {
|
|
ret += folly::to<fbstring>("i:", i, ";", phpSerialize(v));
|
|
}
|
|
return ret + "};";
|
|
}
|
|
if (d.isObject()) {
|
|
fbstring ret = folly::to<fbstring>("a:", d.size(), ":{");
|
|
int nextindex = 0;
|
|
for (auto &k : d.keys()) {
|
|
if (k.isNull()) {
|
|
ret += "i:0;";
|
|
if (nextindex <= 0) {
|
|
nextindex = 1;
|
|
}
|
|
} else if (k.isInt() || k.isDouble()) {
|
|
int i = k.asInt();
|
|
ret += folly::to<fbstring>("i:", i, ";");
|
|
if (nextindex <= i) {
|
|
nextindex = i + 1;
|
|
}
|
|
} else if (k.isString()) {
|
|
ret += folly::to<fbstring>("s:", k.size(), ":\"",
|
|
escapeCpp(k.asString()), "\";");
|
|
} else {
|
|
/* Should never be reached, but cover it to be safe */
|
|
ret += folly::to<fbstring>("i:", nextindex++, ";");
|
|
}
|
|
ret += phpSerialize(d[k]);
|
|
}
|
|
return ret + "};";
|
|
}
|
|
throw std::logic_error("Unhandled dynamic type in php serialization");
|
|
return "N;";
|
|
}
|
|
|
|
static fbstring getFollyDynamicDefaultString(const folly::dynamic& d,
|
|
const fbstring& key,
|
|
const fbstring& def) {
|
|
auto it = d.find(key);
|
|
if (it == d.items().end()) {
|
|
return def;
|
|
}
|
|
auto val = it->second;
|
|
if (val.isNull()) {
|
|
return def;
|
|
}
|
|
return val.asString();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// PhpConst
|
|
|
|
bool PhpConst::parseType(const folly::dynamic& cns) {
|
|
auto it = cns.find("type");
|
|
if (it != cns.items().end()) {
|
|
m_kindOf = kindOfFromDynamic(it->second);
|
|
m_cppType = typeString(it->second, false);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PhpConst::inferType(const folly::dynamic& cns) {
|
|
auto it = cns.find("value");
|
|
if (it != cns.items().end()) {
|
|
m_kindOf = kindOfFromValue(it->second);
|
|
auto typeIt = g_typeMap.find((int)m_kindOf);
|
|
if (typeIt != g_typeMap.end()) {
|
|
m_cppType = typeIt->second;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
PhpConst::PhpConst(const folly::dynamic& cns,
|
|
fbstring cls /* = "" */) :
|
|
m_constant(cns),
|
|
m_name(cns["name"].asString()),
|
|
m_className(cls) {
|
|
if (!parseType(cns) && !inferType(cns)) {
|
|
// Constant has neither explicit type nor implicit type from 'value'
|
|
assert(false);
|
|
m_kindOf = KindOfInvalid;
|
|
m_cppType = "void";
|
|
}
|
|
|
|
// Override typeString()'s selection for string values
|
|
if (m_kindOf == KindOfString) {
|
|
m_cppType = "HPHP::StaticString";
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// PhpParam
|
|
|
|
PhpParam::PhpParam(const folly::dynamic& param,
|
|
bool isMagicMethod /*= false */,
|
|
ParamMode paramMode /*= CoerceAndCall */) :
|
|
m_name(param["name"].asString()),
|
|
m_param(param),
|
|
m_desc(getFollyDynamicDefaultString(param, "desc", "")),
|
|
m_paramMode(paramMode) {
|
|
if (isMagicMethod) {
|
|
m_kindOf = KindOfAny;
|
|
m_cppType = "HPHP::Variant";
|
|
return;
|
|
}
|
|
|
|
if (isRef()) {
|
|
m_kindOf = KindOfRef;
|
|
m_cppType = "HPHP::VRefParamValue const&";
|
|
} else {
|
|
m_kindOf = kindOfFromDynamic(param["type"]);
|
|
m_cppType = typeString(param["type"], false);
|
|
}
|
|
|
|
m_phpType = phpTypeFromDataType(m_kindOf);
|
|
}
|
|
|
|
bool PhpParam::defValueNeedsVariable() const {
|
|
DataType cppKindOf = kindOf();
|
|
|
|
if (!hasDefault() || !isIndirectPass()) {
|
|
return false;
|
|
}
|
|
|
|
fbstring defVal = getDefault();
|
|
|
|
if (cppKindOf == KindOfString &&
|
|
((defVal == "empty_string") ||
|
|
(defVal == "null_string") ||
|
|
(g_knownStringConstants.count(defVal) > 0))) {
|
|
return false;
|
|
}
|
|
if ((cppKindOf == KindOfArray) && (defVal == "null_array")) {
|
|
return false;
|
|
}
|
|
if ((cppKindOf == KindOfObject) &&
|
|
(defVal == "null_object" || defVal == "null_resource")) {
|
|
return false;
|
|
}
|
|
if ((cppKindOf == KindOfAny) && (defVal == "null_variant")) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// PhpFunc
|
|
|
|
PhpFunc::PhpFunc(const folly::dynamic& d,
|
|
const fbstring& className) :
|
|
m_name(d["name"].asString()),
|
|
m_className(className),
|
|
m_func(d),
|
|
m_desc(getFollyDynamicDefaultString(d, "desc", "")),
|
|
m_returnRef(d.getDefault("ref", "false") == "true"),
|
|
m_returnKindOf(KindOfNull),
|
|
m_returnCppType("void"),
|
|
m_returnPhpType("void"),
|
|
m_minNumParams(0),
|
|
m_numTypeChecks(0) {
|
|
auto returnIt = d.find("return");
|
|
if (returnIt != d.items().end()) {
|
|
auto retNode = returnIt->second;
|
|
auto typeIt = retNode.find("type");
|
|
if (typeIt != retNode.items().end()) {
|
|
auto type = typeIt->second;
|
|
if ((type.isString()) && (type != "void") && (type != "null")) {
|
|
m_returnKindOf = m_returnRef ? KindOfRef : kindOfFromDynamic(type);
|
|
m_returnCppType = typeString(type, true);
|
|
m_returnPhpType = phpTypeFromDataType(m_returnKindOf);
|
|
}
|
|
}
|
|
m_returnDesc = getFollyDynamicDefaultString(retNode, "desc", "");
|
|
}
|
|
|
|
auto args = d.find("args");
|
|
if (args == d.items().end() || !args->second.isArray()) {
|
|
throw std::logic_error(
|
|
folly::format("'{0}' must have an array field 'args'", name()).str()
|
|
);
|
|
}
|
|
auto ret = d.find("return");
|
|
if (ret == d.items().end() || !ret->second.isObject() ||
|
|
ret->second.find("type") == ret->second.items().end()) {
|
|
throw std::logic_error(
|
|
folly::format("'{0}' must have an array field 'return', which must have "
|
|
"a string field 'type'", name()).str()
|
|
);
|
|
}
|
|
|
|
bool magic = isMagicMethod();
|
|
|
|
m_flags = parseFlags(m_func["flags"]);
|
|
|
|
ParamMode paramMode = (m_flags & ZendParamMode) ?
|
|
ParamMode::Zend : ParamMode::CoerceAndCall;
|
|
|
|
for (auto &p : args->second) {
|
|
PhpParam param(p, magic, paramMode);
|
|
m_params.push_back(param);
|
|
if (!param.hasDefault()) {
|
|
++m_minNumParams;
|
|
}
|
|
if (param.isCheckedType()) {
|
|
++m_numTypeChecks;
|
|
}
|
|
}
|
|
}
|
|
|
|
fbstring PhpFunc::getCppSig() const {
|
|
std::ostringstream out;
|
|
|
|
fbstring nm = name();
|
|
fbstring lowername = nm;
|
|
std::transform(nm.begin(), nm.end(), lowername.begin(),
|
|
std::ptr_fun<int, int>(std::tolower));
|
|
|
|
if (!isMethod()) {
|
|
out << "HPHP::f_" << lowername << "(";
|
|
} else {
|
|
if (isStatic()) {
|
|
out << "HPHP::c_" << className() << "::ti_" << lowername << "(";
|
|
} else {
|
|
out << "HPHP::c_" << className() << "::t_" << lowername << "(";
|
|
}
|
|
}
|
|
|
|
bool firstParam = true;
|
|
if (isVarArgs()) {
|
|
if (!firstParam) {
|
|
out << ", ";
|
|
}
|
|
out << "int";
|
|
firstParam = false;
|
|
}
|
|
|
|
for (auto const& param : m_params) {
|
|
if (!firstParam) {
|
|
out << ", ";
|
|
}
|
|
out << param.getCppType();
|
|
firstParam = false;
|
|
}
|
|
|
|
if (isVarArgs()) {
|
|
assert(!firstParam);
|
|
out << ", HPHP::Array const&";
|
|
}
|
|
|
|
out << ")";
|
|
return out.str();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// PhpProp
|
|
|
|
PhpProp::PhpProp(const folly::dynamic& d, fbstring cls) :
|
|
m_name(d["name"].asString()),
|
|
m_className(cls),
|
|
m_prop(d),
|
|
m_flags(parseFlags(m_prop["flags"])),
|
|
m_kindOf(kindOfFromDynamic(m_prop["type"])) {
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// PhpClass
|
|
|
|
PhpClass::PhpClass(const folly::dynamic &c) :
|
|
m_class(c),
|
|
m_name(c["name"].asString()),
|
|
m_flags(parseFlags(m_class["flags"])),
|
|
m_desc(getFollyDynamicDefaultString(c, "desc", "")) {
|
|
|
|
auto ifacesIt = m_class.find("ifaces");
|
|
if (ifacesIt != m_class.items().end()) {
|
|
auto ifaces = ifacesIt->second;
|
|
if (!ifaces.isArray()) {
|
|
throw std::logic_error(
|
|
folly::format("Class {0}.ifaces field must be an array", m_name).str()
|
|
);
|
|
}
|
|
for (auto &interface : ifaces) {
|
|
m_ifaces.push_back(interface.asString());
|
|
}
|
|
}
|
|
|
|
for (auto const& f : c["funcs"]) {
|
|
PhpFunc func(f, m_name);
|
|
m_methods.push_back(func);
|
|
}
|
|
|
|
if (c.find("consts") != c.items().end()) {
|
|
for (auto const& cns : c["consts"]) {
|
|
PhpConst cons(cns, m_name);
|
|
m_constants.push_back(cons);
|
|
}
|
|
}
|
|
|
|
if (c.find("properties") != c.items().end()) {
|
|
for (auto const& prp : c["properties"]) {
|
|
PhpProp prop(prp, m_name);
|
|
m_properties.push_back(prop);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void parseIDL(const char* idlFilePath,
|
|
fbvector<PhpFunc>& funcVec,
|
|
fbvector<PhpClass>& classVec,
|
|
fbvector<PhpConst>& constVec,
|
|
fbvector<PhpExtension>& extVec) {
|
|
std::ostringstream jsonString;
|
|
std::ifstream infile(idlFilePath);
|
|
infile >> jsonString.rdbuf();
|
|
|
|
auto parsed = folly::parseJson(jsonString.str());
|
|
|
|
for (auto const& f : parsed["funcs"]) {
|
|
PhpFunc func(f, "");
|
|
funcVec.push_back(func);
|
|
}
|
|
for (auto const& c : parsed["classes"]) {
|
|
PhpClass klass(c);
|
|
classVec.push_back(klass);
|
|
}
|
|
for (auto const& c : parsed["consts"]) {
|
|
PhpConst cns(c);
|
|
constVec.push_back(cns);
|
|
}
|
|
auto it = parsed.find("extension");
|
|
if (it != parsed.items().end()) {
|
|
PhpExtension ext(it->second);
|
|
extVec.push_back(ext);
|
|
}
|
|
}
|
|
|
|
void parseIDL(const char* idlFilePath,
|
|
fbvector<PhpFunc>& funcVec,
|
|
fbvector<PhpClass>& classVec) {
|
|
fbvector<PhpConst> consts; // dummy
|
|
fbvector<PhpExtension> exts; // dummy
|
|
parseIDL(idlFilePath, funcVec, classVec, consts, exts);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
}} // namespace HPHP::IDL
|