a8e7668189
Very little is actually used anymore
1423 linhas
46 KiB
C++
1423 linhas
46 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");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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");
|
|
}
|