7ff582616f
We don't need them in g_class_map, beacuse hhvm processes the php for itself.
3168 linhas
107 KiB
C++
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;
|
|
}
|