9b6bf426b9
Also rename array_inline.h to array_data-inl.h
706 linhas
20 KiB
C++
706 linhas
20 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 <cxxabi.h>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
|
|
#include "folly/FBString.h"
|
|
#include "folly/Format.h"
|
|
#include "folly/ScopeGuard.h"
|
|
|
|
#include "hphp/tools/bootstrap/idl.h"
|
|
|
|
using folly::fbstring;
|
|
using namespace HPHP::IDL;
|
|
using namespace HPHP;
|
|
|
|
std::unordered_map<fbstring, const PhpFunc*> g_mangleMap;
|
|
std::unordered_map<fbstring, const PhpClass*> g_classMap;
|
|
|
|
// Functions with return types that don't fit in registers are handled
|
|
// differently on ARM. Instead of using a pointer-to-return-value-space hidden
|
|
// first parameter like on x64, the pointer is passed in a register reserved for
|
|
// this purpose, not part of the normal argument sequence.
|
|
bool g_armMode = false;
|
|
|
|
constexpr char* g_allIncludes = R"(
|
|
#include "hphp/runtime/ext_hhvm/ext_hhvm.h"
|
|
#include "hphp/runtime/base/builtin_functions.h"
|
|
#include "hphp/runtime/base/array_init.h"
|
|
#include "hphp/runtime/ext/ext.h"
|
|
#include "hphp/runtime/vm/class.h"
|
|
#include "hphp/runtime/vm/runtime.h"
|
|
#include <exception>
|
|
)";
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Code emission helpers -- leaf functions
|
|
|
|
void emitCtorHelper(const fbstring& className, std::ostream& out) {
|
|
out << folly::format(
|
|
R"(
|
|
ObjectData* new_{0:s}_Instance(HPHP::Class* cls) {{
|
|
size_t nProps = cls->numDeclProperties();
|
|
size_t builtinPropSize = sizeof(c_{0:s}) - sizeof(ObjectData);
|
|
size_t size = ObjectData::sizeForNProps(nProps) + builtinPropSize;
|
|
ObjectData *inst = (ObjectData*)ALLOCOBJSZ(size);
|
|
new ((void *)inst) c_{0:s}(cls);
|
|
return inst;
|
|
}})",
|
|
className) << "\n\n";
|
|
}
|
|
|
|
/*
|
|
* Emits a declaration that corresponds to the f_* functions, but with types and
|
|
* signatures adjusted to reflect the underlying C++ ABI.
|
|
*/
|
|
void emitRemappedFuncDecl(const PhpFunc& func,
|
|
const fbstring& mangled,
|
|
const fbstring& prefix,
|
|
std::ostream& out) {
|
|
int returnKindOf = func.returnKindOf();
|
|
bool indirectReturn = func.isIndirectReturn();
|
|
if (indirectReturn) {
|
|
if (returnKindOf == KindOfAny) {
|
|
out << "TypedValue* ";
|
|
} else {
|
|
out << "Value* ";
|
|
}
|
|
} else {
|
|
out << func.returnCppType() << ' ';
|
|
}
|
|
|
|
out << prefix << func.getUniqueName() << '(';
|
|
|
|
bool isFirstParam = true;
|
|
|
|
if (!g_armMode && indirectReturn) {
|
|
if (func.returnKindOf() == KindOfAny) {
|
|
out << "TypedValue* _rv";
|
|
} else {
|
|
out << "Value* _rv";
|
|
}
|
|
isFirstParam = false;
|
|
}
|
|
|
|
if (func.usesThis()) {
|
|
if (!isFirstParam) {
|
|
out << ", ";
|
|
}
|
|
out << "ObjectData* this_";
|
|
isFirstParam = false;
|
|
}
|
|
|
|
if (func.isVarArgs()) {
|
|
if (!isFirstParam) {
|
|
out << ", ";
|
|
}
|
|
out << "int64_t _argc";
|
|
isFirstParam = false;
|
|
}
|
|
|
|
for (auto const& param : func.params()) {
|
|
if (!isFirstParam) {
|
|
out << ", ";
|
|
}
|
|
auto kindof = param.kindOf();
|
|
if (param.isIndirectPass()) {
|
|
if (kindof == KindOfAny || kindof == KindOfRef) {
|
|
out << "TypedValue*";
|
|
} else {
|
|
out << "Value*";
|
|
}
|
|
} else {
|
|
out << param.getCppType();
|
|
}
|
|
out << ' ' << param.name();
|
|
isFirstParam = false;
|
|
}
|
|
|
|
if (func.isVarArgs()) {
|
|
assert(!isFirstParam);
|
|
out << ", Value* _argv";
|
|
}
|
|
|
|
out << ") asm(\""
|
|
<< mangled << "\");\n\n";
|
|
}
|
|
|
|
|
|
void emitCast(const PhpParam& param, int32_t index, std::ostream& out,
|
|
const char* ind, bool doCheck) {
|
|
if (doCheck) {
|
|
out << ind << "if (";
|
|
if (param.kindOf() == KindOfString) {
|
|
out << "!IS_STRING_TYPE((args-" << index << ")->m_type)";
|
|
} else {
|
|
out << "(args-" << index << ")->m_type != KindOf"
|
|
<< kindOfString(param.kindOf());
|
|
}
|
|
out << ") {\n";
|
|
ind -= 2;
|
|
}
|
|
|
|
|
|
if (param.getParamMode() == ParamMode::Zend) {
|
|
out << ind << "if (!"
|
|
<< "tvCoerceParamTo" << kindOfString(param.kindOf())
|
|
<< "InPlace(args-" << index << ")) {\n"
|
|
<< ind << " raise_param_type_warning(__func__, " << index << " + 1, "
|
|
<< "KindOf" << kindOfString(param.kindOf()) << ", "
|
|
<< "(args-" << index << ")->m_type);\n"
|
|
<< ind << " rv->m_type = KindOfUninit;\n"
|
|
<< ind << " return;\n"
|
|
<< ind << "}\n";
|
|
} else if (param.kindOf() != KindOfAny) {
|
|
out << ind << "tvCastTo" << kindOfString(param.kindOf())
|
|
<< "InPlace(args-" << index << ");\n";
|
|
}
|
|
|
|
if (doCheck) {
|
|
ind += 2;
|
|
out << ind << "}\n";
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Emits an expression which will check the types of arguments on the VM stack.
|
|
*/
|
|
void emitTypechecks(const PhpFunc& func, std::ostream& out, const char* ind) {
|
|
bool isFirstParam = true;
|
|
for (int k = func.numParams() - 1; k >= 0; --k) {
|
|
auto const& param = func.param(k);
|
|
auto kindof = param.kindOf();
|
|
if (kindof == KindOfAny || kindof == KindOfRef) {
|
|
continue;
|
|
}
|
|
|
|
if (!isFirstParam) {
|
|
out << " &&\n" << ind << " ";
|
|
}
|
|
isFirstParam = false;
|
|
|
|
bool isOptional = (k >= func.minNumParams());
|
|
if (isOptional) {
|
|
out << "(count <= " << k << " || ";
|
|
}
|
|
if (kindof == KindOfString) {
|
|
out << "IS_STRING_TYPE((args - " << k << ")->m_type)";
|
|
} else {
|
|
out << "(args - " << k << ")->m_type == KindOf" << kindOfString(kindof);
|
|
}
|
|
|
|
if (isOptional) {
|
|
out << ")";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Marshals varargs into an array.
|
|
*/
|
|
void emitBuildExtraArgs(const PhpFunc& func, std::ostream& out,
|
|
const char* ind) {
|
|
out << folly::format(
|
|
R"(
|
|
{0}Array extraArgs;
|
|
{0}{{
|
|
{0} ArrayInit ai(count-{1});
|
|
{0} for (int32_t i = {1}; i < count; ++i) {{
|
|
{0} TypedValue* extraArg = ar->getExtraArg(i-{1});
|
|
{0} if (tvIsStronglyBound(extraArg)) {{
|
|
{0} ai.setRef(i-{1}, tvAsVariant(extraArg));
|
|
{0} }} else {{
|
|
{0} ai.set(i-{1}, tvAsVariant(extraArg));
|
|
{0} }}
|
|
{0} }}
|
|
{0} extraArgs = ai.create();
|
|
{0}}}
|
|
)",
|
|
ind,
|
|
func.numParams()
|
|
);
|
|
}
|
|
|
|
|
|
void emitCallExpression(const PhpFunc& func, const fbstring& prefix,
|
|
std::ostream& out) {
|
|
out << prefix << func.getUniqueName() << '(';
|
|
|
|
bool isFirstParam = true;
|
|
if (!g_armMode && func.isIndirectReturn()) {
|
|
isFirstParam = false;
|
|
if (func.returnKindOf() == KindOfAny) {
|
|
out << "rv";
|
|
} else {
|
|
out << "&(rv->m_data)";
|
|
}
|
|
}
|
|
|
|
if (func.usesThis()) {
|
|
if (!isFirstParam) {
|
|
out << ", ";
|
|
}
|
|
isFirstParam = false;
|
|
out << "(this_)";
|
|
}
|
|
|
|
if (func.isVarArgs()) {
|
|
if (!isFirstParam) {
|
|
out << ", ";
|
|
}
|
|
isFirstParam = false;
|
|
out << "count";
|
|
}
|
|
|
|
for (auto k = 0; k < func.numParams(); ++k) {
|
|
auto const& param = func.param(k);
|
|
if (!isFirstParam) {
|
|
out << ", ";
|
|
}
|
|
isFirstParam = false;
|
|
|
|
if (param.hasDefault()) {
|
|
out << "(count > " << k << ") ? ";
|
|
}
|
|
|
|
if (param.isIndirectPass()) {
|
|
auto kindof = param.kindOf();
|
|
if (kindof == KindOfAny || kindof == KindOfRef) {
|
|
out << "(args-" << k << ')';
|
|
} else {
|
|
out << "&args[-" << k << "].m_data";
|
|
}
|
|
} else {
|
|
if (param.kindOf() == KindOfDouble) {
|
|
out << "(args[-" << k << "].m_data.dbl)";
|
|
} else {
|
|
out << '(' << param.getCppType() << ")(args[-" << k << "].m_data.num)";
|
|
}
|
|
}
|
|
|
|
if (param.hasDefault()) {
|
|
out << " : ";
|
|
auto kindof = param.kindOf();
|
|
if (param.defValueNeedsVariable()) {
|
|
if (kindof == KindOfAny || kindof == KindOfRef) {
|
|
out << "(TypedValue*)(&defVal" << k << ')';
|
|
} else {
|
|
out << "(Value*)(&defVal" << k << ')';
|
|
}
|
|
} else if (param.isIndirectPass()) {
|
|
if (kindof == KindOfAny || kindof == KindOfRef) {
|
|
out << "(TypedValue*)(&" << param.getDefault() << ')';
|
|
} else {
|
|
out << "(Value*)(&" << param.getDefault() << ')';
|
|
}
|
|
} else {
|
|
out << '(' << param.getCppType() << ")(" << param.getDefault() << ')';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (func.isVarArgs()) {
|
|
assert(!isFirstParam);
|
|
out << ", (Value*)(&extraArgs)";
|
|
}
|
|
out << ')';
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Code emission helpers -- non-leaf functions
|
|
|
|
/*
|
|
* Marshals varargs into an array if necessary, emits variables for default
|
|
* values if necessary, and emits the call itself.
|
|
*/
|
|
void emitExtCall(const PhpFunc& func, std::ostream& out, const char* ind) {
|
|
fbstring call_prefix;
|
|
fbstring call_suffix;
|
|
|
|
// Set up the type of the return value, and emit post-call code to normalize
|
|
// return types and values
|
|
auto returnKindOf = func.returnKindOf();
|
|
if (returnKindOf == KindOfBoolean) {
|
|
out << ind << "rv->m_type = KindOfBoolean;\n";
|
|
call_prefix = "rv->m_data.num = (";
|
|
call_suffix = ") ? 1LL : 0LL;\n";
|
|
} else if (returnKindOf == KindOfInt64) {
|
|
out << ind << "rv->m_type = KindOfInt64;\n";
|
|
call_prefix = "rv->m_data.num = (int64_t)";
|
|
call_suffix = ";\n";
|
|
} else if (returnKindOf == KindOfDouble) {
|
|
out << ind << "rv->m_type = KindOfDouble;\n";
|
|
call_prefix = "rv->m_data.dbl = ";
|
|
call_suffix = ";\n";
|
|
} else if (returnKindOf == KindOfInvalid || returnKindOf == KindOfNull) {
|
|
out << ind << "rv->m_type = KindOfNull;\n";
|
|
call_suffix = ";\n";
|
|
} else if (returnKindOf == KindOfString) {
|
|
out << ind << "rv->m_type = KindOfString;\n";
|
|
call_suffix = (fbstring(";\n") + ind +
|
|
"if (rv->m_data.num == 0LL) rv->m_type = KindOfNull;\n");
|
|
} else if (returnKindOf == KindOfArray) {
|
|
out << ind << "rv->m_type = KindOfArray;\n";
|
|
call_suffix = (fbstring(";\n") + ind +
|
|
"if (rv->m_data.num == 0LL) rv->m_type = KindOfNull;\n");
|
|
} else if (returnKindOf == KindOfObject) {
|
|
out << ind << "rv->m_type = KindOfObject;\n";
|
|
call_suffix = (fbstring(";\n") + ind +
|
|
"if (rv->m_data.num == 0LL) rv->m_type = KindOfNull;\n");
|
|
} else {
|
|
call_suffix = (fbstring(";\n") + ind +
|
|
"if (rv->m_type == KindOfUninit) "
|
|
"rv->m_type = KindOfNull;\n");
|
|
}
|
|
|
|
if (func.isVarArgs()) {
|
|
emitBuildExtraArgs(func, out, ind);
|
|
}
|
|
|
|
// If any default values need variables (because they have nontrivial values),
|
|
// declare and initialize those
|
|
for (auto k = 0; k < func.numParams(); ++k) {
|
|
auto const& param = func.param(k);
|
|
if (param.defValueNeedsVariable()) {
|
|
DataType kindof = param.kindOf();
|
|
out << ind << param.getStrippedCppType() << " defVal" << k;
|
|
fbstring defVal = param.getDefault();
|
|
if (kindof != KindOfAny ||
|
|
(defVal != "null" && defVal != "null_variant")) {
|
|
out << " = ";
|
|
std::string nullToType =
|
|
kindof == KindOfArray ? ".toArray()" :
|
|
kindof == KindOfString ? ".toString()" :
|
|
kindof == KindOfObject ? ".toObject()" :
|
|
kindof == KindOfRef ? "" :
|
|
"icantconvertthisfromnull";
|
|
if (defVal == "null_variant") defVal += nullToType;
|
|
out << (defVal == "null" ? "uninit_null()" + nullToType : defVal);
|
|
}
|
|
out << ";\n";
|
|
}
|
|
}
|
|
|
|
// Put the return-value-space pointer into x8
|
|
if (g_armMode) {
|
|
out << ind << "asm volatile (\"mov x8, %0\\n\" : : \"r\"(";
|
|
if (func.returnKindOf() == KindOfAny) {
|
|
out << "rv";
|
|
} else {
|
|
out << "&(rv->m_data)";
|
|
}
|
|
out << ") : \"x8\");\n";
|
|
}
|
|
|
|
out << ind << call_prefix;
|
|
emitCallExpression(func, func.isMethod() ? "th_" : "fh_", out);
|
|
out << call_suffix;
|
|
}
|
|
|
|
void emitCasts(const PhpFunc& func, std::ostream& out, const char* ind) {
|
|
assert(func.numTypeChecks() > 0);
|
|
|
|
if (func.numTypeChecks() == 1) {
|
|
for (auto i = func.numParams() - 1; i >= 0; --i) {
|
|
if (func.param(i).isCheckedType()) {
|
|
emitCast(func.param(i), i, out, ind, false);
|
|
return;
|
|
}
|
|
}
|
|
assert(false); // not reached
|
|
}
|
|
|
|
if (func.minNumParams() != func.numParams()) {
|
|
out << ind << "switch (count) {\n";
|
|
for (auto i = func.numParams() - 1; i >= func.minNumParams(); --i) {
|
|
auto const& param = func.param(i);
|
|
if (i == func.numParams() - 1) {
|
|
out << ind << "default: // count >= " << func.numParams() << '\n';
|
|
} else {
|
|
out << ind << "case " << (i + 1) << ":\n";
|
|
}
|
|
if (param.isCheckedType()) {
|
|
emitCast(param, i, out, ind - 2, true);
|
|
}
|
|
}
|
|
out << ind << "case " << func.minNumParams() << ":\n";
|
|
out << ind << " break;\n";
|
|
out << ind << "}\n";
|
|
}
|
|
for (auto i = func.minNumParams() - 1; i >= 0; --i) {
|
|
auto const& param = func.param(i);
|
|
if (param.isCheckedType()) {
|
|
emitCast(param, i, out, ind, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Emits the fg1_ helper, which assumes that the arg count is acceptable, but at
|
|
* least one typecheck has failed. It will cast arguments to the appropriate
|
|
* types and then call the fh_ alias.
|
|
*/
|
|
void emitSlowPathHelper(const PhpFunc& func, const fbstring& prefix,
|
|
std::ostream& out) {
|
|
out << "void " << prefix << func.getUniqueName()
|
|
<< "(TypedValue* rv, ActRec* ar, int32_t count";
|
|
if (func.usesThis()) {
|
|
out << ", ObjectData* this_";
|
|
}
|
|
out << ") __attribute__((noinline,cold));\n";
|
|
|
|
out << "void " << prefix << func.getUniqueName()
|
|
<< "(TypedValue* rv, ActRec* ar, int32_t count";
|
|
if (func.usesThis()) {
|
|
out << ", ObjectData* this_";
|
|
}
|
|
out << ") {\n";
|
|
|
|
const char* eightSpaces = " ";
|
|
const char* ind = eightSpaces + 6;
|
|
|
|
out << ind << "TypedValue* args UNUSED = ((TypedValue*)ar) - 1;\n";
|
|
|
|
emitCasts(func, out, ind);
|
|
emitExtCall(func, out, ind);
|
|
|
|
out << "}\n\n";
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Top level
|
|
|
|
/*
|
|
* Called for each line on stdin. Looks up the symbol's characteristics in the
|
|
* IDL, and emits up to three things:
|
|
*
|
|
* - An fh_* declaration, which is the f_* signature with ABI exposed.
|
|
* - [Maybe] an fg1_* stub which does type casting of arguments, then calls fh_.
|
|
* - An fg_ stub which checks arg counts and types, then calls fh_ or fg1_.
|
|
*/
|
|
void processSymbol(const fbstring& symbol, std::ostream& header,
|
|
std::ostream& cpp) {
|
|
int status;
|
|
const char *mangledSymbol = symbol.c_str();
|
|
#ifdef __APPLE__
|
|
mangledSymbol++;
|
|
#endif
|
|
auto demangled = abi::__cxa_demangle(mangledSymbol, nullptr, 0, &status);
|
|
SCOPE_EXIT { free(demangled); };
|
|
|
|
if (status != 0) {
|
|
return;
|
|
}
|
|
|
|
auto idlIt = g_mangleMap.find(demangled);
|
|
if (idlIt == g_mangleMap.end()) {
|
|
// A symbol that doesn't correspond to anything in the IDL.
|
|
return;
|
|
}
|
|
|
|
auto& func = *idlIt->second;
|
|
bool isMethod = func.isMethod();
|
|
|
|
auto classIt = g_classMap.find(func.className());
|
|
if (func.isCtor() && classIt != g_classMap.end()) {
|
|
auto& klass = *classIt->second;
|
|
|
|
if (!(klass.flags() & IsCppAbstract)) {
|
|
emitCtorHelper(klass.name(), cpp);
|
|
}
|
|
if (klass.flags() & NoDefaultSweep) {
|
|
cpp << "IMPLEMENT_CLASS_NO_DEFAULT_SWEEP(" << klass.name() << ");\n";
|
|
} else {
|
|
cpp << "IMPLEMENT_CLASS(" << klass.name() << ");\n";
|
|
}
|
|
}
|
|
|
|
fbstring declPrefix = (isMethod ? "th_" : "fh_");
|
|
fbstring slowPathPrefix = (isMethod ? "tg1_" : "fg1_");
|
|
fbstring stubPrefix = (isMethod ? "tg_" : "fg_");
|
|
|
|
std::ostringstream decl;
|
|
emitRemappedFuncDecl(func, symbol, declPrefix, decl);
|
|
if (!isMethod) {
|
|
header << decl.str();
|
|
}
|
|
cpp << decl.str();
|
|
|
|
if (func.numTypeChecks() > 0) {
|
|
emitSlowPathHelper(func, slowPathPrefix, cpp);
|
|
}
|
|
|
|
// This is how we change the indentation level. So clever.
|
|
const char* eightSpaces = " ";
|
|
const char* in = eightSpaces + 6;
|
|
|
|
cpp << "TypedValue* " << stubPrefix << func.getUniqueName()
|
|
<< "(ActRec* ar) {\n";
|
|
cpp << in << "TypedValue rvSpace;\n";
|
|
cpp << in << "TypedValue* rv = &rvSpace;\n";
|
|
cpp << in << "int32_t count = ar->numArgs();\n";
|
|
cpp << in << "TypedValue* args UNUSED = ((TypedValue*)ar) - 1;\n";
|
|
|
|
if (func.usesThis()) {
|
|
cpp << in
|
|
<< "ObjectData* this_ = (ar->hasThis() ? ar->getThis() : nullptr);\n";
|
|
cpp << in << "if (this_) {\n";
|
|
in -= 2;
|
|
}
|
|
|
|
// Check the arg count
|
|
bool needArgMiscountClause = false;
|
|
if (func.isVarArgs()) {
|
|
if (func.minNumParams() > 0) {
|
|
cpp << in << "if (count >= " << func.minNumParams() << ") {\n";
|
|
needArgMiscountClause = true;
|
|
in -= 2;
|
|
}
|
|
} else {
|
|
if (func.minNumParams() == func.numParams()) {
|
|
cpp << in << "if (count == " << func.minNumParams() << ") {\n";
|
|
} else if (func.minNumParams() == 0) {
|
|
cpp << in << "if (count <= " << func.numParams() << ") {\n";
|
|
} else {
|
|
cpp << in << "if (count >= " << func.minNumParams()
|
|
<< " && count <= "<< func.numParams() << ") {\n";
|
|
}
|
|
needArgMiscountClause = true;
|
|
in -= 2;
|
|
}
|
|
|
|
// Count is OK. Check arg types
|
|
if (func.numTypeChecks() > 0) {
|
|
cpp << in << "if (";
|
|
emitTypechecks(func, cpp, in);
|
|
cpp << ") {\n";
|
|
in -= 2;
|
|
}
|
|
|
|
// Call the f_ function via the fh_ alias
|
|
emitExtCall(func, cpp, in);
|
|
|
|
// Deal with type mismatches: punt to fg1_
|
|
if (func.numTypeChecks() > 0) {
|
|
cpp << in + 2 << "} else {\n";
|
|
cpp << in << slowPathPrefix << func.getUniqueName() << "(rv, ar, count";
|
|
if (func.usesThis()) {
|
|
cpp << ", this_";
|
|
}
|
|
cpp << ");\n";
|
|
in += 2;
|
|
cpp << in << "}\n";
|
|
}
|
|
|
|
if (needArgMiscountClause) {
|
|
cpp << in + 2 << "} else {\n";
|
|
if (func.isVarArgs()) {
|
|
cpp << in << "throw_missing_arguments_nr(\"" << func.getPrettyName()
|
|
<< "\", " << func.minNumParams() << ", count, 1);\n";
|
|
} else {
|
|
if (func.minNumParams() == 0) {
|
|
cpp << in << "throw_toomany_arguments_nr(\"" << func.getPrettyName()
|
|
<< "\", " << func.numParams() << ", 1);\n";
|
|
} else {
|
|
cpp << in << "throw_wrong_arguments_nr(\"" << func.getPrettyName()
|
|
<< "\", count, " << func.minNumParams() << ", "
|
|
<< func.numParams() << ", 1);\n";
|
|
}
|
|
}
|
|
cpp << in << "rv->m_data.num = 0LL;\n";
|
|
cpp << in << "rv->m_type = KindOfNull;\n";
|
|
in += 2;
|
|
cpp << in << "}\n";
|
|
}
|
|
|
|
if (func.isMethod() && !func.isStatic()) {
|
|
cpp << in + 2 << "} else {\n";
|
|
cpp << in << "throw_instance_method_fatal(\"" << func.className()
|
|
<< "::" << func.name() << "\");\n";
|
|
in += 2;
|
|
cpp << in << "}\n";
|
|
}
|
|
|
|
auto numLocals = func.numParams();
|
|
auto frameFree =
|
|
func.usesThis() ? "frame_free_locals_inl" : "frame_free_locals_no_this_inl";
|
|
cpp << in << frameFree << "(ar, " << numLocals << ");\n";
|
|
cpp << in << "memcpy(&ar->m_r, rv, sizeof(TypedValue));\n";
|
|
cpp << in << "return &ar->m_r;\n";
|
|
cpp << "}\n\n";
|
|
}
|
|
|
|
int main(int argc, const char* argv[]) {
|
|
if (argc < 5) {
|
|
std::cout << "Usage: " << argv[0]
|
|
<< " <x64|arm> <output .h> <output .cpp> <*.idl.json>...\n"
|
|
<< "Pipe mangled C++ symbols to stdin.\n";
|
|
return 0;
|
|
}
|
|
|
|
g_armMode = (strcmp(argv[1], "arm") == 0);
|
|
|
|
std::ofstream header(argv[2]);
|
|
std::ofstream cpp(argv[3]);
|
|
|
|
fbvector<PhpFunc> funcs;
|
|
fbvector<PhpClass> classes;
|
|
|
|
for (auto i = 4; i < argc; ++i) {
|
|
try {
|
|
parseIDL(argv[i], funcs, classes);
|
|
} catch (const std::exception& exc) {
|
|
std::cerr << argv[i] << ": " << exc.what() << "\n";
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
for (auto const& func : funcs) {
|
|
g_mangleMap[func.getCppSig()] = &func;
|
|
}
|
|
for (auto const& klass : classes) {
|
|
g_classMap[klass.name()] = &klass;
|
|
for (auto const& func : klass.methods()) {
|
|
g_mangleMap[func.getCppSig()] = &func;
|
|
}
|
|
}
|
|
|
|
header << "namespace HPHP {\n\n";
|
|
cpp << g_allIncludes << "\n";
|
|
cpp << "namespace HPHP {\n\n";
|
|
|
|
std::string line;
|
|
while (std::getline(std::cin, line)) {
|
|
processSymbol(line, header, cpp);
|
|
}
|
|
|
|
header << "} // namespace HPHP\n";
|
|
cpp << "} // namespace HPHP\n";
|
|
|
|
return 0;
|
|
}
|