summaryrefslogtreecommitdiff
path: root/cpp/src/Slice/PythonUtil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/Slice/PythonUtil.cpp')
-rw-r--r--cpp/src/Slice/PythonUtil.cpp2545
1 files changed, 2545 insertions, 0 deletions
diff --git a/cpp/src/Slice/PythonUtil.cpp b/cpp/src/Slice/PythonUtil.cpp
new file mode 100644
index 00000000000..7b62ef2970a
--- /dev/null
+++ b/cpp/src/Slice/PythonUtil.cpp
@@ -0,0 +1,2545 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <Slice/PythonUtil.h>
+#include <Slice/Checksum.h>
+#include <Slice/Util.h>
+#include <IceUtil/IceUtil.h>
+#include <IceUtil/StringUtil.h>
+#include <IceUtil/InputUtil.h>
+#include <climits>
+#include <iterator>
+
+using namespace std;
+using namespace Slice;
+using namespace IceUtil;
+using namespace IceUtilInternal;
+
+namespace Slice
+{
+namespace Python
+{
+
+class MetaDataVisitor : public ParserVisitor
+{
+public:
+
+ virtual bool visitUnitStart(const UnitPtr&);
+ virtual bool visitModuleStart(const ModulePtr&);
+ virtual void visitClassDecl(const ClassDeclPtr&);
+ virtual bool visitClassDefStart(const ClassDefPtr&);
+ virtual bool visitExceptionStart(const ExceptionPtr&);
+ virtual bool visitStructStart(const StructPtr&);
+ virtual void visitOperation(const OperationPtr&);
+ virtual void visitDataMember(const DataMemberPtr&);
+ virtual void visitSequence(const SequencePtr&);
+ virtual void visitDictionary(const DictionaryPtr&);
+ virtual void visitEnum(const EnumPtr&);
+ virtual void visitConst(const ConstPtr&);
+
+private:
+
+ //
+ // Validates sequence metadata.
+ //
+ void validateSequence(const string&, const string&, const TypePtr&, const StringList&);
+
+ //
+ // Checks a definition that doesn't currently support Python metadata.
+ //
+ void reject(const ContainedPtr&);
+
+ StringSet _history;
+};
+
+//
+// ModuleVisitor finds all of the Slice modules whose include level is greater
+// than 0 and emits a statement of the following form:
+//
+// _M_Foo = Ice.openModule('Foo')
+//
+// This statement allows the code generated for this translation unit to refer
+// to types residing in those included modules.
+//
+class ModuleVisitor : public ParserVisitor
+{
+public:
+
+ ModuleVisitor(Output&, set<string>&);
+
+ virtual bool visitModuleStart(const ModulePtr&);
+
+private:
+
+ Output& _out;
+ set<string>& _history;
+};
+
+//
+// CodeVisitor generates the Python mapping for a translation unit.
+//
+class CodeVisitor : public ParserVisitor
+{
+public:
+
+ CodeVisitor(IceUtilInternal::Output&, set<string>&);
+
+ virtual bool visitModuleStart(const ModulePtr&);
+ virtual void visitModuleEnd(const ModulePtr&);
+ virtual void visitClassDecl(const ClassDeclPtr&);
+ virtual bool visitClassDefStart(const ClassDefPtr&);
+ virtual bool visitExceptionStart(const ExceptionPtr&);
+ virtual bool visitStructStart(const StructPtr&);
+ virtual void visitSequence(const SequencePtr&);
+ virtual void visitDictionary(const DictionaryPtr&);
+ virtual void visitEnum(const EnumPtr&);
+ virtual void visitConst(const ConstPtr&);
+
+private:
+
+ //
+ // Return a Python symbol for the given parser element.
+ //
+ string getSymbol(const ContainedPtr&, const string& = string());
+
+ //
+ // Emit Python code to assign the given symbol in the current module.
+ //
+ void registerName(const string&);
+
+ //
+ // Emit the tuple for a Slice type.
+ //
+ void writeType(const TypePtr&);
+
+ //
+ // Write an initializer value for a given type.
+ //
+ void writeInitializer(const TypePtr&);
+
+ //
+ // Add a value to a hash code.
+ //
+ void writeHash(const string&, const TypePtr&, int&);
+
+ //
+ // Write Python metadata as a tuple.
+ //
+ void writeMetaData(const StringList&);
+
+ //
+ // Convert an operation mode into a string.
+ //
+ string getOperationMode(Slice::Operation::Mode);
+
+ struct MemberInfo
+ {
+ string fixedName;
+ bool inherited;
+ DataMemberPtr dataMember;
+ };
+ typedef list<MemberInfo> MemberInfoList;
+
+ //
+ // Write a member assignment statement for a constructor.
+ //
+ void writeAssign(const MemberInfo&);
+
+ //
+ // Write a constant value.
+ //
+ void writeConstantValue(const TypePtr&, const SyntaxTreeBasePtr&, const string&);
+
+ //
+ // Write constructor parameters with default values.
+ //
+ void writeConstructorParams(const MemberInfoList&);
+
+ void collectClassMembers(const ClassDefPtr&, MemberInfoList&, bool);
+ void collectExceptionMembers(const ExceptionPtr&, MemberInfoList&, bool);
+
+ string editComment(const string&);
+
+ Output& _out;
+ set<string>& _moduleHistory;
+ list<string> _moduleStack;
+ set<string> _classHistory;
+};
+
+}
+}
+
+static string
+lookupKwd(const string& name)
+{
+ //
+ // Keyword list. *Must* be kept in alphabetical order.
+ //
+ static const string keywordList[] =
+ {
+ "None", "and", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "exec",
+ "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "not", "or", "pass",
+ "print", "raise", "return", "try", "while", "yield"
+ };
+ bool found = binary_search(&keywordList[0],
+ &keywordList[sizeof(keywordList) / sizeof(*keywordList)],
+ name);
+ return found ? "_" + name : name;
+}
+
+//
+// Split a scoped name into its components and return the components as a list of (unscoped) identifiers.
+//
+static vector<string>
+splitScopedName(const string& scoped)
+{
+ assert(scoped[0] == ':');
+ vector<string> ids;
+ string::size_type next = 0;
+ string::size_type pos;
+ while((pos = scoped.find("::", next)) != string::npos)
+ {
+ pos += 2;
+ if(pos != scoped.size())
+ {
+ string::size_type endpos = scoped.find("::", pos);
+ if(endpos != string::npos)
+ {
+ ids.push_back(scoped.substr(pos, endpos - pos));
+ }
+ }
+ next = pos;
+ }
+ if(next != scoped.size())
+ {
+ ids.push_back(scoped.substr(next));
+ }
+ else
+ {
+ ids.push_back("");
+ }
+
+ return ids;
+}
+
+static string
+getDictLookup(const ContainedPtr& cont, const string& suffix = string())
+{
+ string scope = Slice::Python::scopedToName(cont->scope());
+ assert(!scope.empty());
+
+ string package = Slice::Python::getPackageMetadata(cont);
+ if(!package.empty())
+ {
+ scope = package + "." + scope;
+ }
+
+ return "_M_" + scope + "__dict__.has_key('" + suffix + Slice::Python::fixIdent(cont->name()) + "')";
+}
+
+//
+// ModuleVisitor implementation.
+//
+Slice::Python::ModuleVisitor::ModuleVisitor(Output& out, set<string>& history) :
+ _out(out), _history(history)
+{
+}
+
+bool
+Slice::Python::ModuleVisitor::visitModuleStart(const ModulePtr& p)
+{
+ if(p->includeLevel() > 0)
+ {
+ string abs = getAbsolute(p);
+ if(_history.count(abs) == 0)
+ {
+ //
+ // If this is a top-level module, then we check if it has package metadata.
+ // If so, we need to emit statements to open each of the modules in the
+ // package before we can open this module.
+ //
+ if(UnitPtr::dynamicCast(p->container()))
+ {
+ string pkg = getPackageMetadata(p);
+ if(!pkg.empty())
+ {
+ vector<string> v;
+ splitString(pkg, ".", v);
+ string mod;
+ for(vector<string>::iterator q = v.begin(); q != v.end(); ++q)
+ {
+ mod = mod.empty() ? *q : mod + "." + *q;
+ if(_history.count(mod) == 0)
+ {
+ _out << nl << "_M_" << mod << " = Ice.openModule('" << mod << "')";
+ _history.insert(mod);
+ }
+ }
+ }
+ }
+
+ _out << sp << nl << "# Included module " << abs;
+ _out << nl << "_M_" << abs << " = Ice.openModule('" << abs << "')";
+ _history.insert(abs);
+ }
+ }
+
+ return true;
+}
+
+//
+// CodeVisitor implementation.
+//
+Slice::Python::CodeVisitor::CodeVisitor(Output& out, set<string>& moduleHistory) :
+ _out(out), _moduleHistory(moduleHistory)
+{
+}
+
+bool
+Slice::Python::CodeVisitor::visitModuleStart(const ModulePtr& p)
+{
+ //
+ // As each module is opened, we emit the statement
+ //
+ // __name__ = 'Foo'
+ //
+ // This renames the current module to 'Foo' so that subsequent
+ // type definitions have the proper fully-qualified name.
+ //
+ // We also emit the statement
+ //
+ // _M_Foo = Ice.openModule('Foo')
+ //
+ // This allows us to create types in the module Foo.
+ //
+ string abs = getAbsolute(p);
+ _out << sp << nl << "# Start of module " << abs;
+ if(_moduleHistory.count(abs) == 0) // Don't emit this more than once for each module.
+ {
+ //
+ // If this is a top-level module, then we check if it has package metadata.
+ // If so, we need to emit statements to open each of the modules in the
+ // package before we can open this module.
+ //
+ if(UnitPtr::dynamicCast(p->container()))
+ {
+ string pkg = getPackageMetadata(p);
+ if(!pkg.empty())
+ {
+ vector<string> v;
+ splitString(pkg, ".", v);
+ string mod;
+ for(vector<string>::iterator q = v.begin(); q != v.end(); ++q)
+ {
+ mod = mod.empty() ? *q : mod + "." + *q;
+ if(_moduleHistory.count(mod) == 0) // Don't emit this more than once for each module.
+ {
+ _out << nl << "_M_" << mod << " = Ice.openModule('" << mod << "')";
+ _moduleHistory.insert(mod);
+ }
+ }
+ }
+ }
+ _out << nl << "_M_" << abs << " = Ice.openModule('" << abs << "')";
+ _moduleHistory.insert(abs);
+ }
+ _out << nl << "__name__ = '" << abs << "'";
+
+ string comment = p->comment();
+ if(!comment.empty())
+ {
+ _out << nl << "_M_" << abs << ".__doc__ = '''" << editComment(comment) << "'''";
+ }
+
+ _moduleStack.push_front(abs);
+ return true;
+}
+
+void
+Slice::Python::CodeVisitor::visitModuleEnd(const ModulePtr& p)
+{
+ assert(!_moduleStack.empty());
+ _out << sp << nl << "# End of module " << _moduleStack.front();
+ _moduleStack.pop_front();
+
+ if(!_moduleStack.empty())
+ {
+ _out << sp << nl << "__name__ = '" << _moduleStack.front() << "'";
+ }
+}
+
+void
+Slice::Python::CodeVisitor::visitClassDecl(const ClassDeclPtr& p)
+{
+ //
+ // Emit forward declarations.
+ //
+ string scoped = p->scoped();
+ if(_classHistory.count(scoped) == 0)
+ {
+ _out << sp << nl << "if not " << getDictLookup(p) << ':';
+ _out.inc();
+ string type = getAbsolute(p, "_t_");
+ _out << nl << "_M_" << type << " = IcePy.declareClass('" << scoped << "')";
+ if(!p->isLocal())
+ {
+ _out << nl << "_M_" << getAbsolute(p, "_t_", "Prx") << " = IcePy.declareProxy('" << scoped << "')";
+ }
+ _out.dec();
+ _classHistory.insert(scoped); // Avoid redundant declarations.
+ }
+}
+
+bool
+Slice::Python::CodeVisitor::visitClassDefStart(const ClassDefPtr& p)
+{
+ string scoped = p->scoped();
+ string type = getAbsolute(p, "_t_");
+ string abs = getAbsolute(p);
+ string name = fixIdent(p->name());
+ string prxAbs = getAbsolute(p, "", "Prx");
+ string prxName = fixIdent(p->name() + "Prx");
+ string prxType = getAbsolute(p, "_t_", "Prx");
+ ClassList bases = p->bases();
+ ClassDefPtr base;
+ OperationList ops = p->operations();
+ OperationList::iterator oli;
+ bool isAbstract = p->isInterface() || p->allOperations().size() > 0; // Don't use isAbstract() - see bug 3739
+
+ //
+ // Define the class.
+ //
+ _out << sp << nl << "if not " << getDictLookup(p) << ':';
+ _out.inc();
+ _out << nl << "_M_" << abs << " = Ice.createTempClass()";
+ _out << nl << "class " << name << '(';
+ if(bases.empty())
+ {
+ if(p->isLocal())
+ {
+ _out << "object";
+ }
+ else
+ {
+ _out << "Ice.Object";
+ }
+ }
+ else
+ {
+ for(ClassList::const_iterator q = bases.begin(); q != bases.end(); ++q)
+ {
+ if(q != bases.begin())
+ {
+ _out << ", ";
+ }
+ _out << getSymbol(*q);
+ }
+ if(!bases.front()->isInterface())
+ {
+ base = bases.front();
+ }
+ }
+ _out << "):";
+
+ _out.inc();
+
+ string comment = p->comment();
+ if(!comment.empty())
+ {
+ _out << nl << "'''" << editComment(comment) << "'''";
+ }
+
+ //
+ // __init__
+ //
+ _out << nl << "def __init__(self";
+ MemberInfoList allMembers;
+ collectClassMembers(p, allMembers, false);
+ writeConstructorParams(allMembers);
+ _out << "):";
+ _out.inc();
+ if(!base && !p->hasDataMembers() && !isAbstract)
+ {
+ _out << nl << "pass";
+ }
+ else
+ {
+ if(isAbstract)
+ {
+ _out << nl << "if __builtin__.type(self) == _M_" << abs << ':';
+ _out.inc();
+ _out << nl << "raise RuntimeError('" << abs << " is an abstract class')";
+ _out.dec();
+ }
+ if(base)
+ {
+ _out << nl << getSymbol(base) << ".__init__(self";
+ for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
+ {
+ if(q->inherited)
+ {
+ _out << ", " << q->fixedName;
+ }
+ }
+ _out << ')';
+ }
+ for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
+ {
+ if(!q->inherited)
+ {
+ writeAssign(*q);
+ }
+ }
+ }
+ _out.dec();
+
+ if(!p->isLocal())
+ {
+ //
+ // ice_ids
+ //
+ ClassList allBases = p->allBases();
+ StringList ids;
+#if defined(__IBMCPP__) && defined(NDEBUG)
+//
+// VisualAge C++ 6.0 does not see that ClassDef is a Contained,
+// when inlining is on. The code below issues a warning: better
+// than an error!
+//
+ transform(allBases.begin(), allBases.end(), back_inserter(ids),
+ IceUtil::constMemFun<string,ClassDef>(&Contained::scoped));
+#else
+ transform(allBases.begin(), allBases.end(), back_inserter(ids), IceUtil::constMemFun(&Contained::scoped));
+#endif
+ StringList other;
+ other.push_back(scoped);
+ other.push_back("::Ice::Object");
+ other.sort();
+ ids.merge(other);
+ ids.unique();
+ _out << sp << nl << "def ice_ids(self, current=None):";
+ _out.inc();
+ _out << nl << "return (";
+ for(StringList::iterator q = ids.begin(); q != ids.end(); ++q)
+ {
+ if(q != ids.begin())
+ {
+ _out << ", ";
+ }
+ _out << "'" << *q << "'";
+ }
+ _out << ')';
+ _out.dec();
+
+ //
+ // ice_id
+ //
+ _out << sp << nl << "def ice_id(self, current=None):";
+ _out.inc();
+ _out << nl << "return '" << scoped << "'";
+ _out.dec();
+
+ //
+ // ice_staticId
+ //
+ _out << sp << nl << "def ice_staticId():";
+ _out.inc();
+ _out << nl << "return '" << scoped << "'";
+ _out.dec();
+ _out << nl << "ice_staticId = staticmethod(ice_staticId)";
+ }
+
+ if(!ops.empty())
+ {
+ //
+ // Emit a placeholder for each operation.
+ //
+ for(oli = ops.begin(); oli != ops.end(); ++oli)
+ {
+ string fixedOpName = fixIdent((*oli)->name());
+ if(!p->isLocal() && (p->hasMetaData("amd") || (*oli)->hasMetaData("amd")))
+ {
+ _out << sp << nl << "def " << fixedOpName << "_async(self, _cb";
+
+ ParamDeclList params = (*oli)->parameters();
+
+ for(ParamDeclList::iterator pli = params.begin(); pli != params.end(); ++pli)
+ {
+ if(!(*pli)->isOutParam())
+ {
+ _out << ", " << fixIdent((*pli)->name());
+ }
+ }
+ if(!p->isLocal())
+ {
+ _out << ", current=None";
+ }
+ _out << "):";
+ _out.inc();
+ comment = (*oli)->comment();
+ if(!comment.empty())
+ {
+ _out << nl << "'''" << editComment(comment) << "'''";
+ }
+ _out << nl << "pass";
+ _out.dec();
+ }
+ else
+ {
+ _out << sp << nl << "def " << fixedOpName << "(self";
+
+ ParamDeclList params = (*oli)->parameters();
+
+ for(ParamDeclList::iterator pli = params.begin(); pli != params.end(); ++pli)
+ {
+ if(!(*pli)->isOutParam())
+ {
+ _out << ", " << fixIdent((*pli)->name());
+ }
+ }
+ if(!p->isLocal())
+ {
+ _out << ", current=None";
+ }
+ _out << "):";
+ _out.inc();
+ comment = (*oli)->comment();
+ if(!comment.empty())
+ {
+ _out << nl << "'''" << editComment(comment) << "'''";
+ }
+ _out << nl << "pass";
+ _out.dec();
+ }
+ }
+ }
+
+ //
+ // __str__
+ //
+ _out << sp << nl << "def __str__(self):";
+ _out.inc();
+ _out << nl << "return IcePy.stringify(self, _M_" << getAbsolute(p, "_t_") << ")";
+ _out.dec();
+ _out << sp << nl << "__repr__ = __str__";
+
+ _out.dec();
+
+ //
+ // Define the proxy class.
+ //
+ if(!p->isLocal())
+ {
+ _out << sp << nl << "_M_" << prxAbs << " = Ice.createTempClass()";
+ _out << nl << "class " << prxName << "(";
+ if(bases.empty())
+ {
+ _out << "Ice.ObjectPrx";
+ }
+ else
+ {
+ ClassList::const_iterator q = bases.begin();
+ while(q != bases.end())
+ {
+ _out << getSymbol(*q, "Prx");
+ if(++q != bases.end())
+ {
+ _out << ", ";
+ }
+ }
+ }
+ _out << "):";
+ _out.inc();
+
+ for(oli = ops.begin(); oli != ops.end(); ++oli)
+ {
+ string fixedOpName = fixIdent((*oli)->name());
+ if(fixedOpName == "checkedCast" || fixedOpName == "uncheckedCast")
+ {
+ fixedOpName.insert(0, "_");
+ }
+ TypePtr ret = (*oli)->returnType();
+ ParamDeclList paramList = (*oli)->parameters();
+ string inParams;
+
+ for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q)
+ {
+ if(!(*q)->isOutParam())
+ {
+ if(!inParams.empty())
+ {
+ inParams.append(", ");
+ }
+ inParams.append(fixIdent((*q)->name()));
+ }
+ }
+
+ comment = (*oli)->comment();
+ if(!comment.empty())
+ {
+ comment = "'''" + editComment(comment) + "'''";
+ }
+
+ _out << sp;
+ if(!comment.empty())
+ {
+ _out << nl << comment;
+ }
+ _out << nl << "def " << fixedOpName << "(self";
+ if(!inParams.empty())
+ {
+ _out << ", " << inParams;
+ }
+ _out << ", _ctx=None):";
+ _out.inc();
+ _out << nl << "return _M_" << abs << "._op_" << (*oli)->name() << ".invoke(self, ((" << inParams;
+ if(!inParams.empty() && inParams.find(',') == string::npos)
+ {
+ _out << ", ";
+ }
+ _out << "), _ctx))";
+ _out.dec();
+
+ //
+ // Async operations.
+ //
+ _out << sp;
+ if(!comment.empty())
+ {
+ _out << nl << comment;
+ }
+ _out << nl << "def begin_" << (*oli)->name() << "(self";
+ if(!inParams.empty())
+ {
+ _out << ", " << inParams;
+ }
+ _out << ", _response=None, _ex=None, _sent=None, _ctx=None):";
+ _out.inc();
+ _out << nl << "return _M_" << abs << "._op_" << (*oli)->name() << ".begin(self, ((" << inParams;
+ if(!inParams.empty() && inParams.find(',') == string::npos)
+ {
+ _out << ", ";
+ }
+ _out << "), _response, _ex, _sent, _ctx))";
+ _out.dec();
+
+ _out << sp;
+ if(!comment.empty())
+ {
+ _out << nl << comment;
+ }
+ _out << nl << "def end_" << (*oli)->name() << "(self, _r):";
+ _out.inc();
+ _out << nl << "return _M_" << abs << "._op_" << (*oli)->name() << ".end(self, _r)";
+ _out.dec();
+
+ //
+ // Old AMI operations.
+ //
+ if(p->hasMetaData("ami") || (*oli)->hasMetaData("ami"))
+ {
+ _out << sp;
+ if(!comment.empty())
+ {
+ _out << nl << comment;
+ }
+ _out << nl << "def " << fixedOpName << "_async(self, _cb";
+ if(!inParams.empty())
+ {
+ _out << ", " << inParams;
+ }
+ _out << ", _ctx=None):";
+ _out.inc();
+
+ _out << nl << "return _M_" << abs << "._op_" << (*oli)->name() << ".invokeAsync(self, (_cb, ("
+ << inParams;
+ if(!inParams.empty() && inParams.find(',') == string::npos)
+ {
+ _out << ", ";
+ }
+ _out << "), _ctx))";
+ _out.dec();
+ }
+ }
+
+ _out << sp << nl << "def checkedCast(proxy, facetOrCtx=None, _ctx=None):";
+ _out.inc();
+ _out << nl << "return _M_" << prxAbs << ".ice_checkedCast(proxy, '" << scoped << "', facetOrCtx, _ctx)";
+ _out.dec();
+ _out << nl << "checkedCast = staticmethod(checkedCast)";
+
+ _out << sp << nl << "def uncheckedCast(proxy, facet=None):";
+ _out.inc();
+ _out << nl << "return _M_" << prxAbs << ".ice_uncheckedCast(proxy, facet)";
+ _out.dec();
+ _out << nl << "uncheckedCast = staticmethod(uncheckedCast)";
+
+ _out.dec();
+
+ _out << sp << nl << "_M_" << prxType << " = IcePy.defineProxy('" << scoped << "', " << prxName << ")";
+ }
+
+ if(_classHistory.count(scoped) == 0 && p->canBeCyclic())
+ {
+ //
+ // Emit a forward declaration for the class in case a data member refers to this type.
+ //
+ _out << sp << nl << "_M_" << type << " = IcePy.declareClass('" << scoped << "')";
+ }
+
+ DataMemberList members = p->dataMembers();
+ _out << sp << nl << "_M_" << type << " = IcePy.defineClass('" << scoped << "', " << name << ", ";
+ writeMetaData(p->getMetaData());
+ _out << ", " << (isAbstract ? "True" : "False") << ", ";
+ if(!base)
+ {
+ _out << "None";
+ }
+ else
+ {
+ _out << "_M_" << getAbsolute(base, "_t_");
+ }
+ _out << ", (";
+ //
+ // Interfaces
+ //
+ int interfaceCount = 0;
+ for(ClassList::const_iterator q = bases.begin(); q != bases.end(); ++q)
+ {
+ if((*q)->isInterface())
+ {
+ if(interfaceCount > 0)
+ {
+ _out << ", ";
+ }
+ _out << "_M_" << getAbsolute(*q, "_t_");
+ ++interfaceCount;
+ }
+ }
+ if(interfaceCount == 1)
+ {
+ _out << ',';
+ }
+ //
+ // Members
+ //
+ // Data members are represented as a tuple:
+ //
+ // ('MemberName', MemberMetaData, MemberType)
+ //
+ // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
+ //
+ _out << "), (";
+ if(members.size() > 1)
+ {
+ _out.inc();
+ _out << nl;
+ }
+ bool isProtected = p->hasMetaData("protected");
+ for(DataMemberList::iterator r = members.begin(); r != members.end(); ++r)
+ {
+ if(r != members.begin())
+ {
+ _out << ',' << nl;
+ }
+ _out << "('";
+ if(isProtected || (*r)->hasMetaData("protected"))
+ {
+ _out << '_';
+ }
+ _out << fixIdent((*r)->name()) << "', ";
+ writeMetaData((*r)->getMetaData());
+ _out << ", ";
+ writeType((*r)->type());
+ _out << ')';
+ }
+ if(members.size() == 1)
+ {
+ _out << ',';
+ }
+ else if(members.size() > 1)
+ {
+ _out.dec();
+ _out << nl;
+ }
+ _out << "))";
+ _out << nl << name << "._ice_type = _M_" << type;
+
+ //
+ // Define each operation. The arguments to the IcePy.Operation constructor are:
+ //
+ // 'opName', Mode, SendMode, AMD, (MetaData), (InParams), (OutParams), ReturnType, (Exceptions)
+ //
+ // where InParams and OutParams are tuples of type descriptions, and Exceptions
+ // is a tuple of exception type ids.
+ //
+ if(!p->isLocal())
+ {
+ if(!ops.empty())
+ {
+ _out << sp;
+ }
+ for(OperationList::iterator s = ops.begin(); s != ops.end(); ++s)
+ {
+ ParamDeclList params = (*s)->parameters();
+ ParamDeclList::iterator t;
+ int count;
+
+ _out << nl << name << "._op_" << (*s)->name() << " = IcePy.Operation('" << (*s)->name() << "', "
+ << getOperationMode((*s)->mode()) << ", " << getOperationMode((*s)->sendMode()) << ", "
+ << ((p->hasMetaData("amd") || (*s)->hasMetaData("amd")) ? "True" : "False") << ", ";
+ writeMetaData((*s)->getMetaData());
+ _out << ", (";
+ for(t = params.begin(), count = 0; t != params.end(); ++t)
+ {
+ if(!(*t)->isOutParam())
+ {
+ if(count > 0)
+ {
+ _out << ", ";
+ }
+ _out << '(';
+ writeMetaData((*t)->getMetaData());
+ _out << ", ";
+ writeType((*t)->type());
+ _out << ')';
+ ++count;
+ }
+ }
+ if(count == 1)
+ {
+ _out << ',';
+ }
+ _out << "), (";
+ for(t = params.begin(), count = 0; t != params.end(); ++t)
+ {
+ if((*t)->isOutParam())
+ {
+ if(count > 0)
+ {
+ _out << ", ";
+ }
+ _out << '(';
+ writeMetaData((*t)->getMetaData());
+ _out << ", ";
+ writeType((*t)->type());
+ _out << ')';
+ ++count;
+ }
+ }
+ if(count == 1)
+ {
+ _out << ',';
+ }
+ _out << "), ";
+ TypePtr returnType = (*s)->returnType();
+ if(returnType)
+ {
+ writeType(returnType);
+ }
+ else
+ {
+ _out << "None";
+ }
+ _out << ", (";
+ ExceptionList exceptions = (*s)->throws();
+ for(ExceptionList::iterator u = exceptions.begin(); u != exceptions.end(); ++u)
+ {
+ if(u != exceptions.begin())
+ {
+ _out << ", ";
+ }
+ _out << "_M_" << getAbsolute(*u, "_t_");
+ }
+ if(exceptions.size() == 1)
+ {
+ _out << ',';
+ }
+ _out << "))";
+
+ string deprecateMetadata;
+ if((*s)->findMetaData("deprecate", deprecateMetadata) || p->findMetaData("deprecate", deprecateMetadata))
+ {
+ string msg;
+ string::size_type pos = deprecateMetadata.find(':');
+ if(pos != string::npos && pos < deprecateMetadata.size() - 1)
+ {
+ msg = deprecateMetadata.substr(pos + 1);
+ }
+ _out << nl << name << "._op_" << (*s)->name() << ".deprecate(\"" << msg << "\")";
+ }
+ }
+ }
+
+ registerName(name);
+
+ if(!p->isLocal())
+ {
+ registerName(prxName);
+ }
+
+ _out.dec();
+
+ if(_classHistory.count(scoped) == 0)
+ {
+ _classHistory.insert(scoped); // Avoid redundant declarations.
+ }
+
+ return false;
+}
+
+bool
+Slice::Python::CodeVisitor::visitExceptionStart(const ExceptionPtr& p)
+{
+ string scoped = p->scoped();
+ string abs = getAbsolute(p);
+ string name = fixIdent(p->name());
+
+ _out << sp << nl << "if not " << getDictLookup(p) << ':';
+ _out.inc();
+ _out << nl << "_M_" << abs << " = Ice.createTempClass()";
+ _out << nl << "class " << name << '(';
+ ExceptionPtr base = p->base();
+ string baseName;
+ if(base)
+ {
+ baseName = getSymbol(base);
+ _out << baseName;
+ }
+ else if(p->isLocal())
+ {
+ _out << "Ice.LocalException";
+ }
+ else
+ {
+ _out << "Ice.UserException";
+ }
+ _out << "):";
+ _out.inc();
+
+ string comment = p->comment();
+ if(!comment.empty())
+ {
+ _out << nl << "'''" << editComment(comment) << "'''";
+ }
+
+ DataMemberList members = p->dataMembers();
+ DataMemberList::iterator dmli;
+
+ //
+ // __init__
+ //
+ _out << nl << "def __init__(self";
+ MemberInfoList allMembers;
+ collectExceptionMembers(p, allMembers, false);
+ writeConstructorParams(allMembers);
+ _out << "):";
+ _out.inc();
+ if(!base && members.empty())
+ {
+ _out << nl << "pass";
+ }
+ else
+ {
+ if(base)
+ {
+ _out << nl << baseName << ".__init__(self";
+ for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
+ {
+ if(q->inherited)
+ {
+ _out << ", " << q->fixedName;
+ }
+ }
+ _out << ')';
+ }
+ for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
+ {
+ if(!q->inherited)
+ {
+ writeAssign(*q);
+ }
+ }
+ }
+ _out.dec();
+
+ //
+ // __str__
+ //
+ _out << sp << nl << "def __str__(self):";
+ _out.inc();
+ _out << nl << "return IcePy.stringifyException(self)";
+ _out.dec();
+ _out << sp << nl << "__repr__ = __str__";
+
+ //
+ // _ice_name
+ //
+ _out << sp << nl << "_ice_name = '" << scoped.substr(2) << "'";
+
+ _out.dec();
+
+ //
+ // Emit the type information.
+ //
+ string type = getAbsolute(p, "_t_");
+ _out << sp << nl << "_M_" << type << " = IcePy.defineException('" << scoped << "', " << name << ", ";
+ writeMetaData(p->getMetaData());
+ _out << ", ";
+ if(!base)
+ {
+ _out << "None";
+ }
+ else
+ {
+ _out << "_M_" << getAbsolute(base, "_t_");
+ }
+ _out << ", (";
+ if(members.size() > 1)
+ {
+ _out.inc();
+ _out << nl;
+ }
+ //
+ // Data members are represented as a tuple:
+ //
+ // ('MemberName', MemberMetaData, MemberType)
+ //
+ // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
+ //
+ for(dmli = members.begin(); dmli != members.end(); ++dmli)
+ {
+ if(dmli != members.begin())
+ {
+ _out << ',' << nl;
+ }
+ _out << "('" << fixIdent((*dmli)->name()) << "', ";
+ writeMetaData((*dmli)->getMetaData());
+ _out << ", ";
+ writeType((*dmli)->type());
+ _out << ')';
+ }
+ if(members.size() == 1)
+ {
+ _out << ',';
+ }
+ else if(members.size() > 1)
+ {
+ _out.dec();
+ _out << nl;
+ }
+ _out << "))";
+ _out << nl << name << "._ice_type = _M_" << type;
+
+ registerName(name);
+
+ _out.dec();
+
+ return false;
+}
+
+bool
+Slice::Python::CodeVisitor::visitStructStart(const StructPtr& p)
+{
+ string scoped = p->scoped();
+ string abs = getAbsolute(p);
+ string name = fixIdent(p->name());
+ MemberInfoList memberList;
+ MemberInfoList::iterator r;
+
+ {
+ DataMemberList members = p->dataMembers();
+ for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
+ {
+ memberList.push_back(MemberInfo());
+ memberList.back().fixedName = fixIdent((*q)->name());
+ memberList.back().inherited = false;
+ memberList.back().dataMember = *q;
+ }
+ }
+
+ _out << sp << nl << "if not " << getDictLookup(p) << ':';
+ _out.inc();
+ _out << nl << "_M_" << abs << " = Ice.createTempClass()";
+ _out << nl << "class " << name << "(object):";
+ _out.inc();
+
+ string comment = p->comment();
+ if(!comment.empty())
+ {
+ _out << nl << "'''" << editComment(comment) << "'''";
+ }
+
+ _out << nl << "def __init__(self";
+ writeConstructorParams(memberList);
+ _out << "):";
+ _out.inc();
+ for(r = memberList.begin(); r != memberList.end(); ++r)
+ {
+ writeAssign(*r);
+ }
+ _out.dec();
+
+ _out << sp << nl << "def __hash__(self):";
+ _out.inc();
+ _out << nl << "_h = 0";
+ int iter = 0;
+ for(r = memberList.begin(); r != memberList.end(); ++r)
+ {
+ string s = "self." + r->fixedName;
+ writeHash(s, r->dataMember->type(), iter);
+ }
+ _out << nl << "return _h % 0x7fffffff";
+ _out.dec();
+
+ //
+ // Rich operators. __lt__, __le__, __eq__, __ne__, __gt__, __ge__
+ //
+ static const char* richOps[] = {
+ "__lt__", "<",
+ "__le__", "<=",
+ "__eq__", "==",
+ "__ne__", "!=",
+ "__gt__", ">",
+ "__ge__", ">="
+ };
+ for(int opIndex = 0; opIndex != sizeof(richOps)/sizeof(richOps[0]); opIndex += 2)
+ {
+ string opName = richOps[opIndex];
+ string opSymbol = richOps[opIndex+1];
+
+ _out << sp << nl << "def " << opName << "(self, other):";
+ _out.inc();
+ _out << nl << "if isinstance(other, _M_" << abs << "):";
+ _out.inc();
+ if(!memberList.empty())
+ {
+ _out << nl << "return ";
+ for(r = memberList.begin(); r != memberList.end(); ++r)
+ {
+ if(r != memberList.begin())
+ {
+ if(opName == "__eq__")
+ {
+ _out << " and ";
+ }
+ else
+ {
+ _out << " or ";
+ }
+ }
+ _out << "self." << r->fixedName << " " << opSymbol << " other." << r->fixedName;
+ }
+ }
+ else
+ {
+ _out << nl << "return False";
+ }
+ _out.dec();
+ _out << nl << "elif other == None:";
+ _out.inc();
+ if(opName == "__ne__")
+ {
+ _out << nl << "return True";
+ }
+ else
+ {
+ _out << nl << "return False";
+ }
+ _out.dec();
+ _out << nl << "return NotImplemented";
+ _out.dec();
+ }
+
+ //
+ // __str__
+ //
+ _out << sp << nl << "def __str__(self):";
+ _out.inc();
+ _out << nl << "return IcePy.stringify(self, _M_" << getAbsolute(p, "_t_") << ")";
+ _out.dec();
+ _out << sp << nl << "__repr__ = __str__";
+
+ _out.dec();
+
+ //
+ // Emit the type information.
+ //
+ _out << sp << nl << "_M_" << getAbsolute(p, "_t_") << " = IcePy.defineStruct('" << scoped << "', " << name << ", ";
+ writeMetaData(p->getMetaData());
+ _out << ", (";
+ //
+ // Data members are represented as a tuple:
+ //
+ // ('MemberName', MemberMetaData, MemberType)
+ //
+ // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
+ //
+ if(memberList.size() > 1)
+ {
+ _out.inc();
+ _out << nl;
+ }
+ for(r = memberList.begin(); r != memberList.end(); ++r)
+ {
+ if(r != memberList.begin())
+ {
+ _out << ',' << nl;
+ }
+ _out << "('" << r->fixedName << "', ";
+ writeMetaData(r->dataMember->getMetaData());
+ _out << ", ";
+ writeType(r->dataMember->type());
+ _out << ')';
+ }
+ if(memberList.size() == 1)
+ {
+ _out << ',';
+ }
+ else if(memberList.size() > 1)
+ {
+ _out.dec();
+ _out << nl;
+ }
+ _out << "))";
+
+ registerName(name);
+
+ _out.dec();
+
+ return false;
+}
+
+void
+Slice::Python::CodeVisitor::visitSequence(const SequencePtr& p)
+{
+ static const string protobuf = "python:protobuf:";
+ StringList metaData = p->getMetaData();
+ bool isCustom = false;
+ string customType;
+ for(StringList::const_iterator q = metaData.begin(); q != metaData.end(); ++q)
+ {
+ if(q->find(protobuf) == 0)
+ {
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(p->type());
+ if(!builtin || builtin->kind() != Builtin::KindByte)
+ {
+ continue;
+ }
+ isCustom = true;
+ customType = q->substr(protobuf.size());
+ break;
+ }
+ }
+
+ //
+ // Emit the type information.
+ //
+ string scoped = p->scoped();
+ _out << sp << nl << "if not " << getDictLookup(p, "_t_") << ':';
+ _out.inc();
+ if(isCustom)
+ {
+ string package = customType.substr(0, customType.find('.'));
+ _out << nl << "import " << package;
+ _out << nl << "_M_" << getAbsolute(p, "_t_")
+ << " = IcePy.defineCustom('" << scoped << "', " << customType << ")";
+ }
+ else
+ {
+ _out << nl << "_M_" << getAbsolute(p, "_t_") << " = IcePy.defineSequence('" << scoped << "', ";
+ writeMetaData(metaData);
+ _out << ", ";
+ writeType(p->type());
+ _out << ")";
+ }
+ _out.dec();
+}
+
+void
+Slice::Python::CodeVisitor::visitDictionary(const DictionaryPtr& p)
+{
+ //
+ // Emit the type information.
+ //
+ string scoped = p->scoped();
+ _out << sp << nl << "if not " << getDictLookup(p, "_t_") << ':';
+ _out.inc();
+ _out << nl << "_M_" << getAbsolute(p, "_t_") << " = IcePy.defineDictionary('" << scoped << "', ";
+ writeMetaData(p->getMetaData());
+ _out << ", ";
+ writeType(p->keyType());
+ _out << ", ";
+ writeType(p->valueType());
+ _out << ")";
+ _out.dec();
+}
+
+void
+Slice::Python::CodeVisitor::visitEnum(const EnumPtr& p)
+{
+ string scoped = p->scoped();
+ string abs = getAbsolute(p);
+ string name = fixIdent(p->name());
+ EnumeratorList enums = p->getEnumerators();
+ EnumeratorList::iterator q;
+ int i;
+
+ _out << sp << nl << "if not " << getDictLookup(p) << ':';
+ _out.inc();
+ _out << nl << "_M_" << abs << " = Ice.createTempClass()";
+ _out << nl << "class " << name << "(object):";
+ _out.inc();
+
+ string comment = p->comment();
+ if(!comment.empty())
+ {
+ _out << nl << "'''" << editComment(comment) << "'''";
+ }
+
+ _out << sp << nl << "def __init__(self, val):";
+ _out.inc();
+ {
+ ostringstream assertion;
+ assertion << "assert(val >= 0 and val < " << enums.size() << ')';
+ _out << nl << assertion.str();
+ }
+ _out << nl << "self.value = val";
+ _out.dec();
+
+ _out << sp << nl << "def __str__(self):";
+ _out.inc();
+ _out << nl << "return self._names[self.value]";
+ _out.dec();
+ _out << sp << nl << "__repr__ = __str__";
+ _out << sp << nl << "def __hash__(self):";
+ _out.inc();
+ _out << nl << "return self.value";
+ _out.dec();
+
+ //
+ // Rich operators. __lt__, __le__, __eq__, __ne__, __gt__, __ge__
+ //
+ static const char* richOps[] = {
+ "__lt__", "<",
+ "__le__", "<=",
+ "__eq__", "==",
+ "__ne__", "!=",
+ "__gt__", ">",
+ "__ge__", ">="
+ };
+ for(int opIndex = 0; opIndex != sizeof(richOps)/sizeof(richOps[0]); opIndex += 2)
+ {
+ const char* opName = richOps[opIndex];
+ const char* opSymbol = richOps[opIndex+1];
+
+ _out << sp << nl << "def " << opName << "(self, other):";
+ _out.inc();
+ _out << nl << "if isinstance(other, _M_" << abs << "):";
+ _out.inc();
+ _out << nl << "return self.value " << opSymbol << " other.value;";
+ _out.dec();
+ _out << nl << "elif other == None:";
+ _out.inc();
+ _out << nl << "return False";
+ _out.dec();
+ _out << nl << "return NotImplemented";
+ _out.dec();
+ }
+
+ _out << sp << nl << "_names = (";
+ for(q = enums.begin(), i = 0; q != enums.end(); ++q, ++i)
+ {
+ if(q != enums.begin())
+ {
+ _out << ", ";
+ }
+ _out << "'" << (*q)->name() << "'";
+ }
+ if(enums.size() == 1)
+ {
+ _out << ',';
+ }
+ _out << ')';
+ _out.dec();
+
+ _out << sp;
+ for(q = enums.begin(), i = 0; q != enums.end(); ++q, ++i)
+ {
+ string fixedEnum = fixIdent((*q)->name());
+ ostringstream idx;
+ idx << i;
+ _out << nl << name << '.' << fixedEnum << " = " << name << '(' << idx.str() << ')';
+ }
+
+ //
+ // Emit the type information.
+ //
+ _out << sp << nl << "_M_" << getAbsolute(p, "_t_") << " = IcePy.defineEnum('" << scoped << "', " << name
+ << ", ";
+ writeMetaData(p->getMetaData());
+ _out << ", (";
+ for(q = enums.begin(); q != enums.end(); ++q)
+ {
+ if(q != enums.begin())
+ {
+ _out << ", ";
+ }
+ string fixedEnum = fixIdent((*q)->name());
+ _out << name << '.' << fixedEnum;
+ }
+ if(enums.size() == 1)
+ {
+ _out << ',';
+ }
+ _out << "))";
+
+ registerName(name);
+
+ _out.dec();
+}
+
+void
+Slice::Python::CodeVisitor::visitConst(const ConstPtr& p)
+{
+ Slice::TypePtr type = p->type();
+ string name = fixIdent(p->name());
+
+ _out << sp << nl << "_M_" << getAbsolute(p) << " = ";
+ writeConstantValue(type, p->valueType(), p->value());
+}
+
+string
+Slice::Python::CodeVisitor::getSymbol(const ContainedPtr& p, const string& nameSuffix)
+{
+ //
+ // An explicit reference to another type must always be prefixed with "_M_".
+ //
+ return "_M_" + getAbsolute(p, "", nameSuffix);
+}
+
+void
+Slice::Python::CodeVisitor::registerName(const string& name)
+{
+ assert(!_moduleStack.empty());
+ _out << sp << nl << "_M_" << _moduleStack.front() << '.' << name << " = " << name;
+ _out << nl << "del " << name;
+}
+
+void
+Slice::Python::CodeVisitor::writeType(const TypePtr& p)
+{
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(p);
+ if(builtin)
+ {
+ switch(builtin->kind())
+ {
+ case Builtin::KindBool:
+ {
+ _out << "IcePy._t_bool";
+ break;
+ }
+ case Builtin::KindByte:
+ {
+ _out << "IcePy._t_byte";
+ break;
+ }
+ case Builtin::KindShort:
+ {
+ _out << "IcePy._t_short";
+ break;
+ }
+ case Builtin::KindInt:
+ {
+ _out << "IcePy._t_int";
+ break;
+ }
+ case Builtin::KindLong:
+ {
+ _out << "IcePy._t_long";
+ break;
+ }
+ case Builtin::KindFloat:
+ {
+ _out << "IcePy._t_float";
+ break;
+ }
+ case Builtin::KindDouble:
+ {
+ _out << "IcePy._t_double";
+ break;
+ }
+ case Builtin::KindString:
+ {
+ _out << "IcePy._t_string";
+ break;
+ }
+ case Builtin::KindObject:
+ {
+ _out << "IcePy._t_Object";
+ break;
+ }
+ case Builtin::KindObjectProxy:
+ {
+ _out << "IcePy._t_ObjectPrx";
+ break;
+ }
+ case Builtin::KindLocalObject:
+ {
+ _out << "IcePy._t_LocalObject";
+ break;
+ }
+ }
+ return;
+ }
+
+ ProxyPtr prx = ProxyPtr::dynamicCast(p);
+ if(prx)
+ {
+ _out << "_M_" << getAbsolute(prx->_class(), "_t_", "Prx");
+ return;
+ }
+
+ ContainedPtr cont = ContainedPtr::dynamicCast(p);
+ assert(cont);
+ _out << "_M_" << getAbsolute(cont, "_t_");
+}
+
+void
+Slice::Python::CodeVisitor::writeInitializer(const TypePtr& p)
+{
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(p);
+ if(builtin)
+ {
+ switch(builtin->kind())
+ {
+ case Builtin::KindBool:
+ {
+ _out << "False";
+ break;
+ }
+ case Builtin::KindByte:
+ case Builtin::KindShort:
+ case Builtin::KindInt:
+ case Builtin::KindLong:
+ {
+ _out << "0";
+ break;
+ }
+ case Builtin::KindFloat:
+ case Builtin::KindDouble:
+ {
+ _out << "0.0";
+ break;
+ }
+ case Builtin::KindString:
+ {
+ _out << "''";
+ break;
+ }
+ case Builtin::KindObject:
+ case Builtin::KindObjectProxy:
+ case Builtin::KindLocalObject:
+ {
+ _out << "None";
+ break;
+ }
+ }
+ return;
+ }
+
+ EnumPtr en = EnumPtr::dynamicCast(p);
+ if(en)
+ {
+ EnumeratorList enums = en->getEnumerators();
+ _out << getSymbol(en) << "." << fixIdent(enums.front()->name());
+ return;
+ }
+
+ StructPtr st = StructPtr::dynamicCast(p);
+ if(st)
+ {
+ //
+ // We cannot emit a call to the struct's constructor here because Python
+ // only evaluates this expression once (see bug 3676). Instead, we emit
+ // a marker that allows us to determine whether the application has
+ // supplied a value.
+ //
+ _out << "Ice._struct_marker";
+ return;
+ }
+
+ _out << "None";
+}
+
+void
+Slice::Python::CodeVisitor::writeHash(const string& name, const TypePtr& p, int& iter)
+{
+ SequencePtr seq = SequencePtr::dynamicCast(p);
+ if(seq)
+ {
+ _out << nl << "if " << name << ':';
+ _out.inc();
+ _out << nl << "for _i" << iter << " in " << name << ':';
+ _out.inc();
+ ostringstream elem;
+ elem << "_i" << iter;
+ iter++;
+ writeHash(elem.str(), seq->type(), iter);
+ _out.dec();
+ _out.dec();
+ return;
+ }
+
+ DictionaryPtr dict = DictionaryPtr::dynamicCast(p);
+ if(dict)
+ {
+ _out << nl << "if " << name << ':';
+ _out.inc();
+ _out << nl << "for _i" << iter << " in " << name << ':';
+ _out.inc();
+ ostringstream key;
+ key << "_i" << iter;
+ ostringstream value;
+ value << name << '[' << key.str() << ']';
+ iter++;
+ writeHash(key.str(), dict->keyType(), iter);
+ writeHash(value.str(), dict->valueType(), iter);
+ _out.dec();
+ _out.dec();
+ return;
+ }
+
+ _out << nl << "_h = 5 * _h + __builtin__.hash(" << name << ")";
+}
+
+void
+Slice::Python::CodeVisitor::writeMetaData(const StringList& meta)
+{
+ int i = 0;
+ _out << '(';
+ for(StringList::const_iterator p = meta.begin(); p != meta.end(); ++p)
+ {
+ if(p->find("python:") == 0)
+ {
+ if(i > 0)
+ {
+ _out << ", ";
+ }
+ _out << "'" << *p << "'";
+ ++i;
+ }
+ }
+ if(i == 1)
+ {
+ _out << ',';
+ }
+ _out << ')';
+}
+
+void
+Slice::Python::CodeVisitor::writeAssign(const MemberInfo& info)
+{
+ //
+ // Structures are treated differently (see bug 3676).
+ //
+ StructPtr st = StructPtr::dynamicCast(info.dataMember->type());
+ if(st)
+ {
+ _out << nl << "if " << info.fixedName << " is Ice._struct_marker:";
+ _out.inc();
+ _out << nl << "self." << info.fixedName << " = " << getSymbol(st) << "()";
+ _out.dec();
+ _out << nl << "else:";
+ _out.inc();
+ _out << nl << "self." << info.fixedName << " = " << info.fixedName;
+ _out.dec();
+ }
+ else
+ {
+ _out << nl << "self." << info.fixedName << " = " << info.fixedName;
+ }
+}
+
+void
+Slice::Python::CodeVisitor::writeConstantValue(const TypePtr& type, const SyntaxTreeBasePtr& valueType,
+ const string& value)
+{
+ ConstPtr constant = ConstPtr::dynamicCast(valueType);
+ if(constant)
+ {
+ _out << "_M_" << getAbsolute(constant);
+ }
+ else
+ {
+ Slice::BuiltinPtr b = Slice::BuiltinPtr::dynamicCast(type);
+ Slice::EnumPtr en = Slice::EnumPtr::dynamicCast(type);
+ if(b)
+ {
+ switch(b->kind())
+ {
+ case Slice::Builtin::KindBool:
+ {
+ _out << (value == "true" ? "True" : "False");
+ break;
+ }
+ case Slice::Builtin::KindByte:
+ case Slice::Builtin::KindShort:
+ case Slice::Builtin::KindInt:
+ case Slice::Builtin::KindFloat:
+ case Slice::Builtin::KindDouble:
+ case Slice::Builtin::KindLong:
+ {
+ _out << value;
+ break;
+ }
+ case Slice::Builtin::KindString:
+ {
+ //
+ // Expand strings into the basic source character set. We can't use isalpha() and the like
+ // here because they are sensitive to the current locale.
+ //
+ static const string basicSourceChars = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_{}[]#()<>%:;.?*+-/^&|~!=, '";
+ static const set<char> charSet(basicSourceChars.begin(), basicSourceChars.end());
+
+ _out << "\""; // Opening "
+
+ for(string::const_iterator c = value.begin(); c != value.end(); ++c)
+ {
+ switch(*c)
+ {
+ case '"':
+ {
+ _out << "\\\"";
+ break;
+ }
+ case '\\':
+ {
+ _out << "\\\\";
+ break;
+ }
+ case '\r':
+ {
+ _out << "\\r";
+ break;
+ }
+ case '\n':
+ {
+ _out << "\\n";
+ break;
+ }
+ case '\t':
+ {
+ _out << "\\t";
+ break;
+ }
+ case '\b':
+ {
+ _out << "\\b";
+ break;
+ }
+ case '\f':
+ {
+ _out << "\\f";
+ break;
+ }
+ default:
+ {
+ if(charSet.find(*c) == charSet.end())
+ {
+ unsigned char uc = *c; // Char may be signed, so make it positive.
+ stringstream s;
+ s << "\\"; // Print as octal if not in basic source character set.
+ s.flags(ios_base::oct);
+ s.width(3);
+ s.fill('0');
+ s << static_cast<unsigned>(uc);
+ _out << s.str();
+ }
+ else
+ {
+ _out << *c; // Print normally if in basic source character set.
+ }
+ break;
+ }
+ }
+ }
+
+ _out << "\""; // Closing "
+ break;
+ }
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+ }
+ else if(en)
+ {
+ string enumName = getSymbol(en);
+ string::size_type colon = value.rfind(':');
+ string enumerator;
+ if(colon != string::npos)
+ {
+ enumerator = fixIdent(value.substr(colon + 1));
+ }
+ else
+ {
+ enumerator = fixIdent(value);
+ }
+ _out << enumName << '.' << enumerator;
+ }
+ else
+ {
+ assert(false); // Unknown const type.
+ }
+ }
+}
+
+void
+Slice::Python::CodeVisitor::writeConstructorParams(const MemberInfoList& members)
+{
+ for(MemberInfoList::const_iterator p = members.begin(); p != members.end(); ++p)
+ {
+ _out << ", " << p->fixedName << "=";
+
+ const DataMemberPtr member = p->dataMember;
+ if(member->defaultValueType())
+ {
+ writeConstantValue(member->type(), member->defaultValueType(), member->defaultValue());
+ }
+ else
+ {
+ writeInitializer(member->type());
+ }
+ }
+}
+
+string
+Slice::Python::CodeVisitor::getOperationMode(Slice::Operation::Mode mode)
+{
+ string result;
+ switch(mode)
+ {
+ case Operation::Normal:
+ result = "Ice.OperationMode.Normal";
+ break;
+ case Operation::Nonmutating:
+ result = "Ice.OperationMode.Nonmutating";
+ break;
+ case Operation::Idempotent:
+ result = "Ice.OperationMode.Idempotent";
+ break;
+ }
+ return result;
+}
+
+void
+Slice::Python::CodeVisitor::collectClassMembers(const ClassDefPtr& p, MemberInfoList& allMembers, bool inherited)
+{
+ ClassList bases = p->bases();
+ if(!bases.empty() && !bases.front()->isInterface())
+ {
+ collectClassMembers(bases.front(), allMembers, true);
+ }
+
+ DataMemberList members = p->dataMembers();
+
+ for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
+ {
+ MemberInfo m;
+ if(p->hasMetaData("protected") || (*q)->hasMetaData("protected"))
+ {
+ m.fixedName = "_" + fixIdent((*q)->name());
+ }
+ else
+ {
+ m.fixedName = fixIdent((*q)->name());
+ }
+ m.inherited = inherited;
+ m.dataMember = *q;
+ allMembers.push_back(m);
+ }
+}
+
+void
+Slice::Python::CodeVisitor::collectExceptionMembers(const ExceptionPtr& p, MemberInfoList& allMembers, bool inherited)
+{
+ ExceptionPtr base = p->base();
+ if(base)
+ {
+ collectExceptionMembers(base, allMembers, true);
+ }
+
+ DataMemberList members = p->dataMembers();
+
+ for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
+ {
+ MemberInfo m;
+ m.fixedName = fixIdent((*q)->name());
+ m.inherited = inherited;
+ m.dataMember = *q;
+ allMembers.push_back(m);
+ }
+}
+
+string
+Slice::Python::CodeVisitor::editComment(const string& comment)
+{
+ //
+ // Strip HTML markup and javadoc links.
+ //
+ string result = comment;
+ string::size_type pos = 0;
+ do
+ {
+ pos = result.find('<', pos);
+ if(pos != string::npos)
+ {
+ string::size_type endpos = result.find('>', pos);
+ if(endpos == string::npos)
+ {
+ break;
+ }
+ result.erase(pos, endpos - pos + 1);
+ }
+ } while(pos != string::npos);
+
+ const string link = "{@link";
+ pos = 0;
+ do
+ {
+ pos = result.find(link, pos);
+ if(pos != string::npos)
+ {
+ result.erase(pos, link.size());
+ string::size_type endpos = result.find('}', pos);
+ if(endpos != string::npos)
+ {
+ string::size_type identpos = result.find_first_not_of(" \t#", pos);
+ if(identpos != string::npos && identpos < endpos)
+ {
+ string ident = result.substr(identpos, endpos - identpos);
+ result.replace(pos, endpos - pos + 1, fixIdent(ident));
+ }
+ }
+ }
+ } while(pos != string::npos);
+
+ //
+ // Strip @see sections.
+ //
+ static const string seeTag = "@see";
+ pos = 0;
+ do
+ {
+ //
+ // Look for the next @ and delete up to that, or
+ // to the end of the string, if not found.
+ //
+ pos = result.find(seeTag, pos);
+ if(pos != string::npos)
+ {
+ string::size_type next = result.find('@', pos + seeTag.size());
+ if(next != string::npos)
+ {
+ result.erase(pos, next - pos);
+ }
+ else
+ {
+ result.erase(pos, string::npos);
+ }
+ }
+ } while(pos != string::npos);
+
+ //
+ // Reformat @param, @return, and @throws.
+ //
+ static const string paramTag = "@param";
+ pos = 0;
+ bool first = true;
+ do
+ {
+ pos = result.find(paramTag, pos);
+ if(pos != string::npos)
+ {
+ result.replace(pos, paramTag.size() + 1, " ");
+
+ if(first)
+ {
+ string::size_type bol = result.rfind('\n', pos);
+ if(bol == string::npos)
+ {
+ bol = 0;
+ }
+ else
+ {
+ bol++;
+ }
+ result.insert(bol, "Arguments:\n");
+ first = false;
+ }
+ }
+ } while(pos != string::npos);
+
+ static const string returnTag = "@return";
+ pos = result.find(returnTag);
+ first = true;
+ if(pos != string::npos)
+ {
+ result.replace(pos, returnTag.size() + 1, " ");
+ string::size_type bol = result.rfind('\n', pos);
+ if(bol == string::npos)
+ {
+ bol = 0;
+ }
+ else
+ {
+ bol++;
+ }
+ result.insert(bol, "Returns:\n");
+ }
+
+ static const string throwsTag = "@throws";
+ pos = 0;
+ first = true;
+ do
+ {
+ pos = result.find(throwsTag, pos);
+ if(pos != string::npos)
+ {
+ result.replace(pos, throwsTag.size() + 1, " ");
+
+ if(first)
+ {
+ string::size_type bol = result.rfind('\n', pos);
+ if(bol == string::npos)
+ {
+ bol = 0;
+ }
+ else
+ {
+ bol++;
+ }
+ result.insert(bol, "Exceptions:\n");
+ first = false;
+ }
+ }
+ } while(pos != string::npos);
+
+ //
+ // Escape triple quotes.
+ //
+ static const string quotes = "'''";
+ pos = 0;
+ do
+ {
+ pos = result.find(quotes, pos);
+ if(pos != string::npos)
+ {
+ result.insert(pos, "\\");
+ pos += quotes.size() + 1;
+ }
+ } while(pos != string::npos);
+
+ //
+ // Fold multiple empty lines.
+ //
+ pos = 0;
+ while(true)
+ {
+ pos = result.find('\n', pos);
+ if(pos == string::npos)
+ {
+ break;
+ }
+
+ //
+ // Skip the next LF or CR/LF, if present.
+ //
+ if(pos < result.size() - 1 && result[pos + 1] == '\n')
+ {
+ pos += 2;
+ }
+ else if(pos < result.size() - 2 && result[pos + 1] == '\r' && result[pos + 2] == '\n')
+ {
+ pos += 3;
+ }
+ else
+ {
+ ++pos;
+ continue;
+ }
+
+ //
+ // Erase any more CR/LF characters.
+ //
+ string::size_type next = result.find_first_not_of("\r\n", pos);
+ if(next != string::npos)
+ {
+ result.erase(pos, next - pos);
+ }
+ }
+
+ //
+ // Remove trailing whitespace.
+ //
+ pos = result.find_last_not_of(" \t\r\n");
+ if(pos != string::npos)
+ {
+ result.erase(pos + 1, result.size() - pos - 1);
+ }
+
+ return result;
+}
+
+void
+Slice::Python::generate(const UnitPtr& un, bool all, bool checksum, const vector<string>& includePaths, Output& out)
+{
+ Slice::Python::MetaDataVisitor visitor;
+ un->visit(&visitor, false);
+
+ out << nl << "import Ice, IcePy, __builtin__";
+
+ if(!all)
+ {
+ vector<string> paths = includePaths;
+ for(vector<string>::iterator p = paths.begin(); p != paths.end(); ++p)
+ {
+ *p = fullPath(*p);
+ }
+
+ StringList includes = un->includeFiles();
+ for(StringList::const_iterator q = includes.begin(); q != includes.end(); ++q)
+ {
+ string file = changeInclude(*q, paths);
+ replace(file.begin(), file.end(), '/', '_');
+ out << nl << "import " << file << "_ice";
+ }
+ }
+
+ set<string> moduleHistory;
+
+ ModuleVisitor moduleVisitor(out, moduleHistory);
+ un->visit(&moduleVisitor, true);
+
+ CodeVisitor codeVisitor(out, moduleHistory);
+ un->visit(&codeVisitor, false);
+
+ if(checksum)
+ {
+ ChecksumMap checksums = createChecksums(un);
+ if(!checksums.empty())
+ {
+ out << sp;
+ for(ChecksumMap::const_iterator p = checksums.begin(); p != checksums.end(); ++p)
+ {
+ out << nl << "Ice.sliceChecksums[\"" << p->first << "\"] = \"";
+ ostringstream str;
+ str.flags(ios_base::hex);
+ str.fill('0');
+ for(vector<unsigned char>::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
+ {
+ str << (int)(*q);
+ }
+ out << str.str() << "\"";
+ }
+ }
+ }
+
+ out << nl; // Trailing newline.
+}
+
+string
+Slice::Python::scopedToName(const string& scoped)
+{
+ string result = fixIdent(scoped);
+ if(result.find("::") == 0)
+ {
+ result.erase(0, 2);
+ }
+
+ string::size_type pos;
+ while((pos = result.find("::")) != string::npos)
+ {
+ result.replace(pos, 2, ".");
+ }
+
+ return result;
+}
+
+string
+Slice::Python::fixIdent(const string& ident)
+{
+ if(ident[0] != ':')
+ {
+ return lookupKwd(ident);
+ }
+ vector<string> ids = splitScopedName(ident);
+ transform(ids.begin(), ids.end(), ids.begin(), ptr_fun(lookupKwd));
+ stringstream result;
+ for(vector<string>::const_iterator i = ids.begin(); i != ids.end(); ++i)
+ {
+ result << "::" + *i;
+ }
+ return result.str();
+}
+
+string
+Slice::Python::getPackageMetadata(const ContainedPtr& cont)
+{
+ UnitPtr unit = cont->container()->unit();
+ string file = cont->file();
+ assert(!file.empty());
+
+ static const string prefix = "python:package:";
+ DefinitionContextPtr dc = unit->findDefinitionContext(file);
+ assert(dc);
+ string q = dc->findMetaData(prefix);
+ if(!q.empty())
+ {
+ q = q.substr(prefix.size());
+ }
+ return q;
+}
+
+string
+Slice::Python::getAbsolute(const ContainedPtr& cont, const string& suffix, const string& nameSuffix)
+{
+ string scope = scopedToName(cont->scope());
+
+ string package = getPackageMetadata(cont);
+ if(!package.empty())
+ {
+ if(!scope.empty())
+ {
+ scope = package + "." + scope;
+ }
+ else
+ {
+ scope = package + ".";
+ }
+ }
+
+ if(suffix.empty())
+ {
+ return scope + fixIdent(cont->name() + nameSuffix);
+ }
+ else
+ {
+ return scope + suffix + fixIdent(cont->name() + nameSuffix);
+ }
+}
+
+void
+Slice::Python::printHeader(IceUtilInternal::Output& out)
+{
+ static const char* header =
+"# **********************************************************************\n"
+"#\n"
+"# Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.\n"
+"#\n"
+"# This copy of Ice is licensed to you under the terms described in the\n"
+"# ICE_LICENSE file included in this distribution.\n"
+"#\n"
+"# **********************************************************************\n"
+ ;
+
+ out << header;
+ out << "#\n";
+ out << "# Ice version " << ICE_STRING_VERSION << "\n";
+ out << "#\n";
+}
+
+bool
+Slice::Python::MetaDataVisitor::visitUnitStart(const UnitPtr& p)
+{
+ static const string prefix = "python:";
+
+ //
+ // Validate global metadata in the top-level file and all included files.
+ //
+ StringList files = p->allFiles();
+
+ for(StringList::iterator q = files.begin(); q != files.end(); ++q)
+ {
+ string file = *q;
+ DefinitionContextPtr dc = p->findDefinitionContext(file);
+ assert(dc);
+ StringList globalMetaData = dc->getMetaData();
+ for(StringList::const_iterator r = globalMetaData.begin(); r != globalMetaData.end(); ++r)
+ {
+ string s = *r;
+ if(_history.count(s) == 0)
+ {
+ if(s.find(prefix) == 0)
+ {
+ static const string packagePrefix = "python:package:";
+ if(s.find(packagePrefix) != 0 || s.size() == packagePrefix.size())
+ {
+ emitWarning(file, "", "ignoring invalid global metadata `" + s + "'");
+ }
+ }
+ _history.insert(s);
+ }
+ }
+ }
+ return true;
+}
+
+bool
+Slice::Python::MetaDataVisitor::visitModuleStart(const ModulePtr& p)
+{
+ reject(p);
+ return true;
+}
+
+void
+Slice::Python::MetaDataVisitor::visitClassDecl(const ClassDeclPtr& p)
+{
+ reject(p);
+}
+
+bool
+Slice::Python::MetaDataVisitor::visitClassDefStart(const ClassDefPtr& p)
+{
+ reject(p);
+ return true;
+}
+
+bool
+Slice::Python::MetaDataVisitor::visitExceptionStart(const ExceptionPtr& p)
+{
+ reject(p);
+ return true;
+}
+
+bool
+Slice::Python::MetaDataVisitor::visitStructStart(const StructPtr& p)
+{
+ reject(p);
+ return true;
+}
+
+void
+Slice::Python::MetaDataVisitor::visitOperation(const OperationPtr& p)
+{
+ TypePtr ret = p->returnType();
+ if(ret)
+ {
+ validateSequence(p->file(), p->line(), ret, p->getMetaData());
+ }
+
+ ParamDeclList params = p->parameters();
+ for(ParamDeclList::iterator q = params.begin(); q != params.end(); ++q)
+ {
+ validateSequence(p->file(), (*q)->line(), (*q)->type(), (*q)->getMetaData());
+ }
+}
+
+void
+Slice::Python::MetaDataVisitor::visitDataMember(const DataMemberPtr& p)
+{
+ validateSequence(p->file(), p->line(), p->type(), p->getMetaData());
+}
+
+void
+Slice::Python::MetaDataVisitor::visitSequence(const SequencePtr& p)
+{
+ static const string protobuf = "python:protobuf:";
+ StringList metaData = p->getMetaData();
+ const string file = p->file();
+ const string line = p->line();
+ for(StringList::const_iterator q = metaData.begin(); q != metaData.end(); )
+ {
+ string s = *q++;
+ if(s.find(protobuf) == 0)
+ {
+ //
+ // Remove from list so validateSequence does not try to handle as well.
+ //
+ metaData.remove(s);
+
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(p->type());
+ if(!builtin || builtin->kind() != Builtin::KindByte)
+ {
+ emitWarning(file, line, "ignoring invalid metadata `" + s + ": " +
+ "`protobuf' encoding must be a byte sequence");
+ }
+ }
+ }
+
+ validateSequence(file, line, p, metaData);
+}
+
+void
+Slice::Python::MetaDataVisitor::visitDictionary(const DictionaryPtr& p)
+{
+ reject(p);
+}
+
+void
+Slice::Python::MetaDataVisitor::visitEnum(const EnumPtr& p)
+{
+ reject(p);
+}
+
+void
+Slice::Python::MetaDataVisitor::visitConst(const ConstPtr& p)
+{
+ reject(p);
+}
+
+void
+Slice::Python::MetaDataVisitor::validateSequence(const string& file, const string& line,
+ const TypePtr& type, const StringList& meta)
+{
+ static const string prefix = "python:";
+
+ for(StringList::const_iterator p = meta.begin(); p != meta.end(); ++p)
+ {
+ string s = *p;
+ if(s.find(prefix) == 0)
+ {
+ string::size_type pos = s.find(':', prefix.size());
+ if(pos != string::npos && s.substr(prefix.size(), pos - prefix.size()) == "seq")
+ {
+ static const string seqPrefix = "python:seq:";
+ string arg = s.substr(seqPrefix.size(), pos - seqPrefix.size());
+ if(SequencePtr::dynamicCast(type))
+ {
+ if(arg == "tuple" || arg == "list" || arg == "default")
+ {
+ continue;
+ }
+ }
+ }
+ emitWarning(file, line, "ignoring invalid metadata `" + s + "'");
+ }
+ }
+}
+
+void
+Slice::Python::MetaDataVisitor::reject(const ContainedPtr& cont)
+{
+ StringList localMetaData = cont->getMetaData();
+
+ static const string prefix = "python:";
+
+ for(StringList::const_iterator p = localMetaData.begin(); p != localMetaData.end(); ++p)
+ {
+ if(p->find(prefix) == 0)
+ {
+ emitWarning(cont->file(), cont->line(), "ignoring invalid metadata `" + *p + "'");
+ }
+ }
+}