Arquivos
hhvm/hphp/compiler/analysis/class_scope.cpp
T
mwilliams 7ff582616f Don't compile systemlib php files
We don't need them in g_class_map, beacuse hhvm processes
the php for itself.
2013-03-07 21:09:46 -08:00

3168 linhas
107 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 <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/construct.h>
#include <compiler/expression/class_constant_expression.h>
#include <compiler/expression/closure_expression.h>
#include <compiler/expression/constant_expression.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/unary_op_expression.h>
#include <compiler/expression/simple_function_call.h>
#include <compiler/option.h>
#include <compiler/parser/parser.h>
#include <compiler/statement/interface_statement.h>
#include <compiler/statement/function_statement.h>
#include <compiler/statement/method_statement.h>
#include <compiler/statement/statement_list.h>
#include <runtime/base/builtin_functions.h>
#include <runtime/base/class_info.h>
#include <compiler/statement/class_variable.h>
#include <compiler/statement/class_constant.h>
#include <compiler/statement/use_trait_statement.h>
#include <compiler/statement/trait_prec_statement.h>
#include <compiler/statement/trait_alias_statement.h>
#include <runtime/base/zend/zend_string.h>
#include <util/util.h>
using namespace HPHP;
using std::map;
///////////////////////////////////////////////////////////////////////////////
ClassScope::ClassScope(KindOf kindOf, const std::string &name,
const std::string &parent,
const vector<string> &bases,
const std::string &docComment, StatementPtr stmt,
const std::vector<UserAttributePtr> &attrs)
: BlockScope(name, docComment, stmt, BlockScope::ClassScope),
m_parent(parent), m_bases(bases), m_attribute(0), m_redeclaring(-1),
m_kindOf(kindOf), m_derivesFromRedeclaring(FromNormal),
m_traitStatus(NOT_FLATTENED), m_volatile(false),
m_persistent(false), m_derivedByDynamic(false),
m_sep(false), m_needsCppCtor(false), m_needsInit(true), m_knownBases(0),
m_needsEnableDestructor(0) {
m_dynamic = Option::IsDynamicClass(m_name);
// dynamic class is also volatile
m_volatile = Option::AllVolatile || m_dynamic;
for (unsigned i = 0; i < attrs.size(); ++i) {
if (m_userAttributes.find(attrs[i]->getName()) != m_userAttributes.end()) {
attrs[i]->parseTimeFatal(Compiler::DeclaredAttributeTwice,
"Redeclared attribute %s",
attrs[i]->getName().c_str());
}
m_userAttributes[attrs[i]->getName()] = attrs[i]->getExp();
}
assert(m_parent.empty() || (!m_bases.empty() && m_bases[0] == m_parent));
}
// System
ClassScope::ClassScope(AnalysisResultPtr ar,
const std::string &name, const std::string &parent,
const std::vector<std::string> &bases,
const FunctionScopePtrVec &methods)
: BlockScope(name, "", StatementPtr(), BlockScope::ClassScope),
m_parent(parent), m_bases(bases),
m_attribute(0), m_redeclaring(-1),
m_kindOf(KindOfObjectClass), m_derivesFromRedeclaring(FromNormal),
m_traitStatus(NOT_FLATTENED), m_dynamic(false),
m_volatile(false), m_persistent(false),
m_derivedByDynamic(false), m_sep(false), m_needsCppCtor(false),
m_needsInit(true), m_knownBases(0), m_needsEnableDestructor(0) {
BOOST_FOREACH(FunctionScopePtr f, methods) {
if (f->getName() == "__construct") setAttribute(HasConstructor);
else if (f->getName() == "__destruct") setAttribute(HasDestructor);
else if (f->getName() == "__get") setAttribute(HasUnknownPropGetter);
else if (f->getName() == "__set") setAttribute(HasUnknownPropSetter);
else if (f->getName() == "__call") setAttribute(HasUnknownMethodHandler);
else if (f->getName() == "__callstatic") {
setAttribute(HasUnknownStaticMethodHandler);
} else if (f->getName() == "__isset") setAttribute(HasUnknownPropTester);
else if (f->getName() == "__unset") setAttribute(HasPropUnsetter);
else if (f->getName() == "__invoke") setAttribute(HasInvokeMethod);
addFunction(ar, f);
}
setAttribute(Extension);
setAttribute(System);
assert(m_parent.empty() || (!m_bases.empty() && m_bases[0] == m_parent));
}
const std::string &ClassScope::getOriginalName() const {
if (m_stmt) {
return dynamic_pointer_cast<InterfaceStatement>(m_stmt)->
getOriginalName();
}
return m_originalName;
}
// like getId(), but without the label formatting
std::string ClassScope::getDocName() const {
string name = getOriginalName();
if (m_redeclaring < 0) {
return name;
}
return name + Option::IdPrefix +
boost::lexical_cast<std::string>(m_redeclaring);
}
std::string ClassScope::getId() const {
string name = CodeGenerator::FormatLabel(getOriginalName());
if (m_redeclaring < 0) {
return name;
}
return name + Option::IdPrefix +
boost::lexical_cast<std::string>(m_redeclaring);
}
bool ClassScope::NeedStaticArray(ClassScopePtr cls, FunctionScopePtr func) {
return cls && cls->getAttribute(NotFinal) && !func->isPrivate();
}
///////////////////////////////////////////////////////////////////////////////
void ClassScope::derivedMagicMethods(ClassScopePtr super) {
super->setAttribute(NotFinal);
if (derivedByDynamic()) {
super->m_derivedByDynamic = true;
}
if (m_attribute & (HasUnknownPropGetter|
MayHaveUnknownPropGetter|
InheritsUnknownPropGetter)) {
super->setAttribute(MayHaveUnknownPropGetter);
}
if (m_attribute & (HasUnknownPropSetter|
MayHaveUnknownPropSetter|
InheritsUnknownPropSetter)) {
super->setAttribute(MayHaveUnknownPropSetter);
}
if (m_attribute & (HasUnknownPropTester|
MayHaveUnknownPropTester|
InheritsUnknownPropTester)) {
super->setAttribute(MayHaveUnknownPropTester);
}
if (m_attribute & (HasPropUnsetter|
MayHavePropUnsetter|
InheritsPropUnsetter)) {
super->setAttribute(MayHavePropUnsetter);
}
if (m_attribute & (HasUnknownMethodHandler|
MayHaveUnknownMethodHandler|
InheritsUnknownMethodHandler)) {
super->setAttribute(MayHaveUnknownMethodHandler);
}
if (m_attribute & (HasUnknownStaticMethodHandler|
MayHaveUnknownStaticMethodHandler|
InheritsUnknownStaticMethodHandler)) {
super->setAttribute(MayHaveUnknownStaticMethodHandler);
}
if (m_attribute & (HasInvokeMethod|
MayHaveInvokeMethod|
InheritsInvokeMethod)) {
super->setAttribute(MayHaveInvokeMethod);
}
if (m_attribute & (HasArrayAccess|
MayHaveArrayAccess|
InheritsArrayAccess)) {
super->setAttribute(MayHaveArrayAccess);
}
}
void ClassScope::inheritedMagicMethods(ClassScopePtr super) {
if (super->m_attribute & UsesUnknownTrait) {
setAttribute(UsesUnknownTrait);
}
if (super->m_attribute &
(HasUnknownPropGetter|InheritsUnknownPropGetter)) {
setAttribute(InheritsUnknownPropGetter);
}
if (super->m_attribute & (HasUnknownPropSetter|InheritsUnknownPropSetter)) {
setAttribute(InheritsUnknownPropSetter);
}
if (super->m_attribute & (HasUnknownPropTester|InheritsUnknownPropTester)) {
setAttribute(InheritsUnknownPropTester);
}
if (super->m_attribute & (HasPropUnsetter|InheritsPropUnsetter)) {
setAttribute(InheritsPropUnsetter);
}
if (super->m_attribute &
(HasUnknownMethodHandler|InheritsUnknownMethodHandler)) {
setAttribute(InheritsUnknownMethodHandler);
}
if (super->m_attribute &
(HasUnknownStaticMethodHandler|InheritsUnknownStaticMethodHandler)) {
setAttribute(InheritsUnknownStaticMethodHandler);
}
if (super->m_attribute & (HasInvokeMethod|InheritsInvokeMethod)) {
setAttribute(InheritsInvokeMethod);
}
if (super->m_attribute & (HasArrayAccess|InheritsArrayAccess)) {
setAttribute(InheritsArrayAccess);
}
}
bool ClassScope::implementsArrayAccess() {
return
getAttribute(MayHaveArrayAccess) |
getAttribute(HasArrayAccess) |
getAttribute(InheritsArrayAccess);
}
bool ClassScope::implementsAccessor(int prop) {
if (m_attribute & prop) return true;
if (prop & MayHaveUnknownPropGetter) {
prop |= HasUnknownPropGetter | InheritsUnknownPropGetter;
}
if (prop & MayHaveUnknownPropSetter) {
prop |= HasUnknownPropSetter | InheritsUnknownPropSetter;
}
if (prop & MayHaveUnknownPropTester) {
prop |= HasUnknownPropTester | InheritsUnknownPropTester;
}
if (prop & MayHavePropUnsetter) {
prop |= HasPropUnsetter | InheritsPropUnsetter;
}
return m_attribute & prop;
}
void ClassScope::checkDerivation(AnalysisResultPtr ar, hphp_string_iset &seen) {
seen.insert(m_name);
hphp_string_iset bases;
for (int i = m_bases.size() - 1; i >= 0; i--) {
const string &base = m_bases[i];
if (seen.find(base) != seen.end() || bases.find(base) != bases.end()) {
Compiler::Error(
Compiler::InvalidDerivation,
m_stmt,
"The class hierarchy contains a circular reference involving " + base);
if (i == 0 && !m_parent.empty()) {
assert(base == m_parent);
m_parent.clear();
}
m_bases.erase(m_bases.begin() + i);
continue;
}
bases.insert(base);
ClassScopePtrVec parents = ar->findClasses(Util::toLower(base));
for (unsigned int j = 0; j < parents.size(); j++) {
parents[j]->checkDerivation(ar, seen);
}
}
seen.erase(m_name);
}
void ClassScope::collectMethods(AnalysisResultPtr ar,
StringToFunctionScopePtrMap &funcs,
bool collectPrivate /* = true */,
bool forInvoke /* = false */) {
// add all functions this class has
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
const FunctionScopePtr &fs = *iter;
if (!collectPrivate && fs->isPrivate()) continue;
FunctionScopePtr &func = funcs[fs->getName()];
if (!func) {
func = fs;
} else {
func->setVirtual();
fs->setVirtual();
fs->setHasOverride();
if (fs->isFinal()) {
std::string s__MockClass = "__MockClass";
ClassScopePtr derivedClass = func->getContainingClass();
if (derivedClass->m_userAttributes.find(s__MockClass) ==
derivedClass->m_userAttributes.end()) {
Compiler::Error(Compiler::InvalidOverride,
fs->getStmt(), func->getStmt());
}
}
}
}
int n = forInvoke ? m_parent.empty() ? 0 : 1 : m_bases.size();
// walk up
for (int i = 0; i < n; i++) {
const string &base = m_bases[i];
ClassScopePtr super = ar->findClass(base);
if (super) {
if (super->isRedeclaring()) {
if (forInvoke) continue;
const ClassScopePtrVec &classes = ar->findRedeclaredClasses(base);
StringToFunctionScopePtrMap pristine(funcs);
BOOST_FOREACH(ClassScopePtr cls, classes) {
cls->m_derivedByDynamic = true;
StringToFunctionScopePtrMap cur(pristine);
derivedMagicMethods(cls);
cls->collectMethods(ar, cur, false, forInvoke);
inheritedMagicMethods(cls);
funcs.insert(cur.begin(), cur.end());
cls->getVariables()->
forceVariants(ar, VariableTable::AnyNonPrivateVars);
}
if (base == m_parent) {
m_derivesFromRedeclaring = DirectFromRedeclared;
getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars,
false);
getVariables()->setAttribute(VariableTable::NeedGlobalPointer);
} else if (isInterface()) {
m_derivesFromRedeclaring = DirectFromRedeclared;
}
setVolatile();
} else {
derivedMagicMethods(super);
super->collectMethods(ar, funcs, false, forInvoke);
inheritedMagicMethods(super);
if (super->derivesFromRedeclaring()) {
if (base == m_parent) {
m_derivesFromRedeclaring = IndirectFromRedeclared;
getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars);
} else if (isInterface()) {
m_derivesFromRedeclaring = IndirectFromRedeclared;
}
setVolatile();
} else if (super->isVolatile()) {
setVolatile();
}
}
} else if (!Option::SystemGen) {
Compiler::Error(Compiler::UnknownBaseClass, m_stmt, base);
if (base == m_parent) {
ar->declareUnknownClass(m_parent);
m_derivesFromRedeclaring = DirectFromRedeclared;
getVariables()->setAttribute(VariableTable::NeedGlobalPointer);
getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars);
setVolatile();
} else {
if (isInterface()) {
m_derivesFromRedeclaring = DirectFromRedeclared;
}
m_bases.erase(m_bases.begin() + i);
n--;
i--;
}
}
}
}
void ClassScope::importTraitProperties(AnalysisResultPtr ar) {
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]);
if (!tCls) continue;
ClassStatementPtr tStmt =
dynamic_pointer_cast<ClassStatement>(tCls->getStmt());
StatementListPtr tStmts = tStmt->getStmts();
if (!tStmts) continue;
for (int s = 0; s < tStmts->getCount(); s++) {
ClassVariablePtr prop =
dynamic_pointer_cast<ClassVariable>((*tStmts)[s]);
if (prop) {
ClassVariablePtr cloneProp = dynamic_pointer_cast<ClassVariable>(
dynamic_pointer_cast<ClassStatement>(m_stmt)->addClone(prop));
cloneProp->resetScope(shared_from_this(), true);
cloneProp->addTraitPropsToScope(ar,
dynamic_pointer_cast<ClassScope>(shared_from_this()));
}
}
}
}
MethodStatementPtr
ClassScope::importTraitMethod(const TraitMethod& traitMethod,
AnalysisResultPtr ar,
string methName,
GeneratorRenameMap& genRenameMap,
const std::map<string, MethodStatementPtr>&
importedTraitMethods) {
MethodStatementPtr meth = traitMethod.m_method;
string origMethName = traitMethod.m_originalName;
ModifierExpressionPtr modifiers = traitMethod.m_modifiers;
if (meth->getOrigGeneratorFunc()) {
const string &name = meth->getOrigGeneratorFunc()->getName();
if (!importedTraitMethods.count(name)) {
// Dont import the generator, if the origGenerator wasnt imported
// this happens when a generator in the trait is hidden by a non-generator
// method in the importing class.
return MethodStatementPtr();
}
}
MethodStatementPtr cloneMeth = dynamic_pointer_cast<MethodStatement>(
dynamic_pointer_cast<ClassStatement>(m_stmt)->addClone(meth));
cloneMeth->setName(methName);
cloneMeth->setOriginalName(origMethName);
// Note: keep previous modifiers if none specified when importing the trait
if (modifiers && modifiers->getCount()) {
cloneMeth->setModifiers(modifiers);
}
FunctionScopePtr funcScope = meth->getFunctionScope();
// Trait method typehints, self and parent, need to be converted
ClassScopePtr cScope = dynamic_pointer_cast<ClassScope>(shared_from_this());
cloneMeth->fixupSelfAndParentTypehints( cScope );
// Generator methods need to be renamed, otherwise code gen produces multiple
// continuation classes with the same name
if (funcScope->isGenerator()) {
const string& newName = getNewGeneratorName(funcScope, genRenameMap);
methName = origMethName = newName;
cloneMeth->setName(newName);
cloneMeth->setOriginalName(newName);
}
FunctionScopePtr cloneFuncScope
(new HPHP::FunctionScope(funcScope, ar, methName, origMethName, cloneMeth,
cloneMeth->getModifiers()));
cloneMeth->resetScope(cloneFuncScope, true);
cloneFuncScope->setOuterScope(shared_from_this());
informClosuresAboutScopeClone(cloneMeth, cloneFuncScope, ar);
cloneMeth->addTraitMethodToScope(ar,
dynamic_pointer_cast<ClassScope>(shared_from_this()));
ar->recordFunctionSource(cloneMeth->getFullName(), meth->getLocation(),
meth->getFileScope()->getName());
return cloneMeth;
}
void ClassScope::informClosuresAboutScopeClone(
ConstructPtr root,
FunctionScopePtr outerScope,
AnalysisResultPtr ar) {
if (!root) {
return;
}
for (int i = 0; i < root->getKidCount(); i++) {
ConstructPtr cons = root->getNthKid(i);
ClosureExpressionPtr closure =
dynamic_pointer_cast<ClosureExpression>(cons);
if (!closure) {
informClosuresAboutScopeClone(cons, outerScope, ar);
continue;
}
FunctionStatementPtr func = closure->getClosureFunction();
HPHP::FunctionScopePtr funcScope = func->getFunctionScope();
assert(funcScope->isClosure());
funcScope->addClonedTraitOuterScope(outerScope);
// Don't need to recurse
}
}
void ClassScope::addImportTraitMethod(const TraitMethod &traitMethod,
const string &methName) {
m_importMethToTraitMap[methName].push_back(traitMethod);
}
void
ClassScope::setImportTraitMethodModifiers(const string &methName,
ClassScopePtr traitCls,
ModifierExpressionPtr modifiers) {
TraitMethodList &methList = m_importMethToTraitMap[methName];
for (TraitMethodList::iterator iter = methList.begin();
iter != methList.end(); iter++) {
if (iter->m_trait == traitCls) {
iter->m_modifiers = modifiers;
return;
}
}
}
MethodStatementPtr
ClassScope::findTraitMethod(AnalysisResultPtr ar,
ClassScopePtr trait,
const string &methodName,
std::set<ClassScopePtr> &visitedTraits) {
if (visitedTraits.find(trait) != visitedTraits.end()) {
return MethodStatementPtr();
}
visitedTraits.insert(trait);
ClassStatementPtr tStmt =
dynamic_pointer_cast<ClassStatement>(trait->getStmt());
StatementListPtr tStmts = tStmt->getStmts();
// Look in the current trait
for (int s = 0; s < tStmts->getCount(); s++) {
MethodStatementPtr meth =
dynamic_pointer_cast<MethodStatement>((*tStmts)[s]);
if (meth) { // Handle methods
if (meth->getName() == methodName) {
return meth;
}
}
}
// Look into children traits
for (int s = 0; s < tStmts->getCount(); s++) {
UseTraitStatementPtr useTraitStmt =
dynamic_pointer_cast<UseTraitStatement>((*tStmts)[s]);
if (useTraitStmt) {
vector<string> usedTraits;
useTraitStmt->getUsedTraitNames(usedTraits);
for (unsigned i = 0; i < usedTraits.size(); i++) {
MethodStatementPtr foundMethod =
findTraitMethod(ar, ar->findClass(usedTraits[i]), methodName,
visitedTraits);
if (foundMethod) return foundMethod;
}
}
}
return MethodStatementPtr(); // not found
}
void ClassScope::findTraitMethodsToImport(AnalysisResultPtr ar,
ClassScopePtr trait) {
ClassStatementPtr tStmt =
dynamic_pointer_cast<ClassStatement>(trait->getStmt());
StatementListPtr tStmts = tStmt->getStmts();
if (!tStmts) return;
for (int s = 0; s < tStmts->getCount(); s++) {
MethodStatementPtr meth =
dynamic_pointer_cast<MethodStatement>((*tStmts)[s]);
if (meth) {
TraitMethod traitMethod(trait, meth, ModifierExpressionPtr(),
MethodStatementPtr());
addImportTraitMethod(traitMethod, meth->getName());
}
}
}
void ClassScope::applyTraitPrecRule(TraitPrecStatementPtr stmt) {
const string methodName = Util::toLower(stmt->getMethodName());
const string selectedTraitName = Util::toLower(stmt->getTraitName());
std::set<string> otherTraitNames;
stmt->getOtherTraitNames(otherTraitNames);
map<string,TraitMethodList>::iterator methIter =
m_importMethToTraitMap.find(methodName);
if (methIter == m_importMethToTraitMap.end()) {
Compiler::Error(Compiler::UnknownObjectMethod, stmt);
return;
}
bool foundSelectedTrait = false;
TraitMethodList &methList = methIter->second;
for (TraitMethodList::iterator nextTraitIter = methList.begin();
nextTraitIter != methList.end(); ) {
TraitMethodList::iterator traitIter = nextTraitIter++;
string availTraitName = traitIter->m_trait->getName();
if (availTraitName == selectedTraitName) {
foundSelectedTrait = true;
} else {
if (otherTraitNames.find(availTraitName) != otherTraitNames.end()) {
otherTraitNames.erase(availTraitName);
methList.erase(traitIter);
}
}
}
// Report error if didn't find the selected trait
if (!foundSelectedTrait) {
Compiler::Error(Compiler::UnknownTrait, stmt);
}
// Sanity checking: otherTraitNames should be empty now
if (otherTraitNames.size()) {
Compiler::Error(Compiler::UnknownTrait, stmt);
}
}
bool ClassScope::hasMethod(const string &methodName) const {
return m_functions.find(methodName) != m_functions.end();
}
ClassScopePtr
ClassScope::findSingleTraitWithMethod(AnalysisResultPtr ar,
const string &methodName) const {
ClassScopePtr trait = ClassScopePtr();
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]);
if (!tCls) continue;
if (tCls->hasMethod(methodName)) {
if (trait) { // more than one trait contains method
return ClassScopePtr();
}
trait = tCls;
}
}
return trait;
}
void ClassScope::addTraitAlias(TraitAliasStatementPtr aliasStmt) {
const string &traitName = aliasStmt->getTraitName();
const string &origMethName = aliasStmt->getMethodName();
const string &newMethName = aliasStmt->getNewMethodName();
string origName = traitName.empty() ? "(null)" : traitName;
origName += "::" + origMethName;
m_traitAliases.push_back(std::pair<string, string>(newMethName, origName));
}
void ClassScope::applyTraitAliasRule(AnalysisResultPtr ar,
TraitAliasStatementPtr stmt) {
const string traitName = Util::toLower(stmt->getTraitName());
const string origMethName = Util::toLower(stmt->getMethodName());
const string newMethName = Util::toLower(stmt->getNewMethodName());
// Get the trait's "class"
ClassScopePtr traitCls;
if (traitName.empty()) {
traitCls = findSingleTraitWithMethod(ar, origMethName);
} else {
traitCls = ar->findClass(traitName);
}
if (!traitCls || !(traitCls->isTrait())) {
Compiler::Error(Compiler::UnknownTrait, stmt);
return;
}
// Keep record of alias rule
addTraitAlias(stmt);
// Get the method
std::set<ClassScopePtr> visitedTraits;
MethodStatementPtr methStmt = findTraitMethod(ar, traitCls, origMethName,
visitedTraits);
if (!methStmt) {
Compiler::Error(Compiler::UnknownTraitMethod, stmt);
return;
}
if (origMethName == newMethName) {
setImportTraitMethodModifiers(origMethName, traitCls, stmt->getModifiers());
}
else {
// Insert renamed entry into the set of methods to be imported
TraitMethod traitMethod(traitCls, methStmt, stmt->getModifiers(), stmt,
stmt->getNewMethodName());
addImportTraitMethod(traitMethod, newMethName);
}
}
void ClassScope::applyTraitRules(AnalysisResultPtr ar) {
ClassStatementPtr classStmt = dynamic_pointer_cast<ClassStatement>(getStmt());
assert(classStmt);
StatementListPtr stmts = classStmt->getStmts();
if (!stmts) return;
for (int s = 0; s < stmts->getCount(); s++) {
StatementPtr stmt = (*stmts)[s];
UseTraitStatementPtr useStmt =
dynamic_pointer_cast<UseTraitStatement>(stmt);
if (!useStmt) continue;
StatementListPtr rules = useStmt->getStmts();
for (int r = 0; r < rules->getCount(); r++) {
StatementPtr rule = (*rules)[r];
TraitPrecStatementPtr precStmt =
dynamic_pointer_cast<TraitPrecStatement>(rule);
if (precStmt) {
applyTraitPrecRule(precStmt);
} else {
TraitAliasStatementPtr aliasStmt =
dynamic_pointer_cast<TraitAliasStatement>(rule);
assert(aliasStmt);
applyTraitAliasRule(ar, aliasStmt);
}
}
}
}
// This method removes trait abstract methods that are either:
// 1) implemented by other traits
// 2) duplicate
void ClassScope::removeSpareTraitAbstractMethods(AnalysisResultPtr ar) {
for (MethodToTraitListMap::iterator iter = m_importMethToTraitMap.begin();
iter != m_importMethToTraitMap.end(); iter++) {
TraitMethodList& tMethList = iter->second;
bool hasNonAbstractMeth = false;
unsigned countAbstractMeths = 0;
for (TraitMethodList::const_iterator traitMethIter = tMethList.begin();
traitMethIter != tMethList.end(); traitMethIter++) {
ModifierExpressionPtr modifiers = traitMethIter->m_modifiers ?
traitMethIter->m_modifiers : traitMethIter->m_method->getModifiers();
if (!(modifiers->isAbstract())) {
hasNonAbstractMeth = true;
} else {
countAbstractMeths++;
}
}
if (hasNonAbstractMeth || countAbstractMeths > 1) {
// Erase spare abstract declarations
bool firstAbstractMeth = true;
for (TraitMethodList::iterator nextTraitIter = tMethList.begin();
nextTraitIter != tMethList.end(); ) {
TraitMethodList::iterator traitIter = nextTraitIter++;
ModifierExpressionPtr modifiers = traitIter->m_modifiers ?
traitIter->m_modifiers : traitIter->m_method->getModifiers();
if (modifiers->isAbstract()) {
if (hasNonAbstractMeth || !firstAbstractMeth) {
tMethList.erase(traitIter);
}
firstAbstractMeth = false;
}
}
}
}
}
const string& ClassScope::getNewGeneratorName(
FunctionScopePtr genFuncScope, GeneratorRenameMap &genRenameMap) {
assert(genFuncScope->isGenerator());
const string& oldName = genFuncScope->getName();
GeneratorRenameMap::iterator mapIt = genRenameMap.find(oldName);
if (mapIt != genRenameMap.end()) {
return mapIt->second;
}
string newName = oldName + "_" +
lexical_cast<string>(genFuncScope->getNewID());
genRenameMap[oldName] = newName;
return genRenameMap[oldName];
}
void
ClassScope::renameCreateContinuationCalls(AnalysisResultPtr ar,
ConstructPtr c,
ImportedMethodMap &importedMethods) {
if (!c) return;
SimpleFunctionCallPtr funcCall = dynamic_pointer_cast<SimpleFunctionCall>(c);
if (funcCall && funcCall->getName() == "hphp_create_continuation") {
ExpressionListPtr params = funcCall->getParams();
assert(params->getCount() >= 2);
const string &oldClassName =
dynamic_pointer_cast<ScalarExpression>((*params)[0])->getString();
ClassScopePtr oldClassScope = ar->findClass(oldClassName);
if (!oldClassScope || !oldClassScope->isTrait()) return;
const string &oldGenName =
dynamic_pointer_cast<ScalarExpression>((*params)[1])->getString();
MethodStatementPtr origGenStmt = importedMethods[oldGenName];
assert(origGenStmt);
const string &newGenName = origGenStmt->getOriginalName();
ExpressionPtr newGenExpr = funcCall->makeScalarExpression(ar, newGenName);
ExpressionPtr newClsExpr = funcCall->makeScalarExpression(ar, getName());
(*params)[0] = newClsExpr;
(*params)[1] = newGenExpr;
funcCall->analyzeProgram(ar);
return;
}
for (int i=0; i < c->getKidCount(); i++) {
renameCreateContinuationCalls(ar, c->getNthKid(i), importedMethods);
}
}
void ClassScope::relinkGeneratorMethods(
AnalysisResultPtr ar,
ImportedMethodMap &importedMethods) {
for (ImportedMethodMap::const_iterator methIt =
importedMethods.begin(); methIt != importedMethods.end(); methIt++) {
MethodStatementPtr newMeth = methIt->second;
// Skip non-generator methods
if (!newMeth) continue;
if (newMeth->getOrigGeneratorFunc()) {
// Get corresponding original generator method in the current class
const string& origGenName = newMeth->getOrigGeneratorFunc()->getName();
MethodStatementPtr origGenStmt = importedMethods[origGenName];
assert(origGenStmt);
// It must be an orig gen func already, we're just updating to point
// to the corresponding method cloned from the trait
assert(origGenStmt->getGeneratorFunc());
newMeth->setOrigGeneratorFunc(origGenStmt);
origGenStmt->setGeneratorFunc(newMeth);
}
// OrigGenerator methods need to have their hphp_create_continuation calls
// patched to the new generator name.
if (newMeth->getGeneratorFunc()) {
renameCreateContinuationCalls(ar, newMeth, importedMethods);
}
}
}
void ClassScope::importUsedTraits(AnalysisResultPtr ar) {
if (m_traitStatus == FLATTENED) return;
if (m_traitStatus == BEING_FLATTENED) {
Compiler::Error(Compiler::CyclicDependentTraits, getStmt());
return;
}
if (m_usedTraitNames.size() == 0) {
m_traitStatus = FLATTENED;
return;
}
m_traitStatus = BEING_FLATTENED;
// First, make sure that parent classes have their traits imported
if (!m_parent.empty()) {
ClassScopePtr parent = ar->findClass(m_parent);
if (parent) {
parent->importUsedTraits(ar);
}
}
// Find trait methods to be imported
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]);
if (!tCls || !(tCls->isTrait())) {
setAttribute(UsesUnknownTrait);
Compiler::Error(Compiler::UnknownTrait, getStmt());
continue;
}
// First, make sure the used trait is flattened
tCls->importUsedTraits(ar);
findTraitMethodsToImport(ar, tCls);
}
// Apply rules
applyTraitRules(ar);
// Remove trait abstract methods provided by other traits and duplicates
removeSpareTraitAbstractMethods(ar);
// Apply precedence of current class over used traits
for (MethodToTraitListMap::iterator iter = m_importMethToTraitMap.begin();
iter != m_importMethToTraitMap.end(); ) {
MethodToTraitListMap::iterator thisiter = iter;
iter++;
if (findFunction(ar, thisiter->first, 0, 0) != FunctionScopePtr()) {
m_importMethToTraitMap.erase(thisiter);
}
}
std::map<string, MethodStatementPtr> importedTraitMethods;
std::vector<std::pair<string,const TraitMethod*> > importedTraitsWithOrigName;
GeneratorRenameMap genRenameMap;
// Actually import the methods
for (MethodToTraitListMap::const_iterator
iter = m_importMethToTraitMap.begin();
iter != m_importMethToTraitMap.end(); iter++) {
// The rules may rule out a method from all traits.
// In this case, simply don't import the method.
if (iter->second.size() == 0) {
continue;
}
// Consistency checking: each name must only refer to one imported method
if (iter->second.size() > 1) {
Compiler::Error(Compiler::MethodInMultipleTraits, getStmt());
} else {
TraitMethodList::const_iterator traitMethIter = iter->second.begin();
if ((traitMethIter->m_modifiers ? traitMethIter->m_modifiers :
traitMethIter->m_method->getModifiers())->isAbstract()) {
// Skip abstract methods, if method already exists in the class
if (findFunction(ar, iter->first, true) ||
importedTraitMethods.count(iter->first)) {
continue;
}
}
if (traitMethIter->m_modifiers &&
traitMethIter->m_modifiers->isStatic()) {
Compiler::Error(Compiler::InvalidAccessModifier,
traitMethIter->m_modifiers);
continue;
}
string sourceName = traitMethIter->m_ruleStmt ?
Util::toLower(((TraitAliasStatement*)traitMethIter->m_ruleStmt.get())->
getMethodName()) : iter->first;
importedTraitMethods[sourceName] = MethodStatementPtr();
importedTraitsWithOrigName.push_back(
make_pair(sourceName, &*traitMethIter));
}
}
for (unsigned i = 0; i < importedTraitsWithOrigName.size(); i++) {
const string &sourceName = importedTraitsWithOrigName[i].first;
const TraitMethod *traitMethod = importedTraitsWithOrigName[i].second;
MethodStatementPtr newMeth = importTraitMethod(
*traitMethod, ar, Util::toLower(traitMethod->m_originalName),
genRenameMap, importedTraitMethods);
if (newMeth) {
importedTraitMethods[sourceName] = newMeth;
}
}
// Relink generator and origGenerator methods
relinkGeneratorMethods(ar, importedTraitMethods);
// Import trait properties
importTraitProperties(ar);
m_traitStatus = FLATTENED;
}
bool ClassScope::usesTrait(const string &traitName) const {
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
if (traitName == m_usedTraitNames[i]) {
return true;
}
}
return false;
}
bool ClassScope::needsInvokeParent(AnalysisResultConstPtr ar,
bool considerSelf /* = true */) {
// check all functions this class has
if (considerSelf) {
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
if ((*iter)->isPrivate()) return true;
}
}
// walk up
if (!m_parent.empty()) {
ClassScopePtr super = ar->findClass(m_parent);
return !super || super->isRedeclaring() || super->needsInvokeParent(ar);
}
return false;
}
bool ClassScope::derivesDirectlyFrom(const std::string &base) const {
BOOST_FOREACH(std::string base_i, m_bases) {
if (strcasecmp(base_i.c_str(), base.c_str()) == 0) return true;
}
return false;
}
bool ClassScope::derivesFrom(AnalysisResultConstPtr ar,
const std::string &base,
bool strict, bool def) const {
if (derivesDirectlyFrom(base)) return true;
BOOST_FOREACH(std::string base_i, m_bases) {
ClassScopePtr cl = ar->findClass(base_i);
if (cl) {
if (strict && cl->isRedeclaring()) {
if (def) return true;
continue;
}
if (cl->derivesFrom(ar, base, strict, def)) return true;
}
}
return false;
}
ClassScopePtr ClassScope::FindCommonParent(AnalysisResultConstPtr ar,
const std::string &cn1,
const std::string &cn2) {
ClassScopePtr cls1 = ar->findClass(cn1);
ClassScopePtr cls2 = ar->findClass(cn2);
if (!cls1 || !cls2) return ClassScopePtr();
if (cls1->getName() == cls2->getName()) return cls1;
if (cls1->derivesFrom(ar, cn2, true, false)) return cls2;
if (cls2->derivesFrom(ar, cn1, true, false)) return cls1;
// walk up the class hierarchy.
BOOST_FOREACH(const std::string &base1, cls1->m_bases) {
BOOST_FOREACH(const std::string &base2, cls2->m_bases) {
ClassScopePtr parent = FindCommonParent(ar, base1, base2);
if (parent) return parent;
}
}
return ClassScopePtr();
}
void ClassScope::setVolatile() {
if (!m_volatile) {
m_volatile = true;
Lock lock(s_depsMutex);
const BlockScopeRawPtrFlagsVec &orderedUsers = getOrderedUsers();
for (BlockScopeRawPtrFlagsVec::const_iterator it = orderedUsers.begin(),
end = orderedUsers.end(); it != end; ++it) {
BlockScopeRawPtrFlagsVec::value_type pf = *it;
if (pf->second & UseKindParentRef) {
BlockScopeRawPtr scope = pf->first;
if (scope->is(BlockScope::ClassScope)) {
((HPHP::ClassScope*)scope.get())->setVolatile();
}
}
}
}
}
FunctionScopePtr ClassScope::findFunction(AnalysisResultConstPtr ar,
const std::string &name,
bool recursive,
bool exclIntfBase /* = false */) {
assert(Util::toLower(name) == name);
StringToFunctionScopePtrMap::const_iterator iter;
iter = m_functions.find(name);
if (iter != m_functions.end()) {
assert(iter->second);
return iter->second;
}
// walk up
if (recursive) {
int s = m_bases.size();
for (int i = 0; i < s; i++) {
const string &base = m_bases[i];
ClassScopePtr super = ar->findClass(base);
if (!super) continue;
if (exclIntfBase && super->isInterface()) break;
if (super->isRedeclaring()) {
if (base == m_parent) {
m_derivesFromRedeclaring = DirectFromRedeclared;
break;
}
continue;
}
FunctionScopePtr func =
super->findFunction(ar, name, true, exclIntfBase);
if (func) return func;
}
}
if (!Option::AllDynamic &&
derivesFromRedeclaring() == DirectFromRedeclared) {
setDynamic(ar, name);
}
return FunctionScopePtr();
}
FunctionScopePtr ClassScope::findConstructor(AnalysisResultConstPtr ar,
bool recursive) {
StringToFunctionScopePtrMap::const_iterator iter;
string name;
if (classNameCtor()) {
name = getName();
} else {
name = "__construct";
}
iter = m_functions.find(name);
if (iter != m_functions.end()) {
assert(iter->second);
return iter->second;
}
// walk up
if (recursive && derivesFromRedeclaring() != DirectFromRedeclared) {
ClassScopePtr super = ar->findClass(m_parent);
if (super) {
FunctionScopePtr func = super->findConstructor(ar, true);
if (func) return func;
}
}
if (!Option::AllDynamic &&
derivesFromRedeclaring() == DirectFromRedeclared) {
setDynamic(ar, name);
}
return FunctionScopePtr();
}
void ClassScope::setStaticDynamic(AnalysisResultConstPtr ar) {
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
FunctionScopePtr fs = *iter;
if (fs->isStatic()) fs->setDynamic();
}
if (!m_parent.empty()) {
if (derivesFromRedeclaring() == DirectFromRedeclared) {
const ClassScopePtrVec &parents = ar->findRedeclaredClasses(m_parent);
BOOST_FOREACH(ClassScopePtr cl, parents) {
cl->setStaticDynamic(ar);
}
} else {
ClassScopePtr parent = ar->findClass(m_parent);
if (parent) {
parent->setStaticDynamic(ar);
}
}
}
}
void ClassScope::setDynamic(AnalysisResultConstPtr ar,
const std::string &name) {
StringToFunctionScopePtrMap::const_iterator iter =
m_functions.find(name);
if (iter != m_functions.end()) {
FunctionScopePtr fs = iter->second;
fs->setDynamic();
} else if (!m_parent.empty()) {
if (derivesFromRedeclaring() == DirectFromRedeclared) {
const ClassScopePtrVec &parents = ar->findRedeclaredClasses(m_parent);
BOOST_FOREACH(ClassScopePtr cl, parents) {
cl->setDynamic(ar, name);
}
} else {
ClassScopePtr parent = ar->findClass(m_parent);
if (parent) {
parent->setDynamic(ar, name);
}
}
}
}
void ClassScope::setSystem() {
setAttribute(ClassScope::System);
m_volatile = m_dynamic = false;
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
(*iter)->setSystem();
}
}
bool ClassScope::needLazyStaticInitializer() {
return getVariables()->getAttribute(VariableTable::ContainsDynamicStatic) ||
getConstants()->hasDynamic();
}
void ClassScope::outputCPPClassMap(CodeGenerator &cg, AnalysisResultPtr ar) {
// header
int attribute = ClassInfo::IsNothing;
if (!isUserClass()) attribute |= ClassInfo::IsSystem;
if (isVolatile() && !isRedeclaring()) attribute |= ClassInfo::IsVolatile;
if (isInterface()) attribute |= ClassInfo::IsInterface|ClassInfo::IsAbstract;
if (isTrait()) attribute |= ClassInfo::IsTrait;
if (m_usedTraitNames.size() > 0) attribute |= ClassInfo::UsesTraits;
if (m_traitAliases.size() > 0) attribute |= ClassInfo::HasAliasedMethods;
if (m_kindOf == KindOfAbstractClass) attribute |= ClassInfo::IsAbstract;
if (m_kindOf == KindOfFinalClass) attribute |= ClassInfo::IsFinal;
if (needLazyStaticInitializer()) attribute |= ClassInfo::IsLazyInit;
attribute |= m_attributeClassInfo;
if (!m_docComment.empty() && Option::GenerateDocComments) {
attribute |= ClassInfo::HasDocComment;
} else {
attribute &= ~ClassInfo::HasDocComment;
}
cg_printf("(const char *)0x%04X, \"%s\", \"%s\", \"%s\", (const char *)%d, "
"(const char *)%d,\n", attribute,
CodeGenerator::EscapeLabel(getOriginalName()).c_str(),
CodeGenerator::EscapeLabel(m_parent).c_str(),
m_stmt ? m_stmt->getLocation()->file : "",
m_stmt ? m_stmt->getLocation()->line0 : 0,
m_stmt ? m_stmt->getLocation()->line1 : 0);
if (attribute & ClassInfo::IsVolatile) {
cg_printf("(const char *)offsetof(GlobalVariables, CDEC(%s)),\n",
CodeGenerator::FormatLabel(m_name).c_str());
}
if (!m_docComment.empty() && Option::GenerateDocComments) {
std::string dc = string_cplus_escape(m_docComment.c_str(), m_docComment.size());
cg_printf("\"%s\",\n", dc.c_str());
}
// parent interfaces
for (unsigned int i = (m_parent.empty() ? 0 : 1); i < m_bases.size(); i++) {
cg_printf("\"%s\", ", CodeGenerator::EscapeLabel(m_bases[i]).c_str());
}
cg_printf("NULL,\n");
// traits
if (attribute & ClassInfo::UsesTraits) {
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
cg_printf("\"%s\", ",
CodeGenerator::EscapeLabel(m_usedTraitNames[i]).c_str());
}
cg_printf("NULL,\n");
}
// trait alias rules
if (attribute & ClassInfo::HasAliasedMethods) {
for (unsigned i = 0; i < m_traitAliases.size(); i++) {
cg_printf("\"%s\", \"%s\", ",
CodeGenerator::EscapeLabel(m_traitAliases[i].first).c_str(),
CodeGenerator::EscapeLabel(m_traitAliases[i].second).c_str());
}
cg_printf("NULL,\n");
}
// methods
for (unsigned int i = 0; i < m_functionsVec.size(); i++) {
m_functionsVec[i]->outputCPPClassMap(cg, ar);
}
cg_printf("NULL,\n");
// properties
m_variables->outputCPPClassMap(cg, ar);
// constants
m_constants->outputCPPClassMap(cg, ar);
// user attributes
UserAttributeMap::const_iterator it = m_userAttributes.begin();
for (; it != m_userAttributes.end(); ++it) {
ExpressionPtr expr = it->second;
Variant v;
bool isScalar UNUSED = expr->getScalarValue(v);
assert(isScalar);
int valueLen = 0;
string valueText = SymbolTable::getEscapedText(v, valueLen);
cg_printf("\"%s\", (const char *)%d, \"%s\",\n",
CodeGenerator::EscapeLabel(it->first).c_str(),
valueLen, valueText.c_str());
}
cg_printf("NULL,\n");
}
bool ClassScope::hasConst(const string &name) const {
const Symbol *sym = m_constants->getSymbol(name);
assert(!sym || sym->isPresent());
return sym;
}
Symbol *ClassScope::findProperty(ClassScopePtr &cls,
const string &name,
AnalysisResultConstPtr ar) {
return getVariables()->findProperty(cls, name, ar);
}
TypePtr ClassScope::checkProperty(BlockScopeRawPtr context,
Symbol *sym, TypePtr type,
bool coerce, AnalysisResultConstPtr ar) {
return getVariables()->checkProperty(context, sym, type, coerce, ar);
}
TypePtr ClassScope::checkConst(BlockScopeRawPtr context,
const std::string &name, TypePtr type,
bool coerce, AnalysisResultConstPtr ar,
ConstructPtr construct,
const std::vector<std::string> &bases,
BlockScope *&defScope) {
defScope = nullptr;
return getConstants()->check(context, name, type, coerce,
ar, construct, m_bases, defScope);
}
void ClassScope::getAllParents(AnalysisResultConstPtr ar,
std::vector<std::string> &names) {
if (m_stmt) {
if (isInterface()) {
boost::dynamic_pointer_cast<InterfaceStatement>
(m_stmt)->getAllParents(ar, names);
} else {
boost::dynamic_pointer_cast<ClassStatement>
(m_stmt)->getAllParents(ar, names);
}
} else {
for (unsigned i = 0; i < m_bases.size(); i++) {
const string &base = m_bases[i];
names.push_back(base);
if (ClassScopePtr cls = ar->findClass(base)) {
if (!cls->isRedeclaring()) {
cls->getAllParents(ar, names);
}
}
}
}
}
void ClassScope::getInterfaces(AnalysisResultConstPtr ar,
std::vector<std::string> &names,
bool recursive /* = true */) const {
ClassScope *self = const_cast<ClassScope*>(this);
if (recursive && !m_parent.empty()) {
ClassScopePtr cls(ar->findClass(m_parent));
if (cls && cls->isRedeclaring()) {
cls = self->findExactClass(cls);
}
if (cls) cls->getInterfaces(ar, names, true);
}
if (!m_bases.empty()) {
vector<string>::const_iterator begin =
m_parent.empty() ? m_bases.begin() : m_bases.begin() + 1;
for (vector<string>::const_iterator it = begin;
it != m_bases.end(); ++it) {
ClassScopePtr cls(ar->findClass(*it));
if (cls && cls->isRedeclaring()) {
cls = self->findExactClass(cls);
}
if (cls) names.push_back(cls->getDocName());
else names.push_back(*it);
if (cls && recursive) {
cls->getInterfaces(ar, names, true);
}
}
}
}
ClassScopePtr ClassScope::getParentScope(AnalysisResultConstPtr ar) const {
if (m_parent.empty()) return ClassScopePtr();
return ar->findClass(m_parent);
}
void ClassScope::serialize(JSON::CodeError::OutputStream &out) const {
JSON::CodeError::MapStream ms(out);
std::map<string, int> propMap;
std::set<string> names;
m_variables->getNames(names);
BOOST_FOREACH(string name, names) {
int pm = 0;
if (m_variables->isPublic(name)) pm |= ClassScope::Public;
else if (m_variables->isPrivate(name)) pm |= ClassScope::Private;
else if (m_variables->isProtected(name)) pm |= ClassScope::Protected;
if (m_variables->isStatic(name)) pm |= ClassScope::Static;
propMap[name] = pm;
}
names.clear();
vector<string> cnames;
m_constants->getSymbols(cnames);
// What's a mod again?
ms.add("attributes", m_attribute)
.add("kind", m_kindOf)
.add("parent", m_parent)
.add("bases", m_bases)
.add("properties", propMap)
.add("functions", m_functions);
ms.add("consts");
JSON::CodeError::MapStream cs(out);
BOOST_FOREACH(string cname, cnames) {
TypePtr type = m_constants->getType(cname);
if (!type) {
cs.add(cname, -1);
} else if (type->isSpecificObject()) {
cs.add(cname, type->getName());
} else {
cs.add(cname, type->getKindOf());
}
}
cs.done();
ms.done();
}
static inline string GetDocName(AnalysisResultPtr ar,
BlockScopeRawPtr scope,
const string &name) {
ClassScopePtr c(ar->findClass(name));
if (c && c->isRedeclaring()) {
ClassScopePtr exact(scope->findExactClass(c));
return exact ?
exact->getDocName() :
c->getOriginalName(); // if we can't tell which redec class,
// then don't use the redec name
}
// TODO: pick a better way of signaling unknown?
return c ? c->getDocName() : "UnknownClass";
}
class GetDocNameFunctor {
public:
GetDocNameFunctor(AnalysisResultPtr ar, BlockScopeRawPtr scope) :
m_ar(ar), m_scope(scope) {}
inline string operator()(const string &name) const {
return GetDocName(m_ar, m_scope, name);
}
private:
AnalysisResultPtr m_ar;
BlockScopeRawPtr m_scope;
};
void ClassScope::serialize(JSON::DocTarget::OutputStream &out) const {
// TODO(stephentu): fix this hack
ClassScopeRawPtr self(const_cast<ClassScope*>(this));
JSON::DocTarget::MapStream ms(out);
ms.add("name", getDocName());
ms.add("line", getStmt() ? getStmt()->getLocation()->line0 : 0);
ms.add("docs", m_docComment);
ms.add("parent");
if (m_parent.empty()) {
out << JSON::Null;
} else {
out << GetDocName(out.analysisResult(), self, m_parent);
}
vector<string> ifaces;
getInterfaces(out.analysisResult(), ifaces, true);
vector<string> origIfaces;
origIfaces.resize(ifaces.size());
transform(ifaces.begin(), ifaces.end(), origIfaces.begin(),
GetDocNameFunctor(out.analysisResult(), self));
ms.add("interfaces", origIfaces);
int mods = 0;
// TODO: you should really only get one of these, we should assert this
if (m_kindOf == KindOfAbstractClass) mods |= ClassInfo::IsAbstract;
if (m_kindOf == KindOfFinalClass) mods |= ClassInfo::IsFinal;
if (m_kindOf == KindOfInterface) mods |= ClassInfo::IsInterface;
if (m_kindOf == KindOfTrait) mods |= ClassInfo::IsTrait;
ms.add("modifiers", mods);
FunctionScopePtrVec funcs;
getFunctionsFlattened(0, funcs);
ms.add("methods", funcs);
vector<Symbol*> rawSymbols;
getVariables()->getSymbols(rawSymbols, true);
vector<SymClassVarWrapper> wrappedSymbols;
for (vector<Symbol*>::iterator it = rawSymbols.begin();
it != rawSymbols.end(); ++it) {
wrappedSymbols.push_back(SymClassVarWrapper(*it));
}
ms.add("properties", wrappedSymbols);
// TODO: constants
ms.done();
}
void ClassScope::outputCPPHashTableClasses
(CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes,
const vector<const char*> &classes) {
bool system = cg.getOutput() == CodeGenerator::SystemCPP;
if (classes.size()) {
assert(cg.getCurrentIndentation() == 0);
const char text1[] =
"struct hashNodeCTD {\n"
" int64 hash;\n"
" int32 flags;\n"
" int32 cdec;\n"
" const char *name;\n"
" int64 ptv1;\n"
"};\n";
const char text2[] =
"#define GET_CS_OFFSET(n) offsetof(GlobalVariables, %s ## n)\n"
"inline ALWAYS_INLINE "
"const ObjectStaticCallbacks *getCallbacks(\n"
" const hashNodeCTD *p, CStrRef s) {\n"
" int64 off = p->ptv1;\n"
" if (LIKELY(!(off & 1))) return ((const ObjectStaticCallbacks *)off);\n"
" DECLARE_GLOBAL_VARIABLES(g);\n"
" if (UNLIKELY(!checkClassExistsNoThrow("
"s, (bool*)((char*)g + p->cdec)))) return 0;\n"
" if (LIKELY(!(off & 2))) /* volatile class */ return "
"((const ObjectStaticCallbacks *)(off-1));\n"
" /* redeclared class */\n"
" return *(ObjectStaticCallbacks**)((char*)g + (off-3));\n"
"}\n";
const char text3[] =
"\n"
"static const hashNodeCTD *\n"
"findCTD(CStrRef name) {\n"
" int64 hash = name->hash();\n"
" int o = ctdMapTable[hash & %d];\n"
" if (UNLIKELY(o < 0)) return NULL;\n"
" const hashNodeCTD *p = &ctdBuckets[o];\n"
" do {\n"
" int64 h = p->hash;\n"
" if (h == hash && "
"(LIKELY(p->name==name.data())||"
"LIKELY(!strcasecmp(p->name, name.data())))) return p;\n"
" } while (!(p++->flags & 1));\n"
" return NULL;\n"
"}\n";
JumpTable jt(cg, classes, true, true, true, true);
cg_print(text1);
if (!system) {
cg_printf(text2, Option::ClassStaticsCallbackPrefix);
}
cg_printf("static const hashNodeCTD ctdBuckets[] = {\n");
vector<int> offsets;
int prev = -1;
for (int n = 0; jt.ready(); ++n, jt.next()) {
int cur = jt.current();
if (prev != cur) {
while (++prev != cur) {
offsets.push_back(-1);
}
offsets.push_back(n);
}
const char *clsName = jt.key();
StringToClassScopePtrVecMap::const_iterator iterClasses =
classScopes.find(clsName);
ClassScopeRawPtr cls = iterClasses->second[0];
cg_printf(" {" STRHASH_FMT ",%d,",
hash_string_i(clsName), jt.last() ? 1 : 0);
if (cls->isVolatile()) {
cg_printf("offsetof(GlobalVariables,CDEC(%s))",
CodeGenerator::FormatLabel(cls->getName()).c_str());
} else {
cg_printf("0");
}
cg_printf(",\"%s\",", CodeGenerator::EscapeLabel(clsName).c_str());
if (cls->isRedeclaring()) {
assert(!system);
cg_printf("GET_CS_OFFSET(%s)+3",
CodeGenerator::FormatLabel(cls->getName()).c_str());
} else {
string clsFmt = CodeGenerator::FormatLabel(clsName);
cg_printf("(int64)&%s%s%s",
Option::ClassStaticsCallbackPrefix,
clsFmt.c_str(),
cls->isVolatile() ? "+1" : "");
}
cg_printf(" },\n");
}
cg_printf("};\n");
cg_indentBegin("static const int ctdMapTable[] = {\n");
for (int i = 0, e = jt.size(), s = offsets.size(); i < e; i++) {
cg_printf("%d,", i < s ? offsets[i] : -1);
if ((i & 7) == 7) cg_printf("\n");
}
cg_printf("\n");
cg_indentEnd("};\n");
cg_printf(text3, jt.size() - 1);
}
cg_indentBegin("const ObjectStaticCallbacks * "
"get%s_object_static_callbacks(CStrRef s) {\n",
system ? "_builtin" : "");
if (classes.size()) {
if (system) {
cg_printf("const hashNodeCTD *p = findCTD(s);\n"
"if (p) {\n"
" return "
"((const ObjectStaticCallbacks *)p->ptv1);\n"
"}\n"
"return NULL;\n");
} else {
cg_printf("const hashNodeCTD *p = findCTD(s);\n"
"if (!p) return get_builtin_object_static_callbacks(s);\n"
"return getCallbacks(p,s);\n");
}
} else {
if (system) {
cg_printf("return NULL;\n");
} else {
cg_printf("return get_builtin_object_static_callbacks(s);\n");
}
}
cg_indentEnd("}\n");
}
void ClassScope::outputCPPClassVarInitImpl
(CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes,
const vector<const char*> &classes) {
if (cg.getOutput() == CodeGenerator::SystemCPP) return;
cg_indentBegin("Variant get_class_var_init(CStrRef s, "
"const char *var) {\n");
cg_printf("const ObjectStaticCallbacks *cwo = "
"get_%sobject_static_callbacks(s);\n"
"return LIKELY(cwo != 0) ? "
"cwo->os_getInit(var) : throw_missing_class(s);\n",
!classes.size() ? "builtin_" : "");
cg_indentEnd("}\n");
}
void ClassScope::outputCPPGetCallInfoStaticMethodImpl(
CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes,
const vector<const char*> &classes) {
if (cg.getOutput() == CodeGenerator::SystemCPP) return;
cg_indentBegin(
"bool get_call_info_static_method(MethodCallPackage &mcp) {\n");
cg_printf("StringData *s ATTRIBUTE_UNUSED (mcp.rootCls);\n");
cg_printf("const ObjectStaticCallbacks *cwo = "
"get_%sobject_static_callbacks(s);\n"
"if (LIKELY(cwo != 0)) "
"return ObjectStaticCallbacks::GetCallInfo(cwo,mcp,-1);\n"
"if (mcp.m_fatal) throw_missing_class(s->data());\n"
"return false;\n",
!classes.size() ? "builtin_" : "");
cg_indentEnd("}\n");
}
void ClassScope::outputCPPGetStaticPropertyImpl
(CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes,
const vector<const char*> &classes) {
if (cg.getOutput() == CodeGenerator::SystemCPP) return;
cg_indentBegin("Variant get_static_property(CStrRef s, "
"const char *prop) {\n");
cg.printf("const ObjectStaticCallbacks * cwo = "
"get%s_object_static_callbacks(s);\n",
!classes.size() ? "_builtin" : "");
cg.printf("if (cwo) return cwo->os_get(prop);\n");
cg_printf("return null;\n");
cg_indentEnd("}\n");
cg_indentBegin("Variant *get_static_property_lv(CStrRef s, "
"const char *prop) {\n");
cg.printf("const ObjectStaticCallbacks * cwo = "
"get%s_object_static_callbacks(s);\n",
!classes.size() ? "_builtin" : "");
cg.printf("if (cwo) return &cwo->os_lval(prop);\n");
cg_printf("return NULL;\n");
cg_indentEnd("}\n");
}
void ClassScope::outputCPPGetClassConstantImpl
(CodeGenerator &cg, const StringToClassScopePtrVecMap &classScopes) {
if (cg.getOutput() == CodeGenerator::SystemCPP) return;
cg_indentBegin("Variant get_class_constant(CStrRef s, "
"const char *constant, int fatal /* = true */) {\n");
if (Option::EnableEval == Option::FullEval) {
// See if there's an eval'd version
cg_indentBegin("{\n");
cg_printf("Variant r;\n");
cg_printf("if (eval_get_class_constant_hook(r, s, constant)) "
"return r;\n");
cg_indentEnd("}\n");
}
cg.indentBegin("{\n");
cg.printf("const ObjectStaticCallbacks * cwo = "
"get%s_object_static_callbacks(s);\n",
!classScopes.size() ? "_builtin" : "");
cg.printf("if (cwo) return cwo->os_constant(constant);\n");
cg.indentEnd("}\n");
cg_indentBegin("if (fatal > 0) {\n");
cg_printf("raise_error(\"Couldn't find constant %%s::%%s\", s.data(), "
"constant);\n");
cg_indentEnd();
cg_indentBegin("} else if (!fatal) {\n");
cg_printf("raise_warning(\"Couldn't find constant %%s::%%s\", s.data(), "
"constant);\n");
cg_indentEnd("}\n");
cg_printf("return null;\n");
cg_indentEnd("}\n");
}
static int propTableSize(int entries) {
if (!entries) return 0;
int size = Util::roundUpToPowerOfTwo(entries * 2);
return size < 8 ? 8 : size;
}
static void getConstantEntries(ClassScopeRawPtr cls, bool system,
vector<const Symbol *> &entries) {
const std::vector<Symbol*> &constVec =
cls->getConstants()->getSymbols();
for (unsigned j = 0; j < constVec.size(); j++) {
const Symbol *sym = constVec[j];
if (!system && !sym->getValue()) continue;
entries.push_back(sym);
}
}
static bool buildClassPropTableMap(
CodeGenerator &cg, AnalysisResultPtr ar,
const StringToClassScopePtrVecMap &classScopes,
ClassScope::ClassPropTableMap &tables) {
typedef ClassScope::IndexedSym IndexedSym;
bool system = cg.getOutput() == CodeGenerator::SystemCPP;
bool ret = false;
for (StringToClassScopePtrVecMap::const_iterator iter = classScopes.begin();
iter != classScopes.end(); ++iter) {
const ClassScopePtrVec &classes = iter->second;
if (system) assert(classes.size() == 1);
for (unsigned int i = 0; i < classes.size(); i++) {
ClassScopePtr cls = classes[i];
vector<const Symbol *> entries[3];
const std::vector<Symbol*> &symbolVec =
cls->getVariables()->getSymbols();
for (unsigned j = 0; j < symbolVec.size(); j++) {
const Symbol *sym = symbolVec[j];
always_assert(!sym->isStatic() || !sym->isOverride());
entries[sym->isStatic()].push_back(sym);
}
getConstantEntries(cls, system, entries[2]);
for (unsigned k = 0; k < cls->getBases().size(); k++) {
const string &base = cls->getBases()[k];
if (k > 0 || base != cls->getOriginalParent()) {
ClassScopeRawPtr bCls = ar->findExactClass(cls->getStmt(), base);
if (!bCls) continue;
getConstantEntries(bCls, system, entries[2]);
}
}
if (!entries[0].size() && !entries[1].size() && !entries[2].size()) {
continue;
}
ret = true;
ClassScope::ClassPropTableInfo &info = tables[cls->getId()];
info.cls = cls;
for (int s = 0; s < 3; s++) {
int tableSize = propTableSize(entries[s].size());
unsigned p = 0;
for (unsigned j = 0; j < entries[s].size(); j++) {
const Symbol *sym = entries[s][j];
strhash_t hash = hash_string_i(sym->getName().c_str(),
sym->getName().size()) & (tableSize - 1);
int pix = -1;
switch (s) {
case 0:
if (sym->isPrivate() ||
(sym->isProtected() && !sym->isOverride())) {
pix = p++;
}
break;
case 1:
if (static_pointer_cast<Expression>(sym->getClassInitVal())->
containsDynamicConstant(ar)) {
pix = p++;
}
break;
case 2:
if (sym->isDynamic()) {
pix = p++;
}
break;
}
info.syms[s][hash].push_back(IndexedSym(j, pix, sym));
}
info.actualIndex[s].resize(entries[s].size() + 1, -1);
info.privateIndex[s].resize(p);
int newIndex = 0;
for (map<int, vector<IndexedSym> >::iterator it = info.syms[s].begin(),
end = info.syms[s].end(); it != end; ++it) {
const vector<IndexedSym> &v = it->second;
for (int k = 0, sz = v.size(); k < sz; k++, newIndex++) {
const IndexedSym &is = v[k];
info.actualIndex[s][is.origIndex] = newIndex;
if (is.privIndex >= 0) {
info.privateIndex[s][is.privIndex] = newIndex;
}
}
}
}
}
}
return ret;
}
static bool hasConstTable(ClassScopeRawPtr cls) {
ConstantTablePtr constants = cls->getConstants();
const std::vector<Symbol*> &constVec = constants->getSymbols();
if (!constVec.size()) return false;
for (int i = 0, sz = constVec.size(); i < sz; i++) {
const Symbol *sym = constVec[i];
if (cls->getAttribute(ClassScope::System) || sym->getValue()) return true;
}
return false;
}
bool ClassScope::checkHasPropTable(AnalysisResultConstPtr ar) {
VariableTablePtr variables = getVariables();
if (variables->getSymbols().size()) return true;
if (hasConstTable(ClassScopeRawPtr(this))) return true;
for (unsigned k = 0; k < m_bases.size(); k++) {
const string &base = m_bases[k];
if (k > 0 || base != m_parent) {
ClassScopeRawPtr bCls = ar->findExactClass(getStmt(), base);
if (bCls && hasConstTable(bCls)) return true;
}
}
return false;
}
ClassScopePtr ClassScope::getNextParentWithProp(AnalysisResultPtr ar) {
if (derivesFromRedeclaring() == DirectFromRedeclared) return ClassScopePtr();
ClassScopePtr parentCls = getParentScope(ar);
while (parentCls && !parentCls->checkHasPropTable(ar)) {
parentCls = parentCls->getParentScope(ar);
}
return parentCls;
}
enum {
ConstValid = 1,
ConstDynamic = 2,
ConstRedeclared = 4,
ConstDerivesFromRedeclared = 8,
ConstMagicIO = 16,
ConstNeedsName = 32,
ConstNeedsClass = 64,
ConstNeedsSysCon = 128,
ConstNonScalarValue = 256,
ConstPlain = 0x200,
ConstNull = 0x400,
ConstFalse = 0x800,
ConstZero = 0x1000,
ConstDZero = 0x2000,
};
static string findScalar(ExpressionPtr val,
string &name, string &cls, int *flags = 0) {
if (flags) *flags = 0;
if (!val) {
if (flags) *flags |= ConstNull | ConstPlain;
return "N;";
}
Variant v;
if (val->getScalarValue(v)) {
if (flags) {
*flags |= ConstPlain;
if (!v.toBoolean()) {
if (v.isNull()) {
*flags |= ConstNull;
} else if (v.isBoolean()) {
*flags |= ConstFalse;
} else if (v.isInteger()) {
*flags |= ConstZero;
} else if (v.isDouble()) {
*flags |= ConstDZero;
}
}
}
return f_serialize(v).c_str();
}
if (val->is(Expression::KindOfConstantExpression)) {
ConstantExpressionPtr con = static_pointer_cast<ConstantExpression>(val);
name = con->getName();
if (flags) {
if (con->isValid()) {
*flags |= ConstValid;
if (con->isDynamic()) {
*flags |= ConstDynamic | ConstNeedsName;
}
if (name == "STDIN" || name == "STDOUT" || name == "STDERR") {
*flags |= ConstMagicIO;
}
} else {
*flags |= ConstNeedsName;
}
}
cls = "";
} else if (val->is(Expression::KindOfClassConstantExpression)) {
ClassConstantExpressionPtr con =
static_pointer_cast<ClassConstantExpression>(val);
name = con->getConName();
cls = con->getActualClassName();
if (flags) {
if (con->isValid()) {
*flags |= ConstValid;
if (con->isDynamic()) *flags |= ConstDynamic;
} else if (con->isRedeclared()) {
*flags |= ConstRedeclared | ConstNeedsName;
} else if (con->hasClass()) {
*flags |= ConstDerivesFromRedeclared | ConstNeedsName;
} else {
*flags |= ConstNeedsName | ConstNeedsClass;
}
}
} else {
if (flags) *flags |= ConstNonScalarValue;
return val->getText(false, true);
}
return cls + "::" + name;
}
static ExpressionPtr getSymInit(const Symbol *sym) {
ConstructPtr c;
if (sym->isConstant()) {
c = sym->getValue();
} else {
c = sym->getClassInitVal();
}
return static_pointer_cast<Expression>(c);
}
void ClassScope::outputCPPGetClassPropTableImpl(
CodeGenerator &cg, AnalysisResultPtr ar,
const StringToClassScopePtrVecMap &classScopes,
bool extension /* = false */) {
bool system = cg.getOutput() == CodeGenerator::SystemCPP;
ClassPropTableMap tables;
if (!buildClassPropTableMap(cg, ar, classScopes, tables)) return;
cg.printSection("Class tables");
int s; // 1 => static
int index = 0;
std::map<string,int> svarIndex;
std::map<string,ExpressionRawPtr> nonScalarArrays;
if (!Option::UseScalarVariant) {
cg_indentBegin("static Variant %sstatic_init_vals[] = {\n",
Option::ClassPropTablePrefix);
}
for (ClassPropTableMap::const_iterator iter = tables.begin();
iter != tables.end(); iter++) {
const ClassPropTableInfo &info = iter->second;
cg.setLiteralScope(info.cls->getContainingFile());
for (s = 3; s--; ) {
if (!info.syms[s].size()) continue;
for (map<int, vector<IndexedSym> >::const_iterator
it = info.syms[s].begin(), end = info.syms[s].end();
it != end; ++it) {
const vector<IndexedSym> &v = it->second;
for (int k = 0, sz = v.size(); k < sz; k++) {
const Symbol *sym = v[k].sym;
ExpressionPtr val = getSymInit(sym);
int flags = 0;
string name,cls,id;
if (!val) continue;
id = findScalar(val, name, cls, &flags);
if (flags & ConstNonScalarValue) {
nonScalarArrays[id] = val;
continue;
}
if (Option::UseScalarVariant) {
continue;
}
if (name.empty()) {
int *p = &svarIndex[id];
if (!*p) {
val->outputCPP(cg, ar);
cg_printf(",\n");
*p = ++index;
}
}
if (flags & ConstNeedsName) {
int *p = &svarIndex[f_serialize(name).c_str()];
if (!*p) {
*p = ++index;
ScalarExpression::OutputCPPString(name, cg, ar,
info.cls, true);
}
}
if (flags & ConstNeedsClass) {
int *p = &svarIndex[f_serialize(cls).c_str()];
if (!*p) {
*p = ++index;
ScalarExpression::OutputCPPString(cls, cg, ar,
info.cls, true);
}
}
}
}
}
}
cg.setLiteralScope(FileScopeRawPtr());
if (!Option::UseScalarVariant) {
cg_indentEnd("};\n");
}
index = 0;
std::map<string,int> siIndex;
for (std::map<string,ExpressionRawPtr>::iterator
it = nonScalarArrays.begin(), end = nonScalarArrays.end();
it != end; ++it) {
siIndex[it->first] = index += 2;
cg_indentBegin("static Variant %snon_scalar_constant_%d() {\n",
Option::ClassPropTablePrefix, index);
cg.printDeclareGlobals();
cg_printf("return ");
it->second->outputCPP(cg, ar);
cg_printf(";\n");
cg_indentEnd("}\n");
}
nonScalarArrays.clear();
cg_indentBegin("static const int64 %sstatic_inits[] = {\n",
Option::ClassPropTablePrefix);
for (int i = 0; i < index; i += 2) {
cg_printf("(int64)%snon_scalar_constant_%d, 0x%07x6,\n",
Option::ClassPropTablePrefix, i+2, i);
}
for (ClassPropTableMap::const_iterator iter = tables.begin();
iter != tables.end(); iter++) {
const ClassPropTableInfo &info = iter->second;
cg.setLiteralScope(info.cls->getContainingFile());
for (s = 3; s--; ) {
if (!info.syms[s].size()) continue;
for (map<int, vector<IndexedSym> >::const_iterator
it = info.syms[s].begin(), end = info.syms[s].end();
it != end; ++it) {
const vector<IndexedSym> &v = it->second;
for (int k = 0, sz = v.size(); k < sz; k++) {
const Symbol *sym = v[k].sym;
ExpressionPtr val = getSymInit(sym);
string name, cls, id;
int flags = 0;
DataType type = KindOfUnknown;
if (!val) {
if (sym->isConstant()) {
name = sym->getName();
cls = info.cls->getId();
id = cls + "::" + name;
flags = ConstValid|ConstNeedsSysCon;
type = sym->getFinalType()->getDataType();
} else {
flags |= ConstPlain;
id = "N;";
}
} else {
id = findScalar(val, name, cls, &flags);
if (flags & ConstNonScalarValue) continue;
if (id == "N;") {
val.reset();
} else if (flags & ConstValid &&
!(flags & (ConstDynamic|ConstMagicIO))) {
flags |= ConstNeedsSysCon;
type = val->getActualType()->getDataType();
}
}
int *p;
int name_ix = -1, cls_ix = -1;
if ((flags & ConstNeedsSysCon) && type != KindOfUnknown) {
string n = cls + "$$" + name;
p = &siIndex[n];
if (!*p) {
if (cls.size()) {
cg_printf("(int64)&%s%s%s%s,\n",
Option::ClassConstantPrefix,
cls.c_str(),
Option::IdPrefix.c_str(),
CodeGenerator::FormatLabel(name).c_str());
} else {
cg_printf("(int64)&%s%s,\n",
Option::ConstantPrefix,
CodeGenerator::FormatLabel(name).c_str());
}
*p = ++index;
}
name_ix = *p - 1;
}
if (flags & ConstNeedsName) {
string n = f_serialize(name).c_str();
p = &siIndex[n];
if (!*p) {
cg_printf("(int64)&");
if (Option::UseScalarVariant) {
cg.setScalarVariant();
ScalarExpression::OutputCPPString(name, cg, ar,
info.cls, true);
cg.clearScalarVariant();
} else {
cg_printf("%sstatic_init_vals[%d]",
Option::ClassPropTablePrefix,
svarIndex[n] - 1);
}
cg_printf(",\n");
*p = ++index;
}
name_ix = *p - 1;
}
if (flags & ConstNeedsClass) {
string n = f_serialize(cls).c_str();
p = &siIndex[n];
if (!*p) {
cg_printf("(int64)&");
if (Option::UseScalarVariant) {
cg.setScalarVariant();
ScalarExpression::OutputCPPString(cls, cg, ar,
info.cls, true);
cg.clearScalarVariant();
} else {
cg_printf("%sstatic_init_vals[%d]",
Option::ClassPropTablePrefix,
svarIndex[n] - 1);
}
cg_printf(",\n");
*p = ++index;
}
cls_ix = *p - 1;
} else if (cls.size() &&
flags & (ConstDerivesFromRedeclared|ConstDynamic)) {
string n = Option::ClassStaticsCallbackPrefix + cls;
p = &siIndex[n];
if (!*p) {
cg_printf("(int64)&%s,\n", n.c_str());
*p = ++index;
}
cls_ix = *p - 1;
}
p = &siIndex[id];
if (!*p) {
if (flags & ConstPlain) {
if (!val) {
cg_printf("(int64)&null_variant");
} else {
cg_printf("(int64)&");
if (Option::UseScalarVariant) {
cg.setScalarVariant();
val->outputCPP(cg, ar);
cg.clearScalarVariant();
} else {
cg_printf("%sstatic_init_vals[%d]",
Option::ClassPropTablePrefix, svarIndex[id] - 1);
}
}
} else if ((flags & ConstNeedsSysCon) && type != KindOfUnknown) {
cg_printf("0x%08x%07x7", name_ix, int(type));
} else if (flags & ConstMagicIO) {
cg_printf("(int64)&BuiltinFiles::Get%s, 0x1%07x6",
name.c_str(), index++);
} else if (flags & ConstValid && !(flags & ConstDynamic)) {
if (Type::IsMappedToVariant(val->getActualType())) {
cg_printf("(int64)&");
val->outputCPP(cg, ar);
} else {
cg_printf("(int64)&%sstatic_init_vals[%d]",
Option::ClassPropTablePrefix, svarIndex[id] - 1);
}
} else if (cls.empty()) {
if (flags & ConstDynamic) {
cg_printf("(int64(offsetof(%s,%s%s))<<32)+",
system ? "SystemGlobals" : "GlobalVariables",
Option::ConstantPrefix,
CodeGenerator::FormatLabel(name).c_str());
}
cg_printf("int64_t(0x%xd)", name_ix);
} else if (flags & ConstRedeclared) {
cg_printf("offsetof(%s,%s%s)+0x%x00000003",
system ? "SystemGlobals" : "GlobalVariables",
Option::ClassStaticsCallbackPrefix,
CodeGenerator::FormatLabel(cls).c_str(),
name_ix);
} else if (flags & ConstDynamic) {
cg_printf("(int64(offsetof(%s,%s%s%s%s)<<32))+0x%x2",
system ? "SystemGlobals" : "GlobalVariables",
Option::ClassConstantPrefix,
cls.c_str(),
Option::IdPrefix.c_str(),
CodeGenerator::FormatLabel(name).c_str(),
cls_ix);
} else {
cg_printf("0x%x%07x%dL", name_ix, cls_ix,
flags & ConstDerivesFromRedeclared ? 4 : 5);
}
*p = ++index;
cg_printf(",\n");
}
}
}
}
}
cg_indentEnd("};\n");
vector<int> static_inits;
int curEntry = 0;
cg_indentBegin("static const ClassPropTableEntry %stable_entries[] = {\n",
Option::ClassPropTablePrefix);
for (ClassPropTableMap::const_iterator iter = tables.begin();
iter != tables.end(); iter++) {
const ClassPropTableInfo &info = iter->second;
cg.setLiteralScope(info.cls->getContainingFile());
for (s = 3; s--; ) {
if (!info.syms[s].size()) continue;
ClassScopePtr cls = info.cls;
for (map<int, vector<IndexedSym> >::const_iterator
it = info.syms[s].begin(), end = info.syms[s].end();
it != end; ++it) {
const vector<IndexedSym> &v = it->second;
for (int k = 0, sz = v.size(); k < sz; k++) {
const Symbol *sym = v[k].sym;
int cur = info.actualIndex[s][v[k].origIndex];
int next = info.actualIndex[s][v[k].origIndex + 1];
if (next < 0) next = cur;
int off;
int cflags = 0;
{
ExpressionPtr val = getSymInit(sym);
if (!val && sym->isConstant()) {
off = siIndex[info.cls->getId() + "::" + sym->getName()] - 1;
} else {
string name;
string cls;
string id = findScalar(val, name, cls, &cflags);
off = siIndex[id] - 1;
}
always_assert(off >= 0);
}
string prop(sym->getName());
int flags = 0;
DataType ptype = sym->getFinalType()->getDataType();
if (sym->isStatic()) {
flags |= ClassPropTableEntry::Static;
if (v[k].privIndex < 0) {
bool needsInit = true;
switch (ptype) {
case KindOfBoolean:
if (cflags & ConstFalse) needsInit = false;
goto check_plain;
case KindOfInt64:
if (cflags & ConstZero) needsInit = false;
goto check_plain;
case KindOfDouble:
if (cflags & ConstDZero) needsInit = false;
goto check_plain;
case KindOfString:
case KindOfArray:
if (cflags & ConstNull) needsInit = false;
case KindOfUnknown:
check_plain:
if (cflags & ConstPlain && !system) {
flags |= ClassPropTableEntry::FastInit;
}
break;
case KindOfObject:
assert(cflags & ConstNull);
needsInit = false;
break;
default:
break;
}
if (needsInit) {
static_inits.push_back(curEntry);
}
}
}
if (sym->isPublic()) flags |= ClassPropTableEntry::Public;
if (sym->isProtected()) flags |= ClassPropTableEntry::Protected;
if (sym->isConstant()) flags |= ClassPropTableEntry::Constant;
if (sym->isPrivate()) {
assert(!sym->isOverride());
flags |= ClassPropTableEntry::Private;
if (!sym->isStatic()) {
prop = '\0' + cls->getOriginalName() + '\0' + prop;
}
} else {
if (sym->isProtected() && !sym->isStatic()) {
prop = string("\0*\0", 3) + prop;
}
if (sym->isOverride() ||
(!sym->isStatic() && cls->derivesFromRedeclaring())) {
assert(!system);
flags |= ClassPropTableEntry::Override;
}
}
if (k == sz - 1) flags |= ClassPropTableEntry::Last;
curEntry++;
assert(int16_t(next - cur) == int32_t(next - cur));
assert(int16_t(off) == int32_t(off));
cg_printf("{" STRHASH_FMT ",%d,%d,%d,%d,%d,",
hash_string(sym->getName().c_str(),
sym->getName().size()),
next - cur, off,
int(s ? 0 : prop.size() - sym->getName().size()),
flags, int(ptype));
if (s) {
if (s == 2 && !sym->isDynamic()) {
cg_printf("0,");
} else {
cg_printf("offsetof(%s,%s%s%s%s),",
system ? "SystemGlobals" : "GlobalVariables",
s == 2 ?
Option::Option::ClassConstantPrefix :
Option::Option::StaticPropertyPrefix,
cls->getId().c_str(),
Option::IdPrefix.c_str(), sym->getName().c_str());
}
} else {
if (flags & ClassPropTableEntry::Override &&
cls->derivesFromRedeclaring()) {
cg_printf("0,");
} else {
cg_printf("GET_PROPERTY_OFFSET(%s%s, %s%s),",
Option::ClassPrefix, cls->getId().c_str(),
Option::PropertyPrefix, sym->getName().c_str());
}
}
cg_printf("&");
cg_printString(prop, ar, cls);
cg_printf(" },\n");
}
}
cg_printf("\n");
}
}
cg_indentEnd("};\n");
cg.setLiteralScope(FileScopeRawPtr());
cg_indentBegin("static const int %shash_entries[] = {\n",
Option::ClassPropTablePrefix);
curEntry = 0;
for (ClassPropTableMap::const_iterator iter = tables.begin();
iter != tables.end(); iter++) {
const ClassPropTableInfo &info = iter->second;
cg_printf("// %s hash\n", info.cls->getId().c_str());
int m = 0;
int off[3];
for (s = 3; s--; ) {
off[s] = m;
if (!info.syms[s].size()) continue;
int n = 0;
vector<int> offsets;
for (map<int, vector<IndexedSym> >::const_iterator
it = info.syms[s].begin(), end = info.syms[s].end();
it != end; ++it) {
while (n < it->first) {
offsets.push_back(-1);
n++;
}
offsets.push_back(m);
n++;
m += it->second.size();
}
int sz = propTableSize(info.actualIndex[s].size() - 1);
always_assert(n <= sz);
while (n < sz) {
offsets.push_back(-1);
n++;
}
if (s) {
while (n--) {
cg_printf("%d,", offsets[n]);
}
} else {
for (n = 0; n < sz; n++) {
cg_printf("%d,", offsets[n]);
}
}
cg_printf("\n");
curEntry += sz;
}
cg_printf("// %s lists\n", info.cls->getId().c_str());
for (s = 0; s < 3; s++) {
unsigned psize=info.privateIndex[s].size();
for (unsigned i = 0; i < psize; i++) {
cg_printf("%d,", off[s] + info.privateIndex[s][i]);
}
cg_printf("-1,\n");
curEntry += psize + 1;
}
}
if (static_inits.size()) {
cg_printf("%d,", (int)static_inits.size());
for (unsigned i = 0; i < static_inits.size(); i++) {
cg_printf("%d,", static_inits[i]);
}
}
cg_indentEnd("};\n");
int curOffset = 0;
int hashOffset = 0;
for (ClassPropTableMap::const_iterator iter = tables.begin();
iter != tables.end(); iter++) {
const ClassPropTableInfo &info = iter->second;
cg_indentBegin("const ClassPropTable %s%s::%sprop_table = {\n",
Option::ClassPrefix, iter->first.c_str(),
Option::ObjectStaticPrefix);
int tableSizeN = propTableSize(info.actualIndex[0].size() - 1);
int tableSizeS = propTableSize(info.actualIndex[1].size() - 1);
int tableSizeC = propTableSize(info.actualIndex[2].size() - 1);
cg_printf("%d,%d,%d,%d,%d,%d,%d,",
tableSizeN - 1,
tableSizeN ?
int(info.actualIndex[2].size() + info.actualIndex[1].size()) +
info.actualIndex[0][0] - 2 : -1,
tableSizeS - 1,
int(tableSizeS ? info.actualIndex[2].size() +
info.actualIndex[1][0] - 1 : -1),
tableSizeC - 1,
tableSizeC ? info.actualIndex[2][0] : -1,
int(tableSizeN + info.privateIndex[0].size() + 1));
if (info.cls->needLazyStaticInitializer()) {
cg_printf("offsetof(%s,%s%s),",
system ? "SystemGlobals" : "GlobalVariables",
Option::ClassStaticInitializerFlagPrefix,
info.cls->getId().c_str());
} else {
cg_printf("0,\n");
}
hashOffset += tableSizeS + tableSizeC;
cg_printf("%shash_entries+%d,",
Option::ClassPropTablePrefix, hashOffset);
hashOffset += tableSizeN + 3 +
info.privateIndex[0].size() +
info.privateIndex[1].size() +
info.privateIndex[2].size();
ClassScopePtr parentCls = info.cls->getNextParentWithProp(ar);
if (parentCls) {
cg_printf("&%s%s::%sprop_table,",
Option::ClassPrefix, parentCls->getId().c_str(),
Option::ObjectStaticPrefix);
} else {
cg_printf("0,");
}
cg_printf("%stable_entries+%d,",
Option::ClassPropTablePrefix, curOffset);
if (siIndex.size()) {
cg_printf("%sstatic_inits\n", Option::ClassPropTablePrefix);
} else {
cg_printf("0\n");
}
cg_indentEnd("};\n");
curOffset += info.actualIndex[0].size() + info.actualIndex[1].size() +
info.actualIndex[2].size() - 3;
}
if (static_inits.size()) {
ClassPropTableMap::const_iterator iter = tables.begin();
cg_printf("//const Globals::StaticInits %sstatic_initializer("
"&%s%s::%sprop_table, %shash_entries+%d);\n",
Option::ClassPropTablePrefix,
Option::ClassPrefix, iter->first.c_str(),
Option::ObjectStaticPrefix,
Option::ClassPropTablePrefix, curEntry);
}
}
void ClassScope::outputForwardDeclaration(CodeGenerator &cg) {
string clsNameStr = getId();
const char *clsName = clsNameStr.c_str();
if (!isInterface()) {
cg_printf("FORWARD_DECLARE_CLASS(%s);\n", clsName);
} else if (!Option::UseVirtualDispatch || isRedeclaring()) {
cg_printf("FORWARD_DECLARE_GENERIC_INTERFACE(%s);\n", clsName);
} else {
cg_printf("FORWARD_DECLARE_INTERFACE(%s);\n", clsName);
}
}
bool ClassScope::hasProperty(const string &name) const {
const Symbol *sym = m_variables->getSymbol(name);
assert(!sym || sym->isPresent());
return sym;
}
void ClassScope::setRedeclaring(AnalysisResultConstPtr ar, int redecId) {
if (isTrait()) {
Compiler::Error(Compiler::RedeclaredTrait, m_stmt);
}
m_redeclaring = redecId;
setVolatile(); // redeclared class is also volatile
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
(*iter)->setDynamic();
}
}
ClassScopePtr ClassScope::getRootParent(AnalysisResultConstPtr ar,
const std::string &methodName) {
ClassScopePtr root = dynamic_pointer_cast<ClassScope>(shared_from_this());
for (ClassScopePtr cls = getParentScope(ar); cls;
cls = cls->getParentScope(ar)) {
if (methodName.empty() ||
cls->m_functions.find(methodName) != cls->m_functions.end()) {
root = cls;
}
}
return root;
}
void ClassScope::getRootParents(AnalysisResultConstPtr ar,
const std::string &methodName,
ClassScopePtrVec &roots,
ClassScopePtr curClass) {
ClassScopePtr root = dynamic_pointer_cast<ClassScope>(shared_from_this());
if (m_parent.empty()) {
roots.push_back(curClass);
} else {
ClassScopePtrVec parents = ar->findRedeclaredClasses(m_parent);
for (unsigned int i = 0; i < parents.size(); i++) {
ClassScopePtr cls = parents[i];
if (methodName.empty() ||
cls->m_functions.find(methodName) != cls->m_functions.end()) {
curClass = cls;
}
cls->getRootParents(ar, methodName, roots, curClass);
}
}
}
string ClassScope::getBaseHeaderFilename() {
FileScopePtr file = getContainingFile();
assert(file);
string fileBase = file->outputFilebase();
string headerFile = Option::ClassHeaderPrefix;
headerFile += getId();
return headerFile;
}
string ClassScope::getHeaderFilename() {
return getBaseHeaderFilename() + ".h";
}
void ClassScope::outputCPPDef(CodeGenerator &cg) {
if (isVolatile()) {
string name = CodeGenerator::FormatLabel(m_name);
if (isRedeclaring()) {
cg_indentBegin("if (g->CDEC(%s) && g->%s%s != &%s%s) {\n",
name.c_str(),
Option::ClassStaticsCallbackPrefix,
name.c_str(),
Option::ClassStaticsCallbackPrefix,
getId().c_str());
cg_printf("raise_error(\"Class already declared: %s\");\n",
CodeGenerator::EscapeLabel(getOriginalName()).c_str());
cg_indentEnd("}\n");
cg_printf("g->%s%s = &%s%s;\n",
Option::ClassStaticsCallbackPrefix,
name.c_str(),
Option::ClassStaticsCallbackPrefix,
getId().c_str());
}
cg_printf("g->CDEC(%s) = true;\n", name.c_str());
}
}
void ClassScope::outputCPPHeader(AnalysisResultPtr ar,
CodeGenerator::Output output) {
string filename = getHeaderFilename();
string root = ar->getOutputPath() + "/";
Util::mkdir(root + filename);
std::ofstream f((root + filename).c_str());
CodeGenerator cg(&f, output);
cg.setFileOrClassHeader(true);
cg.headerBegin(filename);
if (Option::GenerateCppLibCode ||
cg.getOutput() == CodeGenerator::SystemCPP) {
cg.printBasicIncludes();
}
// 1. includes
BOOST_FOREACH(string base, m_bases) {
ClassScopePtr cls = ar->findClass(base);
if (cls && cls->isUserClass()) {
cg_printInclude(cls->getHeaderFilename());
}
}
BOOST_FOREACH(ClassScopeRawPtr cls, m_usedClassesFullHeader) {
if (cls && cls->isUserClass()) {
cg_printInclude(cls->getHeaderFilename());
}
}
// 2. Declarations
cg.namespaceBegin();
outputCPPForwardHeader(cg, ar);
cg.setContext(CodeGenerator::CppDeclaration);
getStmt()->outputCPP(cg, ar);
cg.namespaceEnd();
cg.headerEnd(filename);
}
void ClassScope::outputCPPForwardHeader(CodeGenerator &cg,
AnalysisResultPtr ar) {
cg.setContext(CodeGenerator::CppForwardDeclaration);
bool done = false;
BOOST_FOREACH(const string &str, m_usedLiteralStringsHeader) {
int index = -1;
int stringId = cg.checkLiteralString(str, index, ar, BlockScopePtr());
always_assert(index != -1);
string lisnam = ar->getLiteralStringName(stringId, index);
done = true;
if (Option::UseStaticStringProxy) {
string proxyNam = ar->getLiteralStringName(stringId, index, true);
cg_printf("extern StaticStringProxy %s;\n", proxyNam.c_str());
cg_printf("#ifndef %s\n", lisnam.c_str());
cg_printf("#define %s (*(StaticString *)(&%s))\n",
lisnam.c_str(), proxyNam.c_str());
cg_printf("#endif\n");
} else {
cg_printf("extern StaticString %s;\n", lisnam.c_str());
}
}
if (done) cg_printf("\n");
done = false;
BOOST_FOREACH(const int64 &val, m_usedScalarVarIntegersHeader) {
int index = -1;
int hash = ar->checkScalarVarInteger(val, index);
always_assert(index != -1);
string name = ar->getScalarVarIntegerName(hash, index);
done = true;
cg_printf("extern const VarNR &%s;\n", name.c_str());
}
if (done) cg_printf("\n");
done = false;
BOOST_FOREACH(const double &val, m_usedScalarVarDoublesHeader) {
int index = -1;
int hash = ar->checkScalarVarDouble(val, index);
always_assert(index != -1);
string name = ar->getScalarVarDoubleName(hash, index);
done = true;
cg_printf("extern const VarNR &%s;\n", name.c_str());
}
if (done) cg_printf("\n");
done = false;
BOOST_FOREACH(const string &str, m_usedLitVarStringsHeader) {
int index = -1;
int stringId = cg.checkLiteralString(str, index, ar, BlockScopePtr());
always_assert(index != -1);
string lisnam = ar->getLitVarStringName(stringId, index);
done = true;
if (Option::UseStaticStringProxy) {
string proxyName = ar->getLitVarStringName(stringId, index, true);
cg_printf("extern VariantProxy %s;\n", proxyName.c_str());
cg_printf("#ifndef %s\n", lisnam.c_str());
cg_printf("#define %s (*(Variant *)&%s)\n",
lisnam.c_str(), proxyName.c_str());
cg_printf("#endif\n");
} else {
cg_printf("extern VarNR %s;\n", lisnam.c_str());
}
}
if (done) cg_printf("\n");
done = false;
BOOST_FOREACH(const string &str, m_usedDefaultValueScalarArrays) {
int index = -1;
int hash = ar->checkScalarArray(str, index);
always_assert(hash != -1 && index != -1);
string name = ar->getScalarArrayName(hash, index);
done = true;
cg_printf("extern StaticArray %s;\n", name.c_str());
}
if (done) cg_printf("\n");
done = false;
BOOST_FOREACH(const string &str, m_usedDefaultValueScalarVarArrays) {
int index = -1;
int hash = ar->checkScalarArray(str, index);
always_assert(hash != -1 && index != -1);
string name = ar->getScalarVarArrayName(hash, index);
done = true;
cg_printf("extern VarNR %s;\n", name.c_str());
}
if (done) cg_printf("\n");
done = false;
BOOST_FOREACH(const string &str, m_usedConstsHeader) {
BlockScopeConstPtr block = ar->findConstantDeclarer(str);
always_assert(block);
ConstantTableConstPtr constants = block->getConstants();
done = true;
constants->outputSingleConstant(cg, ar, str);
}
if (done) cg_printf("\n");
done = false;
BOOST_FOREACH(const CodeGenerator::UsedClassConst& item,
m_usedClassConstsHeader) {
ClassScopePtr cls = item.first;
done = true;
cls->getConstants()->outputSingleConstant(cg, ar, item.second);
}
if (done) cg_printf("\n");
const vector<Symbol*> &symbols = getVariables()->getSymbols();
for (unsigned i = 0; i < symbols.size(); i++) {
const Symbol *sym = symbols[i];
TypePtr type = sym->getFinalType();
if (type->isSpecificObject()) {
ClassScopePtr cls = type->getClass(ar, shared_from_this());
if (cls) m_usedClassesHeader.insert(cls);
}
}
done = false;
BOOST_FOREACH(ClassScopeRawPtr usedClass, m_usedClassesHeader) {
done = true;
usedClass->outputForwardDeclaration(cg);
}
if (done) cg_printf("\n");
}
void ClassScope::outputCPPSupportMethodsImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
string clsNameStr = getId();
const char *clsName = clsNameStr.c_str();
cg.addClass(getName(), getContainingClass());
if (Option::GenerateCPPMacros) {
if ((isUserClass() && !isSepExtension()) ||
m_attributeClassInfo & ClassInfo::NoDefaultSweep) {
cg_printf("IMPLEMENT_CLASS_NO_DEFAULT_SWEEP(%s)\n", clsName);
} else {
cg_printf("IMPLEMENT_CLASS(%s)\n", clsName);
}
}
outputCPPGlobalTableWrappersImpl(cg, ar);
}
void ClassScope::outputCPPGlobalTableWrappersDecl(CodeGenerator &cg,
AnalysisResultPtr ar) {
string id = getId();
cg_printf("extern const %sObjectStaticCallbacks %s%s;\n",
isRedeclaring() ? "Redeclared" : "",
Option::ClassStaticsCallbackPrefix, id.c_str());
}
string ClassScope::getClassPropTableId(AnalysisResultPtr ar) {
if (checkHasPropTable(ar)) return getId();
if (derivesFromRedeclaring() != DirectFromRedeclared) {
if (ClassScopePtr p = getParentScope(ar)) {
return p->getClassPropTableId(ar);
}
}
return "";
}
void ClassScope::outputCPPGlobalTableWrappersImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
string id = getId();
string prop = getClassPropTableId(ar);
always_assert(!isRedeclaring());
cg_indentBegin("const ObjectStaticCallbacks %s%s = {\n",
Option::ClassStaticsCallbackPrefix, id.c_str());
if (!isInterface()) {
cg_printf("&%s%s::s_class_name,\n", Option::ClassPrefix, id.c_str());
} else {
cg_printf("0,\n");
}
if (prop.empty()) {
cg_printf("0,");
} else {
cg_printf("&%s%s::%sprop_table,",
Option::ClassPrefix, prop.c_str(),
Option::ObjectStaticPrefix);
}
always_assert(derivesFromRedeclaring() == FromNormal);
if (ClassScopePtr par = getParentScope(ar)) {
cg_printf("&%s%s",
Option::ClassStaticsCallbackPrefix, par->getId().c_str());
} else {
cg_printf("0");
}
int attributes = 0;
if (m_attribute & (HasUnknownStaticMethodHandler|
InheritsUnknownStaticMethodHandler)) {
attributes |= ObjectData::HasCallStatic;
}
if (m_attribute & (HasUnknownMethodHandler|
InheritsUnknownMethodHandler)) {
attributes |= ObjectData::HasCall;
}
cg_printf(",0x%x,", attributes);
if (isInterface()) {
cg_printf("0\n");
} else {
cg_printf("\n");
cg_printf("&%s%s::s_cls\n", Option::ClassPrefix, id.c_str());
}
cg_indentEnd("};\n");
}
bool ClassScope::addFunction(AnalysisResultConstPtr ar,
FunctionScopePtr funcScope) {
FunctionScopePtr &func = m_functions[funcScope->getName()];
if (func) {
func->getStmt()->parseTimeFatal(Compiler::DeclaredMethodTwice,
"Redeclared method %s::%s",
getOriginalName().c_str(),
func->getOriginalName().c_str());
}
func = funcScope;
m_functionsVec.push_back(funcScope);
return true;
}
void ClassScope::findJumpTableMethods(CodeGenerator &cg, AnalysisResultPtr ar,
vector<const char *> &funcs) {
bool systemcpp = cg.getOutput() == CodeGenerator::SystemCPP;
// output invoke support methods
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
FunctionScopePtr func = *iter;
assert(!func->isRedeclaring());
if (func->isAbstract() ||
!(systemcpp || func->isDynamic() || func->isVirtual())) continue;
const char *name = func->getName().c_str();
funcs.push_back(name);
}
}
bool ClassScope::hasJumpTableMethods(CodeGenerator &cg, AnalysisResultPtr ar) {
if (isInterface()) return false;
bool systemcpp = cg.getOutput() == CodeGenerator::SystemCPP;
// output invoke support methods
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
FunctionScopePtr func = *iter;
assert(!func->isRedeclaring());
if (func->isAbstract() ||
!(systemcpp || func->isDynamic() || func->isVirtual())) continue;
return true;
}
return false;
}
void ClassScope::outputCPPMethodInvokeBareObjectSupport(
CodeGenerator &cg, AnalysisResultPtr ar,
FunctionScopePtr func, bool fewArgs) {
const string &id(getId());
const string &lname(func->getName());
cg_indentBegin("Variant %s%s::%s%s(void *self, ",
Option::ClassPrefix, id.c_str(),
fewArgs ? Option::InvokeWrapperFewArgsPrefix :
Option::InvokeWrapperPrefix,
lname.c_str());
if (fewArgs) {
cg_printf("int count, INVOKE_FEW_ARGS_IMPL_ARGS");
} else {
cg_printf("CArrRef params");
}
cg_printf(") {\n");
cg_printf("MethodCallPackage mcp;\n");
if (func->isStatic() && func->needsClassParam()) {
cg_printf("mcp.isObj = true;\n");
cg_printf("mcp.rootObj = static_cast<ObjectData*>(self);\n");
} else {
cg_printf("mcp.obj = static_cast<ObjectData*>(self);\n");
}
if (fewArgs) {
cg_printf("return %s%s::%s%s(mcp, count, INVOKE_FEW_ARGS_PASS_ARGS);\n",
Option::ClassPrefix,
id.c_str(),
Option::InvokeFewArgsPrefix,
lname.c_str());
} else {
cg_printf("return %s%s::%s%s(mcp, params);\n",
Option::ClassPrefix,
id.c_str(),
Option::InvokePrefix,
lname.c_str());
}
cg_indentEnd("}\n");
}
void ClassScope::outputCPPMethodInvokeTableSupport(CodeGenerator &cg,
AnalysisResultPtr ar, const vector<const char*> &keys,
const StringToFunctionScopePtrMap &funcScopes, bool fewArgs) {
string id = getId();
ClassScopePtr self = dynamic_pointer_cast<ClassScope>(shared_from_this());
for (vector<const char*>::const_iterator it = keys.begin();
it != keys.end(); ++it) {
const char *name = *it;
string lname = CodeGenerator::FormatLabel(name);
StringToFunctionScopePtrMap::const_iterator iterFuncs =
funcScopes.find(name);
assert(iterFuncs != funcScopes.end());
FunctionScopePtr func = iterFuncs->second;
const char *extra = nullptr;
string prefix;
const char *instance = nullptr;
if (func->isStatic()) {
prefix += Option::ClassPrefix;
prefix += id;
prefix += "::";
if (func->needsClassParam()) {
prefix += Option::MethodImplPrefix;
extra = "c";
} else {
prefix += Option::MethodPrefix;
}
} else {
instance = "self->";
prefix += Option::MethodPrefix;
}
string origName = func->getOriginalFullName();
cg_printf("Variant");
if (fewArgs && Option::FunctionSections.find(origName) !=
Option::FunctionSections.end()) {
string funcSection = Option::FunctionSections[origName];
if (!funcSection.empty()) {
cg_printf(" __attribute__ ((section (\".text.%s\")))",
funcSection.c_str());
}
}
if (fewArgs) cg_printf(" NEVER_INLINE");
cg_indentBegin(" %s%s::%s%s(MethodCallPackage &mcp, ",
Option::ClassPrefix, id.c_str(),
fewArgs ? Option::InvokeFewArgsPrefix : Option::InvokePrefix,
lname.c_str());
if (fewArgs) {
cg_printf("int count, INVOKE_FEW_ARGS_IMPL_ARGS");
} else {
cg_printf("CArrRef params");
}
cg_printf(") {\n");
if (!fewArgs && func->getMaxParamCount() <= Option::InvokeFewArgsCount &&
!func->isVariableArgument()) {
if (Option::InvokeWithSpecificArgs && !func->getMaxParamCount() &&
!ar->isSystem() && !ar->isSepExtension()) {
// For functions with no parameter, we can combine the i_ wrapper and
// the ifa_ wrapper.
cg_printf("return ((CallInfo::MethInvoker0Args)&%s%s)(mcp, 0);\n",
Option::InvokeFewArgsPrefix, lname.c_str());
} else {
cg_printf("return invoke_meth_few_handler(mcp, params, &%s%s);\n",
Option::InvokeFewArgsPrefix, lname.c_str());
}
} else {
const char *class_name = "";
if (!func->isStatic()) {
// Instance method called as such
cg_indentBegin("if (UNLIKELY(mcp.obj == 0)) {\n");
cg_printf("return ObjectData::%sdummy(mcp, ",
fewArgs ? Option::InvokeFewArgsPrefix : Option::InvokePrefix);
if (fewArgs) {
cg_printf("count, INVOKE_FEW_ARGS_PASS_ARGS");
} else {
cg_printf("params");
}
cg_printf(", %s%s, %s%s);\n",
fewArgs ? Option::InvokeFewArgsPrefix : Option::InvokePrefix,
lname.c_str(),
Option::CreateObjectOnlyPrefix, id.c_str());
cg_indentEnd("}\n");
cg_printf("%s%s *self ATTRIBUTE_UNUSED "
"(static_cast<%s%s*>(mcp.obj));\n",
Option::ClassPrefix, id.c_str(),
Option::ClassPrefix, id.c_str());
} else if (func->needsClassParam()) {
// If mcp contains an object, was a static method
// invoked instance style.
// Use rootObj's class name as invoking class
class_name =
"CStrRef c(mcp.isObj"
" ? mcp.rootObj->o_getClassName()"
" : String(mcp.rootCls));\n";
}
if (!fewArgs) FunctionScope::OutputCPPDynamicInvokeCount(cg);
func->outputCPPDynamicInvoke(cg, ar, prefix.c_str(),
lname.c_str(), false, fewArgs, true, extra,
func->isConstructor(self), instance,
class_name);
}
cg_indentEnd("}\n");
}
}
void ClassScope::outputCPPJumpTableDecl(CodeGenerator &cg,
AnalysisResultPtr ar) {
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
FunctionScopePtr func = *iter;
string id = CodeGenerator::FormatLabel(func->getName());
bool needsWrapper = func->getName() == "__invoke";
cg_printf("DECLARE_METHOD_INVOKE_HELPERS(%s);\n", id.c_str());
if (needsWrapper) {
cg_printf("DECLARE_METHOD_INVOKE_WRAPPER_HELPERS(%s);\n",
id.c_str());
}
}
}
void ClassScope::outputVolatileCheckBegin(CodeGenerator &cg,
AnalysisResultPtr ar,
BlockScopePtr bs,
const std::string &name) {
if (isVolatile()) {
OutputVolatileCheckBegin(cg, ar, bs, name);
}
}
void ClassScope::outputVolatileCheckEnd(CodeGenerator &cg) {
if (isVolatile()) {
OutputVolatileCheckEnd(cg);
}
}
void ClassScope::OutputVolatileCheckBegin(CodeGenerator &cg,
AnalysisResultPtr ar,
BlockScopePtr bs,
const string &origName) {
cg_printf("((");
OutputVolatileCheck(cg, ar, bs, origName, false);
cg_printf("), (");
}
void ClassScope::OutputVolatileCheckEnd(CodeGenerator &cg) {
cg_printf("))");
}
void ClassScope::OutputVolatileCheck(CodeGenerator &cg, AnalysisResultPtr ar,
BlockScopePtr bs, const string &origName,
bool noThrow) {
bool exist = ar->findClass(origName);
cg_printf("%s%s(",
exist ? "checkClassExists" : "autoloadClass",
noThrow ? "NoThrow" : "Throw");
cg_printString(origName, ar, bs);
if (exist) {
string lwrName(Util::toLower(origName));
cg_printf(", &%s->CDEC(%s))",
cg.getGlobals(ar), CodeGenerator::FormatLabel(lwrName).c_str());
} else {
cg_printf(", (bool*)0)");
}
}
void ClassScope::outputMethodWrappers(CodeGenerator &cg,
AnalysisResultPtr ar) {
if (!isInterface()) {
string name = getId();
FunctionScopePtr constructor = findConstructor(ar, true);
if (constructor) {
if (!constructor->isAbstract()) {
constructor->outputMethodWrapper(cg, ar, name.c_str());
cg_printf("\n");
}
} else {
cg_indentBegin("static %s%s Create() {\n", Option::SmartPtrPrefix,
name.c_str());
cg_printf("return NEWOBJ(%s%s)();\n", Option::ClassPrefix, name.c_str());
cg_indentEnd("}\n");
cg_printf("\n");
}
ClassScopePtr self = static_pointer_cast<ClassScope>(shared_from_this());
for (unsigned int i = 0; i < m_functionsVec.size(); i++) {
FunctionScopePtr func = m_functionsVec[i];
if (func->isPublic() && !func->isConstructor(self) &&
!func->isMagic() && !func->isAbstract()) {
func->outputMethodWrapper(cg, ar, nullptr);
}
}
}
}
/*
* A class without a constructor, but with a destructor may need a special
* create method to clear the NoDestructor flag - but only if
* there is a constructor somewhere above us, and if /that/ constructor
* doesnt need to clear the NoDestructor flag.
*/
bool ClassScope::needsEnableDestructor(
AnalysisResultConstPtr ar) const {
if (m_needsEnableDestructor & 2) {
return m_needsEnableDestructor & 1;
}
bool ret =
(!derivesFromRedeclaring() &&
!getAttribute(HasConstructor) &&
!getAttribute(ClassNameConstructor));
if (ret) {
if (!getAttribute(HasDestructor) && !m_parent.empty()) {
if (ClassScopePtr parent = getParentScope(ar)) {
if (!parent->needsEnableDestructor(ar)) {
ret = false;
}
}
}
}
m_needsEnableDestructor = ret ? 3 : 2;
return ret;
}
bool ClassScope::canSkipCreateMethod(AnalysisResultConstPtr ar) const {
// create() is not necessary if
// 1) not inheriting from any class
// 2) no constructor defined (__construct or class name)
// 3) no init() defined
if (derivesFromRedeclaring() ||
getAttribute(HasConstructor) ||
getAttribute(ClassNameConstructor) ||
needsInitMethod()) {
return false;
}
if (!m_parent.empty()) {
ClassScopePtr parent = getParentScope(ar);
if (parent) return parent->canSkipCreateMethod(ar);
}
return true;
}