8c6d77deef
Instead of having the body of the closure be in the ##__invoke()## on the ##Closure## class, instead we make an anonymous function on the real class and put the body there. The signature for this function is: function methodForClosure$1234($arg1, $arg2, ..., $use1, $use2, ...) and then ##__invoke## now just takes all the params that were passed to it, puts them as the first args to the anonymous function, then takes all the use variables it had saved up and passed them in as the next params. I tried to not have an ##__invoke## at all, but I ended up basically doing the same parameter and use var repacking in iopFCall (and would have had to do it in x86 code too). I opted for doing the rejiggering in bytecode. If I did it in raw PHP I think it would have been much slower with many ##func_get_args()## and array operations.
1966 linhas
66 KiB
C++
1966 linhas
66 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- 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 <runtime/base/complex_types.h>
|
|
|
|
#include <compiler/statement/method_statement.h>
|
|
#include <compiler/statement/return_statement.h>
|
|
#include <compiler/statement/statement_list.h>
|
|
#include <compiler/statement/try_statement.h>
|
|
#include <compiler/statement/label_statement.h>
|
|
#include <compiler/statement/goto_statement.h>
|
|
#include <compiler/statement/exp_statement.h>
|
|
#include <compiler/statement/switch_statement.h>
|
|
#include <compiler/statement/case_statement.h>
|
|
#include <compiler/statement/catch_statement.h>
|
|
|
|
#include <compiler/expression/modifier_expression.h>
|
|
#include <compiler/expression/expression_list.h>
|
|
#include <compiler/expression/constant_expression.h>
|
|
#include <compiler/expression/parameter_expression.h>
|
|
#include <compiler/expression/assignment_expression.h>
|
|
#include <compiler/expression/simple_variable.h>
|
|
|
|
#include <compiler/analysis/ast_walker.h>
|
|
#include <compiler/analysis/analysis_result.h>
|
|
#include <compiler/analysis/code_error.h>
|
|
#include <compiler/analysis/file_scope.h>
|
|
#include <compiler/analysis/variable_table.h>
|
|
#include <compiler/analysis/class_scope.h>
|
|
#include <compiler/analysis/function_scope.h>
|
|
|
|
#include <compiler/option.h>
|
|
#include <compiler/builtin_symbols.h>
|
|
#include <compiler/analysis/alias_manager.h>
|
|
|
|
#include <util/parser/parser.h>
|
|
#include <util/util.h>
|
|
|
|
using namespace HPHP;
|
|
using std::map;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// constructors/destructors
|
|
|
|
MethodStatement::MethodStatement
|
|
(STATEMENT_CONSTRUCTOR_BASE_PARAMETERS,
|
|
ModifierExpressionPtr modifiers, bool ref, const string &name,
|
|
ExpressionListPtr params, StatementListPtr stmt, int attr,
|
|
const string &docComment, ExpressionListPtr attrList,
|
|
bool method /* = true */)
|
|
: Statement(STATEMENT_CONSTRUCTOR_BASE_PARAMETER_VALUES),
|
|
m_method(method), m_ref(ref), m_attribute(attr),
|
|
m_cppLength(-1), m_modifiers(modifiers),
|
|
m_originalName(name), m_params(params), m_stmt(stmt),
|
|
m_docComment(docComment), m_attrList(attrList) {
|
|
m_name = Util::toLower(name);
|
|
}
|
|
|
|
MethodStatement::MethodStatement
|
|
(STATEMENT_CONSTRUCTOR_PARAMETERS,
|
|
ModifierExpressionPtr modifiers, bool ref, const string &name,
|
|
ExpressionListPtr params, StatementListPtr stmt, int attr,
|
|
const string &docComment, ExpressionListPtr attrList,
|
|
bool method /* = true */)
|
|
: Statement(STATEMENT_CONSTRUCTOR_PARAMETER_VALUES(MethodStatement)),
|
|
m_method(method), m_ref(ref), m_attribute(attr), m_cppLength(-1),
|
|
m_modifiers(modifiers), m_originalName(name),
|
|
m_params(params), m_stmt(stmt),
|
|
m_docComment(docComment), m_attrList(attrList) {
|
|
m_name = Util::toLower(name);
|
|
}
|
|
|
|
StatementPtr MethodStatement::clone() {
|
|
MethodStatementPtr stmt(new MethodStatement(*this));
|
|
stmt->m_stmt = Clone(m_stmt);
|
|
stmt->m_params = Clone(m_params);
|
|
stmt->m_modifiers = Clone(m_modifiers);
|
|
return stmt;
|
|
}
|
|
|
|
string MethodStatement::getFullName() const {
|
|
if (m_className.empty()) return m_name;
|
|
return m_className + "::" + m_name;
|
|
}
|
|
|
|
string MethodStatement::getOriginalFullName() const {
|
|
if (m_originalClassName.empty()) return m_originalName;
|
|
return m_originalClassName + "::" + m_originalName;
|
|
}
|
|
|
|
string MethodStatement::getOriginalFullNameForInjection() const {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
string injectionName;
|
|
if (getGeneratorFunc()) {
|
|
injectionName = funcScope->isClosureGenerator() ?
|
|
m_originalName :
|
|
m_originalName + "{continuation}";
|
|
} else if (getOrigGeneratorFunc()) {
|
|
bool needsOrig = !funcScope->getOrigGenFS()->isClosure();
|
|
injectionName = needsOrig ?
|
|
getOrigGeneratorFunc()->getOriginalName() :
|
|
m_originalName;
|
|
} else {
|
|
injectionName = m_originalName;
|
|
}
|
|
return m_originalClassName.empty() ?
|
|
injectionName :
|
|
m_originalClassName + "::" + injectionName;
|
|
}
|
|
|
|
bool MethodStatement::isRef(int index /* = -1 */) const {
|
|
if (index == -1) return m_ref;
|
|
assert(index >= 0 && index < m_params->getCount());
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[index]);
|
|
return param->isRef();
|
|
}
|
|
|
|
int MethodStatement::getRecursiveCount() const {
|
|
return m_stmt ? m_stmt->getRecursiveCount() : 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// parser functions
|
|
|
|
FunctionScopePtr MethodStatement::onInitialParse(AnalysisResultConstPtr ar,
|
|
FileScopePtr fs) {
|
|
int minParam, maxParam;
|
|
ConstructPtr self = shared_from_this();
|
|
minParam = maxParam = 0;
|
|
bool hasRef = false;
|
|
if (m_params) {
|
|
std::set<string> names, allDeclNames;
|
|
int i = 0;
|
|
maxParam = m_params->getCount();
|
|
for (i = maxParam; i--; ) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
if (param->isRef()) hasRef = true;
|
|
if (!param->isOptional()) {
|
|
if (!minParam) minParam = i + 1;
|
|
} else if (minParam && !param->hasTypeHint()) {
|
|
Compiler::Error(Compiler::RequiredAfterOptionalParam, param);
|
|
}
|
|
allDeclNames.insert(param->getName());
|
|
}
|
|
|
|
for (i = maxParam-1; i >= 0; i--) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
if (names.find(param->getName()) != names.end()) {
|
|
Compiler::Error(Compiler::RedundantParameter, param);
|
|
for (int j = 0; j < 1000; j++) {
|
|
string name = param->getName() + lexical_cast<string>(j);
|
|
if (names.find(name) == names.end() &&
|
|
allDeclNames.find(name) == allDeclNames.end()) {
|
|
param->rename(name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
names.insert(param->getName());
|
|
}
|
|
}
|
|
|
|
if (hasRef || m_ref) {
|
|
m_attribute |= FileScope::ContainsReference;
|
|
}
|
|
|
|
vector<UserAttributePtr> attrs;
|
|
if (m_attrList) {
|
|
for (int i = 0; i < m_attrList->getCount(); ++i) {
|
|
UserAttributePtr a =
|
|
dynamic_pointer_cast<UserAttribute>((*m_attrList)[i]);
|
|
attrs.push_back(a);
|
|
}
|
|
}
|
|
|
|
StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this());
|
|
FunctionScopePtr funcScope
|
|
(new FunctionScope(ar, m_method, m_name, stmt, m_ref, minParam, maxParam,
|
|
m_modifiers, m_attribute, m_docComment, fs, attrs));
|
|
if (!m_stmt) {
|
|
funcScope->setVirtual();
|
|
}
|
|
setBlockScope(funcScope);
|
|
|
|
funcScope->setParamCounts(ar, -1, -1);
|
|
return funcScope;
|
|
}
|
|
|
|
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
|
|
ClassScopePtr classScope) {
|
|
|
|
if (m_modifiers) {
|
|
if (classScope->isInterface()) {
|
|
if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
|
|
m_modifiers->isAbstract() || m_modifiers->isFinal()) {
|
|
m_modifiers->parseTimeFatal(
|
|
Compiler::InvalidAttribute,
|
|
"Access type for interface method %s::%s() must be omitted",
|
|
classScope->getOriginalName().c_str(), getOriginalName().c_str());
|
|
}
|
|
}
|
|
if (m_modifiers->isAbstract()) {
|
|
if (m_modifiers->isPrivate() || m_modifiers->isFinal()) {
|
|
m_modifiers->parseTimeFatal(
|
|
Compiler::InvalidAttribute,
|
|
"Cannot declare abstract method %s::%s() %s",
|
|
classScope->getOriginalName().c_str(),
|
|
getOriginalName().c_str(),
|
|
m_modifiers->isPrivate() ? "private" : "final");
|
|
}
|
|
if (!classScope->isInterface() && !classScope->isAbstract()) {
|
|
/* note that classScope->isAbstract() returns true for traits */
|
|
m_modifiers->parseTimeFatal(Compiler::InvalidAttribute,
|
|
"Class %s contains abstract method %s and "
|
|
"must therefore be declared abstract",
|
|
classScope->getOriginalName().c_str(),
|
|
getOriginalName().c_str());
|
|
}
|
|
if (getStmts()) {
|
|
parseTimeFatal(Compiler::InvalidAttribute,
|
|
"Abstract method %s::%s() cannot contain body",
|
|
classScope->getOriginalName().c_str(),
|
|
getOriginalName().c_str());
|
|
}
|
|
}
|
|
}
|
|
if ((!m_modifiers || !m_modifiers->isAbstract()) &&
|
|
!getStmts() && !classScope->isInterface()) {
|
|
parseTimeFatal(Compiler::InvalidAttribute,
|
|
"Non-abstract method %s::%s() must contain body",
|
|
classScope->getOriginalName().c_str(),
|
|
getOriginalName().c_str());
|
|
}
|
|
|
|
FunctionScopeRawPtr fs = getFunctionScope();
|
|
|
|
classScope->addFunction(ar, fs);
|
|
|
|
m_className = classScope->getName();
|
|
m_originalClassName = classScope->getOriginalName();
|
|
|
|
setSpecialMethod(classScope);
|
|
|
|
if (Option::DynamicInvokeFunctions.find(getFullName()) !=
|
|
Option::DynamicInvokeFunctions.end()) {
|
|
fs->setDynamicInvoke();
|
|
}
|
|
if (m_params) {
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
param->parseHandler(classScope);
|
|
}
|
|
}
|
|
FunctionScope::RecordFunctionInfo(m_name, fs);
|
|
}
|
|
|
|
void MethodStatement::fixupSelfAndParentTypehints(ClassScopePtr scope) {
|
|
if (m_params) {
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
param->fixupSelfAndParentTypehints(scope);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MethodStatement::setSpecialMethod(ClassScopePtr classScope) {
|
|
if (m_name.size() < 2 || m_name.substr(0,2) != "__") {
|
|
return;
|
|
}
|
|
int numArgs = -1;
|
|
bool isStatic = false;
|
|
if (m_name == "__construct") {
|
|
classScope->setAttribute(ClassScope::HasConstructor);
|
|
} else if (m_name == "__destruct") {
|
|
classScope->setAttribute(ClassScope::HasDestructor);
|
|
} else if (m_name == "__call") {
|
|
classScope->setAttribute(ClassScope::HasUnknownMethodHandler);
|
|
numArgs = 2;
|
|
} else if (m_name == "__get") {
|
|
classScope->setAttribute(ClassScope::HasUnknownPropGetter);
|
|
numArgs = 1;
|
|
} else if (m_name == "__set") {
|
|
classScope->setAttribute(ClassScope::HasUnknownPropSetter);
|
|
numArgs = 2;
|
|
} else if (m_name == "__isset") {
|
|
classScope->setAttribute(ClassScope::HasUnknownPropTester);
|
|
numArgs = 1;
|
|
} else if (m_name == "__unset") {
|
|
classScope->setAttribute(ClassScope::HasPropUnsetter);
|
|
numArgs = 1;
|
|
} else if (m_name == "__call") {
|
|
classScope->setAttribute(ClassScope::HasUnknownMethodHandler);
|
|
numArgs = 2;
|
|
} else if (m_name == "__callstatic") {
|
|
classScope->setAttribute(ClassScope::HasUnknownStaticMethodHandler);
|
|
numArgs = 2;
|
|
isStatic = true;
|
|
} else if (m_name == "__invoke") {
|
|
classScope->setAttribute(ClassScope::HasInvokeMethod);
|
|
} else if (m_name == "__tostring") {
|
|
numArgs = 0;
|
|
}
|
|
if (numArgs >= 0) {
|
|
// Fatal if the number of arguments is wrong
|
|
int n = m_params ? m_params->getCount() : 0;
|
|
if (numArgs != n) {
|
|
parseTimeFatal(Compiler::InvalidMagicMethod,
|
|
"Method %s::%s() must take exactly %d argument%s",
|
|
m_originalClassName.c_str(), m_originalName.c_str(),
|
|
numArgs, (numArgs == 1) ? "" : "s");
|
|
}
|
|
// Fatal if any arguments are pass by reference
|
|
if (m_params && hasRefParam()) {
|
|
parseTimeFatal(Compiler::InvalidMagicMethod,
|
|
"Method %s::%s() cannot take arguments by reference",
|
|
m_originalClassName.c_str(), m_originalName.c_str());
|
|
}
|
|
// Fatal if protected/private or if the staticness is wrong
|
|
if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
|
|
m_modifiers->isStatic() != isStatic) {
|
|
parseTimeFatal(Compiler::InvalidMagicMethod,
|
|
"Method %s::%s() must have public visibility and %sbe static",
|
|
m_originalClassName.c_str(), m_originalName.c_str(),
|
|
isStatic ? "" : "cannot ");
|
|
}
|
|
}
|
|
}
|
|
|
|
void MethodStatement::addTraitMethodToScope(AnalysisResultConstPtr ar,
|
|
ClassScopePtr classScope) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
classScope->addFunction(ar, funcScope);
|
|
setSpecialMethod(classScope);
|
|
FunctionScope::RecordFunctionInfo(m_name, funcScope);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// static analysis functions
|
|
|
|
int MethodStatement::getLocalEffects() const {
|
|
if (m_method) return NoEffect;
|
|
FunctionScopeRawPtr scope = getFunctionScope();
|
|
return scope->isVolatile() ? OtherEffect | CanThrow : NoEffect;
|
|
}
|
|
|
|
void MethodStatement::addParamRTTI(AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr func = getFunctionScope();
|
|
|
|
VariableTablePtr variables = func->getVariables();
|
|
if (variables->getAttribute(VariableTable::ContainsDynamicVariable) ||
|
|
variables->getAttribute(VariableTable::ContainsExtract)) {
|
|
return;
|
|
}
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
const string ¶mName = param->getName();
|
|
if (variables->isLvalParam(paramName)) continue;
|
|
TypePtr paramType = param->getActualType();
|
|
if ((paramType->is(Type::KindOfVariant) ||
|
|
paramType->is(Type::KindOfSome)) &&
|
|
!param->isRef()) {
|
|
param->setHasRTTI();
|
|
ClassScopePtr cls = getClassScope();
|
|
ar->addParamRTTIEntry(cls, func, paramName);
|
|
const string funcId = ar->getFuncId(cls, func);
|
|
ar->addRTTIFunction(funcId);
|
|
}
|
|
}
|
|
}
|
|
|
|
class TryingGotoFixer {
|
|
public:
|
|
TryingGotoFixer(AnalysisResultPtr ar) : id(0), m_ar(ar) {}
|
|
|
|
void fixTryingGotos(StatementPtr s) {
|
|
findTryingGotosRecur(s);
|
|
while (labelMap.size()) {
|
|
LabelInfoMap::iterator it = labelMap.begin(), end = labelMap.end();
|
|
while (it != end) {
|
|
if (it->second.trys.size()) {
|
|
TryStatementRawPtr t = it->second.trys.back();
|
|
for (int i = it->second.gotos.size(); i--; ) {
|
|
GotoInfo &gi = it->second.gotos[i];
|
|
if (gi.trys.size() < it->second.trys.size() ||
|
|
gi.trys[it->second.trys.size() - 1] != t) {
|
|
// the goto is not nested in the same try as the label
|
|
TryInfo & ti = tryMap[t];
|
|
if (!ti.label) {
|
|
ti.label = ++id;
|
|
}
|
|
string labelStr = lexical_cast<string>(ti.label);
|
|
if (it->second.trys.size() > 1) {
|
|
LabelInfo &li = labelMap[labelStr];
|
|
li.trys = it->second.trys;
|
|
li.trys.pop_back();
|
|
li.gotos.push_back(gi);
|
|
}
|
|
|
|
int lid = gi.goto_stmt->getId();
|
|
if (!lid) {
|
|
int &id = labelIdMap[gi.goto_stmt->label()];
|
|
if (!id) id = labelIdMap.size();
|
|
lid = id;
|
|
gi.goto_stmt->setId(lid);
|
|
ti.targets[-lid] = it->first;
|
|
} else {
|
|
ti.targets[lid] = it->first;
|
|
}
|
|
|
|
gi.goto_stmt->setLabel(labelStr);
|
|
}
|
|
}
|
|
}
|
|
labelMap.erase(it++);
|
|
}
|
|
}
|
|
fixTryingGotosRecur(s);
|
|
}
|
|
|
|
void findTryingGotosRecur(StatementPtr s) {
|
|
if (!s || FunctionWalker::SkipRecurse(s)) return;
|
|
switch (s->getKindOf()) {
|
|
case Statement::KindOfLabelStatement:
|
|
if (trys.size()) {
|
|
LabelStatementRawPtr label_stmt(
|
|
static_pointer_cast<LabelStatement>(s));
|
|
LabelInfo &li = labelMap[label_stmt->label()];
|
|
always_assert(!li.trys.size());
|
|
li.trys = trys;
|
|
}
|
|
break;
|
|
case Statement::KindOfGotoStatement: {
|
|
GotoStatementRawPtr goto_stmt(
|
|
static_pointer_cast<GotoStatement>(s));
|
|
LabelInfo &li = labelMap[goto_stmt->label()];
|
|
li.gotos.push_back(GotoInfo(goto_stmt, trys));
|
|
break;
|
|
}
|
|
case Statement::KindOfTryStatement: {
|
|
TryStatementRawPtr t(static_pointer_cast<TryStatement>(s));
|
|
trys.push_back(t);
|
|
findTryingGotosRecur(t->getBody());
|
|
trys.pop_back();
|
|
findTryingGotosRecur(t->getCatches());
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
for (int i = s->getKidCount(); i--; ) {
|
|
StatementPtr child(s->getNthStmt(i));
|
|
if (child) {
|
|
findTryingGotosRecur(child);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
ExpressionPtr gotoVar(StatementPtr s) {
|
|
SimpleVariablePtr sv(
|
|
new SimpleVariable(
|
|
s->getScope(), s->getLocation(), "0_gtid"));
|
|
sv->updateSymbol(SimpleVariablePtr());
|
|
sv->getSymbol()->setHidden();
|
|
return sv;
|
|
}
|
|
|
|
StatementPtr replaceGoto(GotoStatementRawPtr goto_stmt, int id) {
|
|
StatementListPtr sl(new StatementList(
|
|
goto_stmt->getScope(), goto_stmt->getLocation()));
|
|
AssignmentExpressionPtr ae(
|
|
new AssignmentExpression(
|
|
goto_stmt->getScope(), goto_stmt->getLocation(),
|
|
gotoVar(goto_stmt),
|
|
goto_stmt->makeScalarExpression(m_ar, Variant(id > 0 ? id : 0)),
|
|
false));
|
|
ExpStatementPtr exp(
|
|
new ExpStatement(goto_stmt->getScope(), goto_stmt->getLocation(), ae));
|
|
sl->addElement(exp);
|
|
sl->addElement(goto_stmt);
|
|
return sl;
|
|
}
|
|
|
|
StatementPtr fixTryingGotosRecur(StatementPtr s) {
|
|
if (FunctionWalker::SkipRecurse(s)) return StatementPtr();
|
|
|
|
for (int i = s->getKidCount(); i--; ) {
|
|
StatementPtr child(s->getNthStmt(i));
|
|
if (child) {
|
|
StatementPtr r = fixTryingGotosRecur(child);
|
|
if (r) s->setNthKid(i, r);
|
|
}
|
|
}
|
|
|
|
switch (s->getKindOf()) {
|
|
case Statement::KindOfGotoStatement: {
|
|
GotoStatementRawPtr goto_stmt(
|
|
static_pointer_cast<GotoStatement>(s));
|
|
if (int id = goto_stmt->getId()) {
|
|
return replaceGoto(goto_stmt, id);
|
|
}
|
|
break;
|
|
}
|
|
case Statement::KindOfTryStatement: {
|
|
TryStatementRawPtr t(static_pointer_cast<TryStatement>(s));
|
|
TryInfo &ti = tryMap[t];
|
|
StatementListPtr catches = t->getCatches();
|
|
bool doTry = ti.targets.size();
|
|
bool doCatch = catches->hasReachableLabel();
|
|
if (!doTry && !doCatch) break;
|
|
StatementListPtr sl(new StatementList(
|
|
s->getScope(), s->getLocation()));
|
|
StatementListPtr newBody(new StatementList(
|
|
s->getScope(), s->getLocation()));
|
|
if (doTry) {
|
|
StatementListPtr cases(new StatementList(
|
|
s->getScope(), s->getLocation()));
|
|
for (map<int,string>::iterator it = ti.targets.begin(),
|
|
end = ti.targets.end(); it != end; ++it) {
|
|
StatementPtr g(new GotoStatement(
|
|
s->getScope(), s->getLocation(), it->second));
|
|
if (it->first < 0) {
|
|
g = replaceGoto(static_pointer_cast<GotoStatement>(g), 0);
|
|
}
|
|
CaseStatementPtr c(new CaseStatement(
|
|
s->getScope(), s->getLocation(),
|
|
s->makeScalarExpression(m_ar, abs(it->first)),
|
|
g));
|
|
cases->addElement(c);
|
|
}
|
|
SwitchStatementPtr sw(new SwitchStatement(
|
|
s->getScope(), s->getLocation(),
|
|
gotoVar(s), cases));
|
|
|
|
newBody->addElement(sw);
|
|
|
|
LabelStatementPtr lab(new LabelStatement(
|
|
s->getScope(), s->getLocation(),
|
|
lexical_cast<string>(ti.label)));
|
|
sl->addElement(lab);
|
|
}
|
|
newBody->addElement(t->getBody());
|
|
sl->addElement(StatementPtr(new TryStatement(
|
|
s->getScope(), s->getLocation(),
|
|
newBody, catches)));
|
|
if (doCatch) {
|
|
if (!ti.label) ti.label = ++id;
|
|
string afterLab = lexical_cast<string>(ti.label) + "_a";
|
|
newBody->addElement(StatementPtr(
|
|
new GotoStatement(
|
|
s->getScope(), s->getLocation(), afterLab)));
|
|
|
|
for (int i = 0, n = catches->getCount(); i < n; i++) {
|
|
CatchStatementPtr c =
|
|
static_pointer_cast<CatchStatement>((*catches)[i]);
|
|
StatementPtr body = c->getStmt();
|
|
string lab = lexical_cast<string>(ti.label) + "_c" +
|
|
lexical_cast<string>(i);
|
|
c->setStmt(StatementPtr(
|
|
new GotoStatement(
|
|
c->getScope(), c->getLocation(), lab)));
|
|
StatementListPtr newBody(new StatementList(
|
|
c->getScope(), c->getLocation()));
|
|
newBody->addElement(StatementPtr(
|
|
new LabelStatement(
|
|
c->getScope(), c->getLocation(),
|
|
lab)));
|
|
if (body) newBody->addElement(body);
|
|
if (i + 1 < n) {
|
|
newBody->addElement(
|
|
StatementPtr(new GotoStatement(
|
|
c->getScope(), c->getLocation(),
|
|
afterLab)));
|
|
}
|
|
sl->addElement(newBody);
|
|
}
|
|
|
|
sl->addElement(StatementPtr(
|
|
new LabelStatement(
|
|
s->getScope(), s->getLocation(), afterLab)));
|
|
}
|
|
return sl;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return StatementPtr();
|
|
}
|
|
|
|
private:
|
|
typedef std::vector<TryStatementRawPtr> TryVector;
|
|
struct GotoInfo {
|
|
GotoInfo(GotoStatementRawPtr g, const TryVector &t) :
|
|
goto_stmt(g), trys(t) {}
|
|
GotoStatementRawPtr goto_stmt;
|
|
TryVector trys;
|
|
};
|
|
typedef std::vector<GotoInfo> GotoVector;
|
|
struct LabelInfo {
|
|
TryVector trys;
|
|
GotoVector gotos;
|
|
};
|
|
struct TryInfo {
|
|
int label;
|
|
std::map<int,string> targets;
|
|
};
|
|
TryVector trys;
|
|
typedef map<string,LabelInfo> LabelInfoMap;
|
|
LabelInfoMap labelMap;
|
|
map<TryStatementRawPtr,TryInfo> tryMap;
|
|
map<string,int> labelIdMap;
|
|
int id;
|
|
AnalysisResultPtr m_ar;
|
|
};
|
|
|
|
void MethodStatement::analyzeProgram(AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
|
|
if (m_params) {
|
|
m_params->analyzeProgram(ar);
|
|
if (Option::GenRTTIProfileData &&
|
|
ar->getPhase() == AnalysisResult::AnalyzeFinal) {
|
|
addParamRTTI(ar);
|
|
}
|
|
}
|
|
if (m_stmt) m_stmt->analyzeProgram(ar);
|
|
|
|
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
|
|
funcScope->setParamSpecs(ar);
|
|
if (funcScope->isGenerator()) {
|
|
VariableTablePtr variables = funcScope->getVariables();
|
|
Symbol *cont = variables->getSymbol(CONTINUATION_OBJECT_NAME);
|
|
cont->setHidden();
|
|
getOrigGeneratorFunc()->getFunctionScope()->addUse(
|
|
funcScope, BlockScope::UseKindClosure);
|
|
getOrigGeneratorFunc()->getFunctionScope()->setContainsBareThis(
|
|
funcScope->containsBareThis(), funcScope->containsRefThis());
|
|
getOrigGeneratorFunc()->getFunctionScope()->setContainsThis(
|
|
funcScope->containsThis());
|
|
}
|
|
if (funcScope->isSepExtension() ||
|
|
Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) {
|
|
funcScope->setDynamic();
|
|
}
|
|
#ifndef HHVM
|
|
// This is an hphpc-only transformation to deal with gotos that jump into
|
|
// try/catch blocks. The VM does not need this transformation, and it does
|
|
// not support this transformation.
|
|
if (funcScope->hasGoto() && funcScope->hasTry()) {
|
|
TryingGotoFixer tgf(ar);
|
|
tgf.fixTryingGotos(m_stmt);
|
|
}
|
|
#endif
|
|
// TODO: this may have to expand to a concept of "virtual" functions...
|
|
if (m_method) {
|
|
funcScope->disableInline();
|
|
if (m_name.length() > 2 && m_name.substr(0,2) == "__") {
|
|
bool magic = true;
|
|
int paramCount = 0;
|
|
if (m_name == "__destruct") {
|
|
funcScope->setOverriding(Type::Variant);
|
|
} else if (m_name == "__call") {
|
|
funcScope->setOverriding(Type::Variant, Type::String, Type::Array);
|
|
paramCount = 2;
|
|
} else if (m_name == "__set") {
|
|
funcScope->setOverriding(Type::Variant, Type::String, Type::Variant);
|
|
paramCount = 2;
|
|
} else if (m_name == "__get") {
|
|
funcScope->setOverriding(Type::Variant, Type::String);
|
|
paramCount = 1;
|
|
} else if (m_name == "__isset") {
|
|
funcScope->setOverriding(Type::Boolean, Type::String);
|
|
paramCount = 1;
|
|
} else if (m_name == "__unset") {
|
|
funcScope->setOverriding(Type::Variant, Type::String);
|
|
paramCount = 1;
|
|
} else if (m_name == "__sleep") {
|
|
funcScope->setOverriding(Type::Variant);
|
|
} else if (m_name == "__wakeup") {
|
|
funcScope->setOverriding(Type::Variant);
|
|
} else if (m_name == "__set_state") {
|
|
funcScope->setOverriding(Type::Variant, Type::Variant);
|
|
paramCount = 1;
|
|
} else if (m_name == "__tostring") {
|
|
funcScope->setOverriding(Type::String);
|
|
} else if (m_name == "__clone") {
|
|
funcScope->setOverriding(Type::Variant);
|
|
} else {
|
|
paramCount = -1;
|
|
if (m_name != "__construct") {
|
|
magic = false;
|
|
}
|
|
}
|
|
if (paramCount >= 0 && paramCount != funcScope->getMaxParamCount()) {
|
|
Compiler::Error(Compiler::InvalidMagicMethod, shared_from_this());
|
|
magic = false;
|
|
}
|
|
if (magic) funcScope->setMagicMethod();
|
|
}
|
|
// ArrayAccess methods
|
|
else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") {
|
|
if (m_name == "offsetexists") {
|
|
funcScope->setOverriding(Type::Boolean, Type::Variant);
|
|
} else if (m_name == "offsetget") {
|
|
funcScope->setOverriding(Type::Variant, Type::Variant);
|
|
} else if (m_name == "offsetset") {
|
|
funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant);
|
|
} else if (m_name == "offsetunset") {
|
|
funcScope->setOverriding(Type::Variant, Type::Variant);
|
|
}
|
|
}
|
|
}
|
|
} else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
|
|
TypePtr ret = funcScope->getReturnType();
|
|
if (ret && ret->isSpecificObject()) {
|
|
FileScopePtr fs = getFileScope();
|
|
if (fs) fs->addClassDependency(ar, ret->getName());
|
|
}
|
|
if (!getFunctionScope()->usesLSB()) {
|
|
if (StatementPtr orig = getOrigGeneratorFunc()) {
|
|
orig->getFunctionScope()->clearUsesLSB();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ConstructPtr MethodStatement::getNthKid(int n) const {
|
|
switch (n) {
|
|
case 0:
|
|
return m_modifiers;
|
|
case 1:
|
|
return m_params;
|
|
case 2:
|
|
return m_stmt;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
return ConstructPtr();
|
|
}
|
|
|
|
int MethodStatement::getKidCount() const {
|
|
return 3;
|
|
}
|
|
|
|
void MethodStatement::setNthKid(int n, ConstructPtr cp) {
|
|
switch (n) {
|
|
case 0:
|
|
m_modifiers = boost::dynamic_pointer_cast<ModifierExpression>(cp);
|
|
break;
|
|
case 1:
|
|
m_params = boost::dynamic_pointer_cast<ExpressionList>(cp);
|
|
break;
|
|
case 2:
|
|
m_stmt = boost::dynamic_pointer_cast<StatementList>(cp);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MethodStatement::inferTypes(AnalysisResultPtr ar) {
|
|
}
|
|
|
|
void MethodStatement::inferFunctionTypes(AnalysisResultPtr ar) {
|
|
IMPLEMENT_INFER_AND_CHECK_ASSERT(getFunctionScope());
|
|
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
bool pseudoMain = funcScope->inPseudoMain();
|
|
|
|
if (m_stmt && funcScope->isFirstPass()) {
|
|
if (pseudoMain ||
|
|
funcScope->getReturnType() ||
|
|
m_stmt->hasRetExp()) {
|
|
bool lastIsReturn = false;
|
|
if (m_stmt->getCount()) {
|
|
StatementPtr lastStmt = (*m_stmt)[m_stmt->getCount()-1];
|
|
if (lastStmt->is(Statement::KindOfReturnStatement)) {
|
|
lastIsReturn = true;
|
|
}
|
|
}
|
|
if (!lastIsReturn && (!pseudoMain || Option::GenerateCPPMain)) {
|
|
ExpressionPtr constant =
|
|
makeConstant(ar, funcScope->inPseudoMain() ? "true" : "null");
|
|
ReturnStatementPtr returnStmt =
|
|
ReturnStatementPtr(
|
|
new ReturnStatement(getScope(), getLocation(), constant));
|
|
m_stmt->addElement(returnStmt);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_params) {
|
|
m_params->inferAndCheck(ar, Type::Any, false);
|
|
}
|
|
|
|
// must also include params and use vars if this is a generator. note: we are
|
|
// OK reading the params from the AST nodes of the original generator
|
|
// function, since we have the dependency links set up
|
|
if (funcScope->isGenerator()) {
|
|
// orig function params
|
|
MethodStatementRawPtr m = getOrigGeneratorFunc();
|
|
assert(m);
|
|
|
|
VariableTablePtr variables = funcScope->getVariables();
|
|
ExpressionListPtr params = m->getParams();
|
|
if (params) {
|
|
for (int i = 0; i < params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
const string &name = param->getName();
|
|
assert(!param->isRef() || param->getType()->is(Type::KindOfVariant));
|
|
variables->addParamLike(name, param->getType(), ar, param,
|
|
funcScope->isFirstPass());
|
|
}
|
|
}
|
|
|
|
// use vars
|
|
ExpressionListPtr useVars = m->getFunctionScope()->getClosureVars();
|
|
if (useVars) {
|
|
for (int i = 0; i < useVars->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*useVars)[i]);
|
|
const string &name = param->getName();
|
|
assert(!param->isRef() || param->getType()->is(Type::KindOfVariant));
|
|
variables->addParamLike(name, param->getType(), ar, param,
|
|
funcScope->isFirstPass());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_stmt) {
|
|
m_stmt->inferTypes(ar);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// code generation functions
|
|
|
|
void MethodStatement::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
|
|
m_modifiers->outputPHP(cg, ar);
|
|
cg_printf(" function ");
|
|
if (m_ref) cg_printf("&");
|
|
if (!ParserBase::IsClosureOrContinuationName(m_name)) {
|
|
cg_printf("%s", m_originalName.c_str());
|
|
}
|
|
cg_printf("(");
|
|
if (m_params) m_params->outputPHP(cg, ar);
|
|
if (m_stmt) {
|
|
cg_indentBegin(") {\n");
|
|
funcScope->outputPHP(cg, ar);
|
|
m_stmt->outputPHP(cg, ar);
|
|
cg_indentEnd("}\n");
|
|
} else {
|
|
cg_printf(");\n");
|
|
}
|
|
}
|
|
|
|
void MethodStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
ClassScopePtr scope = getClassScope();
|
|
|
|
if (outputFFI(cg, ar)) return;
|
|
|
|
cg.setPHPLineNo(-1);
|
|
|
|
CodeGenerator::Context context = cg.getContext();
|
|
|
|
if (context == CodeGenerator::CppImplementation) {
|
|
printSource(cg);
|
|
}
|
|
|
|
bool isWrapper = context == CodeGenerator::CppTypedParamsWrapperDecl ||
|
|
context == CodeGenerator::CppTypedParamsWrapperImpl;
|
|
|
|
bool needsWrapper = isWrapper ||
|
|
(Option::HardTypeHints && funcScope->needsTypeCheckWrapper());
|
|
|
|
const char *prefix = needsWrapper && !isWrapper ?
|
|
Option::TypedMethodPrefix : Option::MethodPrefix;
|
|
|
|
switch (context) {
|
|
case CodeGenerator::CppDeclaration:
|
|
case CodeGenerator::CppTypedParamsWrapperDecl:
|
|
{
|
|
if (!m_stmt && !funcScope->isPerfectVirtual()) {
|
|
cg_printf("// ");
|
|
}
|
|
|
|
m_modifiers->outputCPP(cg, ar);
|
|
|
|
if (!m_stmt || m_name == "__offsetget_lval" ||
|
|
funcScope->isPerfectVirtual()) {
|
|
cg_printf("virtual ");
|
|
}
|
|
TypePtr type = funcScope->getReturnType();
|
|
if (type) {
|
|
type->outputCPPDecl(cg, ar, getScope());
|
|
} else {
|
|
cg_printf("void");
|
|
}
|
|
if (m_name == "__offsetget_lval") {
|
|
cg_printf(" &___offsetget_lval(");
|
|
} else if (m_modifiers->isStatic() && m_stmt) {
|
|
// Static method wrappers get generated as support methods
|
|
bool needsClassParam = funcScope->needsClassParam();
|
|
const char *prefix = needsWrapper && !isWrapper ?
|
|
(needsClassParam ?
|
|
Option::TypedMethodImplPrefix : Option::TypedMethodPrefix) :
|
|
(needsClassParam ? Option::MethodImplPrefix : Option::MethodPrefix);
|
|
cg_printf(" %s%s(", prefix,
|
|
CodeGenerator::FormatLabel(m_name).c_str());
|
|
if (needsClassParam) {
|
|
cg_printf("CStrRef cls%s",
|
|
funcScope->isVariableArgument() ||
|
|
(m_params && m_params->getCount()) ? ", " : "");
|
|
}
|
|
} else {
|
|
cg_printf(" %s%s(", prefix,
|
|
CodeGenerator::FormatLabel(m_name).c_str());
|
|
}
|
|
funcScope->outputCPPParamsDecl(cg, ar, m_params, true);
|
|
if (m_stmt) {
|
|
int opt = Option::GetOptimizationLevel(m_cppLength);
|
|
if (opt < 3) cg_printf(") __attribute__((optimize(%d)));\n", opt);
|
|
else cg_printf(");\n");
|
|
} else if (funcScope->isPerfectVirtual()) {
|
|
cg_printf(");\n");
|
|
} else {
|
|
cg_printf(") = 0;\n");
|
|
}
|
|
|
|
if (context != CodeGenerator::CppTypedParamsWrapperDecl) {
|
|
if (funcScope->isConstructor(scope)
|
|
&& !funcScope->isAbstract() && !scope->isInterface()) {
|
|
funcScope->outputCPPCreateDecl(cg, ar);
|
|
}
|
|
if (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()) {
|
|
cg.setContext(CodeGenerator::CppTypedParamsWrapperDecl);
|
|
outputCPPImpl(cg, ar);
|
|
cg.setContext(context);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CodeGenerator::CppImplementation:
|
|
case CodeGenerator::CppTypedParamsWrapperImpl:
|
|
if (m_stmt || funcScope->isPerfectVirtual()) {
|
|
int startLineImplementation = -1;
|
|
if (context == CodeGenerator::CppImplementation) {
|
|
startLineImplementation = cg.getLineNo(CodeGenerator::PrimaryStream);
|
|
}
|
|
TypePtr type = funcScope->getReturnType();
|
|
if (type) {
|
|
bool isHeader = cg.isFileOrClassHeader();
|
|
cg.setFileOrClassHeader(true);
|
|
type->outputCPPDecl(cg, ar, getScope());
|
|
cg.setFileOrClassHeader(isHeader);
|
|
} else {
|
|
cg_printf("void");
|
|
}
|
|
string origFuncName = getOriginalFullName();
|
|
if (Option::FunctionSections.find(origFuncName) !=
|
|
Option::FunctionSections.end()) {
|
|
string funcSection = Option::FunctionSections[origFuncName];
|
|
if (!funcSection.empty()) {
|
|
cg_printf(" __attribute__ ((section (\".text.%s\")))",
|
|
funcSection.c_str());
|
|
}
|
|
}
|
|
origFuncName = CodeGenerator::EscapeLabel(origFuncName);
|
|
|
|
if (m_name == "__offsetget_lval") {
|
|
cg_printf(" &%s%s::___offsetget_lval(",
|
|
Option::ClassPrefix, scope->getId().c_str());
|
|
} else if (m_modifiers->isStatic()) {
|
|
bool needsClassParam = funcScope->needsClassParam();
|
|
const char *prefix = needsWrapper && !isWrapper ?
|
|
(needsClassParam ?
|
|
Option::TypedMethodImplPrefix : Option::TypedMethodPrefix) :
|
|
(needsClassParam ? Option::MethodImplPrefix : Option::MethodPrefix);
|
|
cg_printf(" %s%s::%s%s(", Option::ClassPrefix,
|
|
scope->getId().c_str(), prefix,
|
|
CodeGenerator::FormatLabel(m_name).c_str());
|
|
if (needsClassParam) {
|
|
cg_printf("CStrRef cls%s",
|
|
funcScope->isVariableArgument() ||
|
|
(m_params && m_params->getCount()) ? ", " : "");
|
|
}
|
|
} else {
|
|
cg_printf(" %s%s::%s%s(", Option::ClassPrefix,
|
|
scope->getId().c_str(),
|
|
prefix, CodeGenerator::FormatLabel(m_name).c_str());
|
|
}
|
|
funcScope->outputCPPParamsDecl(cg, ar, m_params, false);
|
|
cg_indentBegin(") {\n");
|
|
if (!m_stmt) {
|
|
cg_printf("return throw_fatal(\"pure virtual\");\n");
|
|
cg_indentEnd("}\n");
|
|
} else if (context != CodeGenerator::CppTypedParamsWrapperImpl) {
|
|
if (m_stmt->hasBody()) {
|
|
const char *suffix =
|
|
(cg.getOutput() == CodeGenerator::SystemCPP ? "_BUILTIN" : "");
|
|
if (suffix[0] == '\0' && !funcScope->needsCheckMem()) {
|
|
suffix = "_NOMEM";
|
|
}
|
|
const string &injectionName =
|
|
CodeGenerator::EscapeLabel(getOriginalFullNameForInjection());
|
|
const string &scopeName =
|
|
CodeGenerator::EscapeLabel(scope->getOriginalName());
|
|
if (m_modifiers->isStatic()) {
|
|
cg_printf("STATIC_METHOD_INJECTION%s(%s, %s);\n", suffix,
|
|
scopeName.c_str(), injectionName.c_str());
|
|
} else if (cg.getOutput() != CodeGenerator::SystemCPP &&
|
|
!scope->isRedeclaring() && !scope->derivedByDynamic()) {
|
|
cg_printf("INSTANCE_METHOD_INJECTION_ROOTLESS%s(%s, %s);\n",
|
|
suffix, scopeName.c_str(), injectionName.c_str());
|
|
} else if (scope->getOriginalName() != "XhprofFrame") {
|
|
cg_printf("INSTANCE_METHOD_INJECTION%s(%s, %s);\n", suffix,
|
|
scopeName.c_str(), injectionName.c_str());
|
|
}
|
|
}
|
|
if (m_name == "__offsetget_lval") {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[0]);
|
|
cg_printf("Variant &v = %s->__lvalProxy;\n", cg.getGlobals(ar));
|
|
string lowered = Util::toLower(m_originalName);
|
|
cg_printf("v = %s%s(%s%s);\n",
|
|
prefix, lowered.c_str(),
|
|
Option::VariablePrefix, param->getName().c_str());
|
|
cg_printf("return v;\n");
|
|
} else {
|
|
outputCPPArgInjections(cg, ar, origFuncName.c_str(),
|
|
scope, funcScope);
|
|
funcScope->outputCPP(cg, ar);
|
|
if (funcScope->needsRefTemp()) {
|
|
cg.genReferenceTemp(shared_from_this());
|
|
}
|
|
if (funcScope->needsObjTemp()) {
|
|
cg_printf("ObjectData *obj_tmp UNUSED;\n");
|
|
}
|
|
cg.setContext(
|
|
CodeGenerator::NoContext); // no inner functions/classes
|
|
if (!funcScope->isStatic() && funcScope->getVariables()->
|
|
getAttribute(VariableTable::ContainsDynamicVariable)) {
|
|
Symbol *sym = funcScope->getVariables()->getSymbol("this");
|
|
if (sym && sym->declarationSet()) {
|
|
string namePrefix;
|
|
if (funcScope->isGenerator()) {
|
|
namePrefix = string(TYPED_CONTINUATION_OBJECT_NAME) + "->";
|
|
}
|
|
cg_printf("%s%sthis = this;\n", namePrefix.c_str(),
|
|
Option::VariablePrefix);
|
|
}
|
|
}
|
|
outputCPPStmt(cg, ar);
|
|
if (funcScope->needsRefTemp()) cg.clearRefereceTemp();
|
|
}
|
|
cg_indentEnd("}\n");
|
|
assert(startLineImplementation >= 0);
|
|
m_cppLength = cg.getLineNo(CodeGenerator::PrimaryStream)
|
|
- startLineImplementation;
|
|
if (Option::HardTypeHints && funcScope->needsTypeCheckWrapper()) {
|
|
cg.setContext(CodeGenerator::CppTypedParamsWrapperImpl);
|
|
outputCPPImpl(cg, ar);
|
|
}
|
|
} else {
|
|
outputCPPTypeCheckWrapper(cg, ar);
|
|
cg_indentEnd("}\n");
|
|
}
|
|
cg.setContext(context);
|
|
cg.printImplSplitter();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool MethodStatement::hasRefParam() {
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
if (param->isRef()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MethodStatement::outputParamArrayCreate(CodeGenerator &cg, bool checkRef) {
|
|
int n = m_params->getCount();
|
|
assert(n > 0);
|
|
cg_printf("array_createvi(%d, ", n);
|
|
VariableTablePtr variables = getFunctionScope()->getVariables();
|
|
for (int i = 0; i < n; i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
const string ¶mName = param->getName();
|
|
cg_printf("toVPOD(");
|
|
const char *pre = "";
|
|
if (!checkRef && variables->getSymbol(paramName)->isStashedVal()) {
|
|
pre = "v";
|
|
}
|
|
string name = pre + (Option::VariablePrefix +
|
|
CodeGenerator::FormatLabel(paramName));
|
|
if (checkRef && param->isRef()) {
|
|
not_reached();
|
|
} else {
|
|
cg_printf("%s", name.c_str());
|
|
}
|
|
cg_printf(")");
|
|
if (i < n - 1) {
|
|
cg_printf(", ");
|
|
} else {
|
|
cg_printf(")");
|
|
}
|
|
}
|
|
}
|
|
|
|
void MethodStatement::outputCPPArgInjections(CodeGenerator &cg,
|
|
AnalysisResultPtr ar,
|
|
const char *name,
|
|
ClassScopePtr cls,
|
|
FunctionScopePtr funcScope) {
|
|
if (cg.getOutput() != CodeGenerator::SystemCPP) {
|
|
if (funcScope->isDynamicInvoke()) {
|
|
cg_printf("INTERCEPT_INJECTION_ALWAYS(\"%s\", \"%s\", ", name, name);
|
|
} else {
|
|
cg_printf("INTERCEPT_INJECTION(\"%s\", ", name);
|
|
}
|
|
if (m_params) {
|
|
int n = m_params->getCount();
|
|
assert(n >= 0);
|
|
if (Option::GenArrayCreate && !hasRefParam()) {
|
|
if (ar->m_arrayIntegerKeyMaxSize < n) ar->m_arrayIntegerKeyMaxSize = n;
|
|
outputParamArrayCreate(cg, true);
|
|
} else {
|
|
cg_printf("(Array(ArrayInit(%d, ArrayInit::vectorInit)", n);
|
|
for (int i = 0; i < n; i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
const string ¶mName = param->getName();
|
|
cg_printf(".set%s(%d, %s%s%s)", param->isRef() ? "Ref" : "",
|
|
i, param->isRef() ? "r" : "",
|
|
Option::VariablePrefix, paramName.c_str());
|
|
}
|
|
cg_printf(".create()))");
|
|
}
|
|
} else {
|
|
cg_printf("null_array");
|
|
}
|
|
TypePtr t = funcScope->getReturnType();
|
|
bool refRet = funcScope->isRefReturn() && t && Type::IsMappedToVariant(t);
|
|
cg_printf(", %s);\n",
|
|
!funcScope->getReturnType() ? "" :
|
|
refRet ? "strongBind(r)" : "r");
|
|
}
|
|
|
|
if (Option::GenRTTIProfileData && m_params) {
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
if (param->hasRTTI()) {
|
|
const string ¶mName = param->getName();
|
|
int id = ar->getParamRTTIEntryId(cls, funcScope, paramName);
|
|
if (id != -1) {
|
|
cg_printf("RTTI_INJECTION(%s%s, %d);\n",
|
|
Option::VariablePrefix, paramName.c_str(), id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MethodStatement::outputCPPStmt(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
if (m_stmt) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
if (funcScope->inPseudoMain()) {
|
|
cg.beginHoistedClasses();
|
|
int i, n = m_stmt->getCount();
|
|
for (i = 0; i < n; ++i) {
|
|
StatementPtr s((*m_stmt)[i]);
|
|
if (s->is(Statement::KindOfClassStatement) ||
|
|
s->is(Statement::KindOfInterfaceStatement)) {
|
|
cg.addHoistedClass(s->getClassScope()->getName());
|
|
}
|
|
}
|
|
bool seenOther = false;
|
|
for (i = 0; i < n; ++i) {
|
|
StatementPtr s((*m_stmt)[i]);
|
|
if (!s || !s->hasImpl()) continue;
|
|
if (s->is(Statement::KindOfFunctionStatement)) {
|
|
s->outputCPP(cg, ar);
|
|
continue;
|
|
}
|
|
if (s->is(Statement::KindOfClassStatement) ||
|
|
s->is(Statement::KindOfInterfaceStatement)) {
|
|
ClassScopeRawPtr cls = s->getClassScope();
|
|
if (cls->isBaseClass() ||
|
|
!cls->isVolatile() ||
|
|
!cls->hasUnknownBases()) {
|
|
cls->outputCPPDef(cg);
|
|
continue;
|
|
}
|
|
const string &parent = cls->getOriginalParent();
|
|
if (cls->getBases().size() > (parent.empty() ? 0 : 1)) {
|
|
// the class is not hoistable because it implements
|
|
// an interface
|
|
continue;
|
|
}
|
|
always_assert(!parent.empty());
|
|
if (seenOther) {
|
|
ClassScopePtr pcls = ar->findClass(parent);
|
|
if (pcls) {
|
|
if (pcls->isVolatile()) {
|
|
cg_indentBegin("if (%s->CDEC(%s)) {\n",
|
|
cg.getGlobals(ar),
|
|
CodeGenerator::FormatLabel(
|
|
pcls->getName()).c_str());
|
|
}
|
|
cls->outputCPPDef(cg);
|
|
if (pcls->isVolatile()) {
|
|
cg_indentEnd("}\n");
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
} else {
|
|
seenOther = true;
|
|
}
|
|
}
|
|
cg.collectHoistedClasses(false);
|
|
for (i = 0; i < n; ++i) {
|
|
StatementPtr s((*m_stmt)[i]);
|
|
if (s->is(Statement::KindOfFunctionStatement)) continue;
|
|
if (s->is(Statement::KindOfClassStatement) ||
|
|
s->is(Statement::KindOfInterfaceStatement)) {
|
|
ClassScopeRawPtr cls = s->getClassScope();
|
|
if (cls->isBaseClass() ||
|
|
!cls->isVolatile() ||
|
|
!cls->hasUnknownBases()) {
|
|
continue;
|
|
}
|
|
cg.collectHoistedClasses(true);
|
|
s->outputCPP(cg, ar);
|
|
cg.collectHoistedClasses(false);
|
|
} else {
|
|
s->outputCPP(cg, ar);
|
|
}
|
|
}
|
|
cg.endHoistedClasses();
|
|
} else {
|
|
m_stmt->outputCPP(cg, ar);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MethodStatement::outputCPPStaticMethodWrapper(CodeGenerator &cg,
|
|
AnalysisResultPtr ar,
|
|
const char *cls) {
|
|
if (!m_modifiers->isStatic() || !m_stmt) return;
|
|
|
|
CodeGenerator::Context context = cg.getContext();
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
if (!funcScope->needsClassParam()) return;
|
|
|
|
bool isWrapper = context == CodeGenerator::CppTypedParamsWrapperDecl ||
|
|
context == CodeGenerator::CppTypedParamsWrapperImpl;
|
|
|
|
bool needsWrapper = isWrapper ||
|
|
(Option::HardTypeHints && funcScope->needsTypeCheckWrapper());
|
|
|
|
m_modifiers->outputCPP(cg, ar);
|
|
TypePtr type = funcScope->getReturnType();
|
|
if (type) {
|
|
type->outputCPPDecl(cg, ar, getScope());
|
|
} else {
|
|
cg_printf("void");
|
|
}
|
|
cg_printf(" %s%s(", needsWrapper && !isWrapper ?
|
|
Option::TypedMethodPrefix : Option::MethodPrefix,
|
|
CodeGenerator::FormatLabel(m_name).c_str());
|
|
if (!isWrapper) cg.setContext(CodeGenerator::CppFunctionWrapperDecl);
|
|
funcScope->outputCPPParamsDecl(cg, ar, m_params, true);
|
|
cg_printf(") { %s%s%s(", type ? "return " : "",
|
|
needsWrapper && !isWrapper ?
|
|
Option::TypedMethodImplPrefix : Option::MethodImplPrefix,
|
|
CodeGenerator::FormatLabel(m_name).c_str());
|
|
cg_printf("%s%s::s_class_name", Option::ClassPrefix, cls);
|
|
cg.setContext(context);
|
|
if (funcScope->isVariableArgument()) {
|
|
cg_printf(", num_args");
|
|
}
|
|
if (m_params) {
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
assert(param);
|
|
cg_printf(", %s%s", Option::VariablePrefix, param->getName().c_str());
|
|
}
|
|
}
|
|
if (funcScope->isVariableArgument()) {
|
|
cg_printf(", args");
|
|
}
|
|
cg_printf("); }\n");
|
|
if (!isWrapper && needsWrapper) {
|
|
cg.setContext(CodeGenerator::CppTypedParamsWrapperDecl);
|
|
outputCPPStaticMethodWrapper(cg, ar, cls);
|
|
cg.setContext(context);
|
|
}
|
|
}
|
|
|
|
void MethodStatement::outputCPPTypeCheckWrapper(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
TypePtr type = funcScope->getReturnType();
|
|
bool isMethod = getClassScope();
|
|
string fname = isMethod ? funcScope->getName() : funcScope->getId();
|
|
|
|
funcScope->outputCPP(cg, ar);
|
|
cg_printf("%s%s%s(", type ? "return " : "",
|
|
(isMethod ?
|
|
(m_modifiers->isStatic() && funcScope->needsClassParam() ?
|
|
Option::TypedMethodImplPrefix :
|
|
Option::TypedMethodPrefix) :
|
|
Option::TypedFunctionPrefix),
|
|
fname.c_str());
|
|
if (!isMethod) {
|
|
if (fname[0] == '0') {
|
|
cg_printf("extra, ");
|
|
}
|
|
} else if (m_modifiers->isStatic() && funcScope->needsClassParam()) {
|
|
cg_printf("cls, ");
|
|
}
|
|
if (funcScope->isVariableArgument()) {
|
|
cg_printf("num_args, ");
|
|
}
|
|
|
|
always_assert(m_params);
|
|
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
assert(param);
|
|
ConstantExpressionPtr con =
|
|
dynamic_pointer_cast<ConstantExpression>(param->defaultValue());
|
|
bool needsNullGet = con && con->isNull();
|
|
if (i) cg_printf(", ");
|
|
|
|
const string ¶mName =
|
|
string(Option::VariablePrefix) + param->getName();
|
|
if (TypePtr spec = funcScope->getParamTypeSpec(i)) {
|
|
if (Type::SameType(spec, funcScope->getParamType(i))) {
|
|
if (spec->isSpecificObject() && !needsNullGet) {
|
|
ClassScopePtr cls = ar->findClass(spec->getName());
|
|
assert(cls && !cls->isRedeclaring());
|
|
spec->outputCPPFastObjectCast(cg, ar, getScope(), true);
|
|
}
|
|
|
|
assert(Type::HasFastCastMethod(spec));
|
|
|
|
const char *nullGetMethod = nullptr;
|
|
switch (spec->getKindOf()) {
|
|
case Type::KindOfArray:
|
|
nullGetMethod = "getArrayDataOrNull";
|
|
break;
|
|
case Type::KindOfString:
|
|
nullGetMethod = "getStringDataOrNull";
|
|
break;
|
|
case Type::KindOfObject:
|
|
assert(spec->isSpecificObject());
|
|
nullGetMethod = "getObjectDataOrNull";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
assert(!needsNullGet || nullGetMethod != nullptr);
|
|
|
|
cg_printf("%s", paramName.c_str());
|
|
if (needsNullGet) {
|
|
cg_printf(".%s()", nullGetMethod);
|
|
} else {
|
|
const string &fastCast = Type::GetFastCastMethod(spec, true, true);
|
|
assert(!fastCast.empty());
|
|
cg_printf(".%s()", fastCast.c_str());
|
|
}
|
|
} else if (needsNullGet &&
|
|
funcScope->getParamType(i)->isStandardObject()) {
|
|
// a standard object can only be inferred if the spec type is
|
|
// a specific object - also, we only do this if we have
|
|
// a default value of null specified since otherwise,
|
|
// the passed in parameter cannot be null anyways
|
|
// and we do not have to generate a special type
|
|
// check in the wrapper
|
|
assert(spec->isSpecificObject());
|
|
cg_printf("%s.getObjectDataOrNull()", paramName.c_str());
|
|
} else {
|
|
cg_printf("%s", paramName.c_str());
|
|
}
|
|
} else {
|
|
cg_printf("%s", paramName.c_str());
|
|
}
|
|
}
|
|
|
|
if (funcScope->isVariableArgument()) {
|
|
cg_printf(", args");
|
|
}
|
|
cg_printf(");\n");
|
|
}
|
|
|
|
bool MethodStatement::outputFFI(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
ClassScopePtr clsScope = getClassScope();
|
|
bool pseudoMain = funcScope->inPseudoMain();
|
|
bool inClass = !m_className.empty();
|
|
// only expose public methods, and ignore those in redeclared classes
|
|
bool inaccessible =
|
|
inClass && (!m_modifiers->isPublic() || clsScope->isRedeclaring());
|
|
// skip constructors
|
|
bool isConstructor = inClass && funcScope->isConstructor(clsScope);
|
|
bool valid = !pseudoMain && !inaccessible && !isConstructor &&
|
|
(inClass || !ParserBase::IsAnonFunctionName(m_originalName));
|
|
|
|
if (cg.getContext() == CodeGenerator::CppFFIDecl ||
|
|
cg.getContext() == CodeGenerator::CppFFIImpl) {
|
|
if (valid) outputCPPFFIStub(cg, ar);
|
|
return true;
|
|
}
|
|
|
|
if (cg.getContext() == CodeGenerator::HsFFI) {
|
|
if (valid) outputHSFFIStub(cg, ar);
|
|
return true;
|
|
}
|
|
|
|
if (cg.getContext() == CodeGenerator::JavaFFI
|
|
|| cg.getContext() == CodeGenerator::JavaFFIInterface) {
|
|
if (valid) outputJavaFFIStub(cg, ar);
|
|
return true;
|
|
}
|
|
|
|
if (cg.getContext() == CodeGenerator::JavaFFICppDecl
|
|
|| cg.getContext() == CodeGenerator::JavaFFICppImpl) {
|
|
if (valid) outputJavaFFICPPStub(cg, ar);
|
|
return true;
|
|
}
|
|
|
|
if (cg.getContext() == CodeGenerator::SwigFFIDecl
|
|
|| cg.getContext() == CodeGenerator::SwigFFIImpl) {
|
|
if (valid) outputSwigFFIStub(cg, ar);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MethodStatement::outputCPPFFIStub(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
ClassScopePtr clsScope = getClassScope();
|
|
bool varArgs = funcScope->isVariableArgument();
|
|
bool ret = funcScope->getReturnType();
|
|
string fname = funcScope->getId();
|
|
bool inClass = !m_className.empty();
|
|
bool isStatic = !inClass || m_modifiers->isStatic();
|
|
|
|
if (inClass && m_modifiers->isAbstract()) {
|
|
return;
|
|
}
|
|
|
|
if (funcScope->getName() == "__offsetget_lval") {
|
|
return;
|
|
}
|
|
|
|
if (ret) {
|
|
cg_printf("int");
|
|
} else {
|
|
cg_printf("void");
|
|
}
|
|
cg_printf(" %s%s%s(", Option::FFIFnPrefix,
|
|
(inClass ? (m_className + "_cls_").c_str() : ""), fname.c_str());
|
|
if (ret) {
|
|
cg_printf("void** res");
|
|
}
|
|
|
|
bool first = !ret;
|
|
|
|
if (!isStatic) {
|
|
// instance methods need one additional parameter for the target
|
|
if (first) {
|
|
first = false;
|
|
}
|
|
else {
|
|
cg_printf(", ");
|
|
}
|
|
cg_printf("Variant *target");
|
|
}
|
|
|
|
int ac = funcScope->getMaxParamCount();
|
|
for (int i = 0; i < ac; ++i) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
cg_printf(", ");
|
|
}
|
|
cg_printf("Variant *a%d", i);
|
|
}
|
|
if (varArgs) {
|
|
if (!first) {
|
|
cg_printf(", ");
|
|
}
|
|
cg_printf("Variant *va");
|
|
}
|
|
cg_printf(")");
|
|
if (cg.getContext() == CodeGenerator::CppFFIDecl) {
|
|
cg_printf(";\n");
|
|
} else {
|
|
cg_indentBegin(" {\n");
|
|
if (ret) {
|
|
cg_printf("return hphp_ffi_exportVariant(");
|
|
}
|
|
|
|
if (!inClass) {
|
|
// simple function call
|
|
cg_printf("%s%s(", Option::FunctionPrefix, fname.c_str());
|
|
} else if (isStatic) {
|
|
// static method call
|
|
cg_printf("%s%s::%s%s(", Option::ClassPrefix,
|
|
clsScope->getId().c_str(),
|
|
Option::MethodPrefix, funcScope->getName().c_str());
|
|
} else {
|
|
// instance method call
|
|
cg_printf("dynamic_cast<%s%s *>(target->getObjectData())->",
|
|
Option::ClassPrefix, clsScope->getId().c_str());
|
|
cg_printf("%s%s(", Option::MethodPrefix, funcScope->getName().c_str());
|
|
}
|
|
|
|
first = true;
|
|
if (varArgs) {
|
|
cg_printf("%d + (va->isNull() ? 0 : va->getArrayData()->size())", ac);
|
|
first = false;
|
|
}
|
|
|
|
for (int i = 0; i < ac; ++i) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
cg_printf(", ");
|
|
}
|
|
cg_printf("*a%d", i);
|
|
}
|
|
if (varArgs) {
|
|
cg_printf(", va->toArray()");
|
|
}
|
|
if (ret) {
|
|
cg_printf("), res");
|
|
}
|
|
cg_printf(");\n");
|
|
cg_indentEnd("}\n");
|
|
cg.printImplSplitter();
|
|
}
|
|
return;
|
|
}
|
|
|
|
void MethodStatement::outputHSFFIStub(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
if (!m_className.empty()) {
|
|
// Haskell currently doesn't support FFI for class methods.
|
|
return;
|
|
}
|
|
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
bool varArgs = funcScope->isVariableArgument();
|
|
bool ret = funcScope->getReturnType();
|
|
string fname = funcScope->getId().c_str();
|
|
cg_indentBegin("foreign import ccall \"stubs.h %s%s\" %s%s\n",
|
|
Option::FFIFnPrefix, fname.c_str(),
|
|
Option::FFIFnPrefix, fname.c_str());
|
|
cg_printf(":: ");
|
|
if (ret) {
|
|
cg_printf("PtrPtr a -> ");
|
|
}
|
|
int ac = funcScope->getMaxParamCount();
|
|
for (int i = 0; i < ac; ++i) {
|
|
cg_printf("HphpVariantPtr -> ");
|
|
}
|
|
if (varArgs) {
|
|
cg_printf("HphpVariantPtr -> ");
|
|
}
|
|
if (ret) {
|
|
cg_printf("IO CInt");
|
|
} else {
|
|
cg_printf("IO ()");
|
|
}
|
|
cg_indentEnd("\n");
|
|
|
|
cg_printf("f_%s :: ", fname.c_str());
|
|
bool first = true;
|
|
if (ac > 0) {
|
|
cg_printf("(");
|
|
}
|
|
for (int i = 0; i < ac; ++i) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
cg_printf(", ");
|
|
}
|
|
cg_printf("VariantAble a%d", i);
|
|
}
|
|
if (ac > 0) {
|
|
cg_printf(") => ");
|
|
}
|
|
for (int i = 0; i < ac; ++i) {
|
|
cg_printf("a%d -> ", i);
|
|
}
|
|
if (varArgs) {
|
|
cg_printf("[Variant] -> ");
|
|
}
|
|
if (ret) {
|
|
cg_printf("IO Variant");
|
|
} else {
|
|
cg_printf("IO ()");
|
|
}
|
|
cg_printf("\n");
|
|
cg_printf("f_%s ", fname.c_str());
|
|
for (int i = 0; i < ac; ++i) {
|
|
cg_printf("v%d ", i);
|
|
}
|
|
if (varArgs) {
|
|
cg_printf("va ");
|
|
}
|
|
cg_indentBegin("=%s\n", ret ? " alloca (\\pres ->" : "");
|
|
for (int i = 0; i < ac; ++i) {
|
|
cg_indentBegin("withExportedVariant (toVariant v%d) (\\p%d ->\n", i, i);
|
|
}
|
|
if (varArgs) {
|
|
cg_indentBegin("withVParamList va (\\pva ->\n");
|
|
}
|
|
cg_indentBegin("do\n");
|
|
cg_printf("%sffi_%s", ret ? "t <- " : "", fname.c_str());
|
|
if (ret) {
|
|
cg_printf(" pres");
|
|
}
|
|
for (int i = 0; i < ac; ++i) {
|
|
cg_printf(" p%d", i);
|
|
}
|
|
if (varArgs) {
|
|
cg_printf(" pva");
|
|
}
|
|
if (ret) {
|
|
cg_printf("\n");
|
|
cg_printf("ppres <- peek pres\n");
|
|
cg_printf("buildVariant (fromIntegral t) ppres");
|
|
}
|
|
cg_indentEnd(); // end do
|
|
if (varArgs) {
|
|
cg_indentEnd(")"); // end varargs
|
|
}
|
|
for (int i = 0; i < ac; ++i) {
|
|
cg_indentEnd(")"); // end wEV i
|
|
}
|
|
if (ret) {
|
|
cg_indentEnd(")"); // end alloca
|
|
} else {
|
|
cg_indentEnd();
|
|
}
|
|
cg_printf("\n");
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Generates the Java stub method for a PHP toplevel function.
|
|
*
|
|
* @author qixin
|
|
*/
|
|
void MethodStatement::outputJavaFFIStub(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
bool varArgs = funcScope->isVariableArgument();
|
|
bool ret = funcScope->getReturnType();
|
|
bool inClass = !m_className.empty();
|
|
bool isStatic = !inClass || m_modifiers->isStatic();
|
|
string fname = funcScope->getId();
|
|
string originalName = funcScope->getOriginalName();
|
|
if (originalName.length() < fname.length()) {
|
|
// if there are functions of the same name, fname may contain "$$..."
|
|
// in the end
|
|
originalName += fname.substr(originalName.length());
|
|
}
|
|
|
|
if (originalName == "clone" || originalName == "equals"
|
|
|| originalName == "finalize" || originalName == "getClass"
|
|
|| originalName == "hashCode" || originalName == "notify"
|
|
|| originalName == "notifyAll" || originalName == "toString"
|
|
|| originalName == "wait") {
|
|
// not to clash with Java method names
|
|
originalName = "_" + originalName;
|
|
}
|
|
|
|
if (cg.getContext() == CodeGenerator::JavaFFIInterface
|
|
|| (inClass && m_modifiers->isAbstract())) {
|
|
// skip all the abstract methods, because php overriding is not very
|
|
// compatible with Java
|
|
return;
|
|
}
|
|
|
|
if (!inClass) printSource(cg);
|
|
|
|
// This Java method extracts the Variant pointer from the HphpVariant
|
|
// argument as a 64-bit integer, and then calls the native version.
|
|
bool exposeNative = false;
|
|
int ac = funcScope->getMaxParamCount();
|
|
if (ac > 0 || varArgs || !isStatic || (!ret && inClass)
|
|
|| cg.getContext() == CodeGenerator::JavaFFIInterface) {
|
|
// make methods always return something, so that they can override
|
|
// each other
|
|
cg_printf("public %s%s %s(",
|
|
(isStatic ? "static " : ""),
|
|
(!ret && !inClass ? "void" : "HphpVariant"),
|
|
originalName.c_str());
|
|
std::ostringstream args;
|
|
bool first = true;
|
|
if (!isStatic) {
|
|
// instance method has an additional parameter
|
|
args << "this.getVariantPtr()";
|
|
}
|
|
for (int i = 0; i < ac; i++) {
|
|
if (first) {
|
|
first = false;
|
|
if (!isStatic) args << ", ";
|
|
}
|
|
else {
|
|
cg_printf(", ");
|
|
args << ", ";
|
|
}
|
|
cg_printf("HphpVariant a%d", i);
|
|
args << "a" << i << ".getVariantPtr()";
|
|
}
|
|
if (varArgs) {
|
|
if (!first) {
|
|
cg_printf(", ");
|
|
args << ", ";
|
|
}
|
|
else if (!isStatic) {
|
|
args << ", ";
|
|
}
|
|
cg_printf("HphpVariant va");
|
|
args << "va.getVariantPtr()";
|
|
}
|
|
|
|
if (cg.getContext() == CodeGenerator::JavaFFIInterface) {
|
|
cg_printf(");\n\n");
|
|
return;
|
|
}
|
|
|
|
cg_indentBegin(") {\n");
|
|
cg_printf("%s%s_native(%s);\n", (ret ? "return " : ""),
|
|
originalName.c_str(),
|
|
args.str().c_str());
|
|
if (!ret && inClass) {
|
|
cg_printf("return HphpNull.phpNull();\n");
|
|
}
|
|
cg_indentEnd("}\n\n");
|
|
}
|
|
else {
|
|
exposeNative = true;
|
|
}
|
|
|
|
// the native method stub
|
|
cg_printf("%s %snative %s %s%s(",
|
|
(exposeNative ? "public" : "private"),
|
|
(isStatic ? "static " : ""), (ret ? "HphpVariant" : "void"),
|
|
originalName.c_str(),
|
|
(exposeNative ? "" : "_native"));
|
|
bool first = true;
|
|
if (!isStatic) {
|
|
// instance method has an additional parameter
|
|
cg_printf("long targetPtr");
|
|
first = false;
|
|
}
|
|
for (int i = 0; i < ac; i++) {
|
|
if (first) first = false;
|
|
else cg_printf(", ");
|
|
cg_printf("long a%d", i);
|
|
}
|
|
if (varArgs) {
|
|
if (!first) cg_printf(", ");
|
|
cg_printf("long va");
|
|
}
|
|
cg_printf(");\n\n");
|
|
}
|
|
|
|
void MethodStatement::outputJavaFFICPPStub(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
// TODO translate PHP namespace once that is supported
|
|
string packageName = Option::JavaFFIRootPackage;
|
|
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
bool varArgs = funcScope->isVariableArgument();
|
|
bool ret = funcScope->getReturnType();
|
|
bool inClass = !m_className.empty();
|
|
bool isStatic = !inClass || m_modifiers->isStatic();
|
|
string fname = funcScope->getId();
|
|
int ac = funcScope->getMaxParamCount();
|
|
bool exposeNative = !(ac > 0 || varArgs || !isStatic || (!ret && inClass));
|
|
|
|
if (inClass && m_modifiers->isAbstract()) {
|
|
// skip all the abstract methods, because hphp doesn't generate code
|
|
// for them
|
|
return;
|
|
}
|
|
|
|
if (funcScope->getName() == "__offsetget_lval") return;
|
|
|
|
const char *clsName;
|
|
if (inClass) {
|
|
// uses capitalized original class name
|
|
ClassScopePtr cls = ar->findClass(m_className);
|
|
clsName = cls->getOriginalName().c_str();
|
|
} else {
|
|
clsName = "HphpMain";
|
|
}
|
|
string mangledName = "Java." + packageName + "." + clsName + "." + fname
|
|
+ (exposeNative ? "" : "_native");
|
|
// all the existing "_" are replaced as "_1"
|
|
Util::replaceAll(mangledName, "_", "_1");
|
|
Util::replaceAll(mangledName, ".", "_");
|
|
|
|
cg_printf("JNIEXPORT %s JNICALL\n", ret ? "jobject" : "void");
|
|
cg_printf("%s(JNIEnv *env, %s target", mangledName.c_str(),
|
|
(isStatic ? "jclass" : "jobject"));
|
|
|
|
std::ostringstream args;
|
|
bool first = true;
|
|
if (!isStatic) {
|
|
// instance method also gets an additional argument, which is a Variant
|
|
// pointer to the target, encoded in int64
|
|
first = false;
|
|
cg_printf(", jlong targetPtr");
|
|
args << "(Variant *)targetPtr";
|
|
}
|
|
for (int i = 0; i < ac; i++) {
|
|
cg_printf(", jlong a%d", i);
|
|
if (first) first = false;
|
|
else args << ", ";
|
|
args << "(Variant *)a" << i;
|
|
}
|
|
if (varArgs) {
|
|
cg_printf(", jlong va");
|
|
if (!first) args << ", ";
|
|
args << "(Variant *)va";
|
|
}
|
|
|
|
if (cg.getContext() == CodeGenerator::JavaFFICppDecl) {
|
|
// java_stubs.h
|
|
cg_printf(");\n\n");
|
|
return;
|
|
}
|
|
|
|
cg_indentBegin(") {\n");
|
|
|
|
// support static/instance methods
|
|
if (ret) {
|
|
cg_printf("void *result;\n");
|
|
cg_printf("int kind = ");
|
|
cg_printf("%s%s%s(&result",
|
|
Option::FFIFnPrefix,
|
|
(inClass ? (m_className + "_cls_").c_str() : ""), fname.c_str());
|
|
if (!isStatic || ac > 0 || varArgs) cg_printf(", ");
|
|
} else {
|
|
cg_printf("%s%s%s(", Option::FFIFnPrefix,
|
|
(inClass ? (m_className + "_cls_").c_str() : ""),
|
|
fname.c_str());
|
|
}
|
|
cg_printf("%s);\n", args.str().c_str());
|
|
if (ret) {
|
|
if (!inClass) {
|
|
// HphpMain extends hphp.Hphp.
|
|
cg_printf("jclass hphp = env->GetSuperclass(target);\n");
|
|
}
|
|
else {
|
|
cg_printf("jclass hphp = env->FindClass(\"hphp/Hphp\");\n");
|
|
}
|
|
cg_printf("return exportVariantToJava(env, hphp, result, kind);\n");
|
|
}
|
|
|
|
cg_indentEnd("}\n");
|
|
cg.printImplSplitter();
|
|
}
|
|
|
|
void MethodStatement::outputSwigFFIStub(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
bool varArgs = funcScope->isVariableArgument();
|
|
bool ret = funcScope->getReturnType();
|
|
string fname = funcScope->getId();
|
|
string originalName = funcScope->getOriginalName();
|
|
int ac = funcScope->getMaxParamCount();
|
|
|
|
if (cg.getContext() == CodeGenerator::SwigFFIImpl) {
|
|
printSource(cg);
|
|
}
|
|
|
|
cg_printf("Variant *%s(HphpSession *s", originalName.c_str());
|
|
std::ostringstream args;
|
|
bool first = true;
|
|
for (int i = 0; i < ac; i++) {
|
|
cg_printf(", Variant *a%d", i);
|
|
if (first) first = false;
|
|
else args << ", ";
|
|
args << "a" << i;
|
|
}
|
|
if (varArgs) {
|
|
cg_printf(", Variant *va");
|
|
if (!first) args << ", ";
|
|
args << "va";
|
|
}
|
|
|
|
if (cg.getContext() == CodeGenerator::SwigFFIDecl) {
|
|
cg_printf(");\n\n");
|
|
return;
|
|
}
|
|
|
|
cg_indentBegin(") {\n");
|
|
if (ret) {
|
|
cg_printf("void *result;\n");
|
|
cg_printf("int kind = ");
|
|
cg_printf("%s%s(&result", Option::FFIFnPrefix, fname.c_str());
|
|
if (ac > 0 || varArgs) cg_printf(", ");
|
|
} else {
|
|
cg_printf("%s%s(", Option::FFIFnPrefix, fname.c_str());
|
|
}
|
|
cg_printf("%s);\n", args.str().c_str());
|
|
cg_printf("Variant *ret = ");
|
|
if (ret) {
|
|
cg_printf("hphpBuildVariant(kind, result);\n");
|
|
cg_printf("s->addVariant(ret);\n");
|
|
} else {
|
|
cg_printf("hphpBuildVariant(0, 0);\n");
|
|
cg_printf("s->addVariant(ret);\n");
|
|
}
|
|
cg_printf("return ret;\n");
|
|
cg_indentEnd("}\n\n");
|
|
}
|