diff options
author | Joe George <joe@zeroc.com> | 2019-02-18 13:21:34 -0500 |
---|---|---|
committer | Joe George <joe@zeroc.com> | 2019-02-18 13:21:34 -0500 |
commit | 68fa96c625a683f7cae45d5f6c646b9b5c269b3a (patch) | |
tree | 59ba0f3e2e839d233f724289b4b178108109674b /cpp/src/slice2swift/SwiftUtil.cpp | |
parent | Fixed formatting bug (diff) | |
download | ice-68fa96c625a683f7cae45d5f6c646b9b5c269b3a.tar.bz2 ice-68fa96c625a683f7cae45d5f6c646b9b5c269b3a.tar.xz ice-68fa96c625a683f7cae45d5f6c646b9b5c269b3a.zip |
Initial commit of Swift mapping
Diffstat (limited to 'cpp/src/slice2swift/SwiftUtil.cpp')
-rw-r--r-- | cpp/src/slice2swift/SwiftUtil.cpp | 931 |
1 files changed, 931 insertions, 0 deletions
diff --git a/cpp/src/slice2swift/SwiftUtil.cpp b/cpp/src/slice2swift/SwiftUtil.cpp new file mode 100644 index 00000000000..c7dd5c5f42c --- /dev/null +++ b/cpp/src/slice2swift/SwiftUtil.cpp @@ -0,0 +1,931 @@ + +// ********************************************************************** +// +// Copyright (c) 2003-2018 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 <IceUtil/OutputUtil.h> +#include <IceUtil/StringUtil.h> + +#include <SwiftUtil.h> + +using namespace std; +using namespace Slice; +using namespace IceUtilInternal; + +namespace +{ + +static string +lookupKwd(const string& name) +{ + // + // Keyword list. *Must* be kept in alphabetical order. + // + static const string keywordList[] = + { + "Any", "as", "associatedtype", "break", "case", "catch", "class", "continue", "default", "defer", "deinit", + "do", "else", "enum", "extension", "fallthrough", "false", "fileprivate", "for", "func", "guard", "if", + "import", "in", "init", "inout", "internal", "is", "let", "nil", "open", "operator", "private", "protocol", + "public", "repeat", "rethrows", "return", "self", "Self", "static", "struct", "subscript", "super", "switch", + "throw", "throws", "true", "try", "typealias", "var", "where", "while" + }; + bool found = binary_search(&keywordList[0], + &keywordList[sizeof(keywordList) / sizeof(*keywordList)], + name, + Slice::CICompare()); + if(found) + { + return "_" + name; + } + + return name; +} + +string +replace(string s, string patt, string val) +{ + string r = s; + string::size_type pos = r.find(patt); + while(pos != string::npos) + { + r.replace(pos, patt.size(), val); + pos += val.size(); + pos = r.find(patt, pos); + } + return r; +} + +// +// Split a scoped name into its components and return the components as a list of (unscoped) identifiers. +// +static StringList +splitScopedName(const string& scoped) +{ + assert(scoped[0] == ':'); + StringList 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 StringList +fixIds(const StringList& ids) +{ + StringList newIds; + for(StringList::const_iterator i = ids.begin(); i != ids.end(); ++i) + { + newIds.push_back(lookupKwd(*i)); + } + return newIds; +} + +} + +SwiftGenerator::ModuleMap SwiftGenerator::_modules; + +void +SwiftGenerator::validateMetaData(const UnitPtr& u) +{ + MetaDataVisitor visitor; + u->visit(&visitor, true); +} + +bool +SwiftGenerator::addModule(const ModulePtr& m, const string& module, const string& name) +{ + string scoped = m->scoped(); + ModuleMap::const_iterator i = _modules.find(scoped); + if(i != _modules.end()) + { + if(i->second.module != module || i->second.name != name) + { + return false; + } + } + else + { + ModulePrefix mp; + mp.m = m; + mp.module = module; + mp.name = name; + _modules[scoped] = mp; + } + return true; +} + +SwiftGenerator::ModulePrefix +SwiftGenerator::modulePrefix(const ModulePtr& m) +{ + return _modules[m->scoped()]; +} + +string +SwiftGenerator::moduleName(const ModulePtr& m) +{ + return _modules[m->scoped()].name; +} + +ModulePtr +SwiftGenerator::findModule(const ContainedPtr& cont) +{ + ModulePtr m = ModulePtr::dynamicCast(cont); + ContainerPtr container = cont->container(); + while(container && !m) + { + ContainedPtr contained = ContainedPtr::dynamicCast(container); + container = contained->container(); + m = ModulePtr::dynamicCast(contained); + } + assert(m); + return m; +} + +string +SwiftGenerator::getLocalScope(const string& scope, const string& separator) +{ + assert(!scope.empty()); + + // + // Remove trailing "::" if present. + // + string fixedScope; + if(scope[scope.size() - 1] == ':') + { + assert(scope[scope.size() - 2] == ':'); + fixedScope = scope.substr(0, scope.size() - 2); + } + else + { + fixedScope = scope; + } + if(fixedScope.empty()) + { + return ""; + } + const StringList ids = fixIds(splitScopedName(fixedScope)); + + // + // Return local scope for "::A::B::C" as A.B.C + // + stringstream result; + for(StringList::const_iterator i = ids.begin(); i != ids.end(); ++i) + { + if(i != ids.begin()) + { + result << separator; + } + result << *i; + } + return result.str(); + +} + +string +SwiftGenerator::typeToString(const TypePtr& type, const ContainedPtr& c) +{ + static const char* builtinTable[] = + { + "UInt8", + "Bool", + "Int16", + "Int32", + "Int64", + "Float", + "Double", + "String", + "Ice.Object", // Object + "Ice.ObjectPrx", // ObjectPrx + "AnyObject", // LocalObject + "Ice.Value" // Value + }; + + if(!type) + { + return ""; + } + + const bool hasNonOptionalMetadata = c && c->hasMetaData("swift:non-optional"); + const ParamDeclPtr p = ParamDeclPtr::dynamicCast(c); + const bool isOptionalParam = p && p->optional(); + + string str; + bool optional = isOptionalParam || (!isValueType(type) && !hasNonOptionalMetadata); + + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + str = builtinTable[builtin->kind()]; + return optional ? str + "?" : str; + } + + ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type); + if(cl) + { + str = getAbsolute(cl); + + if(cl->isLocal()) + { + const ClassDefPtr def = cl->definition(); + if(p && def && def->isDelegate()) + { + + // Swift closures are @escaping by default + if(p->hasMetaData("swift:non-optional")) + { + return "@escaping " + str; + } + else + { + return str + "?"; + } + } + } + return optional ? str + "?" : str; + } + + ProxyPtr proxy = ProxyPtr::dynamicCast(type); + if(proxy) + { + str = getAbsolute(proxy->_class(), "", "Prx"); + return optional ? str + "?" : str; + } + + DictionaryPtr dict = DictionaryPtr::dynamicCast(type); + if(dict) + { + str = getAbsolute(dict); + return optional ? str + "?" : str; + } + + ContainedPtr contained = ContainedPtr::dynamicCast(type); + if(contained) + { + str = getAbsolute(contained); + return optional ? str + "?" : str; + } + + return "???"; +} + +std::string +SwiftGenerator::typeToProxyImpl(const TypePtr& type) +{ + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + switch(builtin->kind()) + { + case Builtin::KindObjectProxy: + { + return "_ObjectPrxI"; + break; + } + default: + { + assert(false); + break; + } + } + + } + ProxyPtr proxy = ProxyPtr::dynamicCast(type); + if(proxy) + { + return getAbsolute(proxy->_class(), "_", "PrxI"); + } + + return "???"; +} + +// +// Check the given identifier against Swift's list of reserved words. If it matches +// a reserved word, then an escaped version is returned with a leading underscore. +// +std::string +SwiftGenerator::fixIdent(const std::string& ident) +{ + if(ident[0] != ':') + { + return lookupKwd(ident); + } + StringList ids = splitScopedName(ident); + transform(ids.begin(), ids.end(), ids.begin(), ptr_fun(lookupKwd)); + stringstream result; + for(StringList::const_iterator i = ids.begin(); i != ids.end(); ++i) + { + result << "::" + *i; + } + return result.str(); +} + +std::string +SwiftGenerator::fixName(const ContainedPtr& cont) +{ + return moduleName(findModule(cont)) + cont->name(); +} + +// +// Get the fully-qualified name of the given definition. If a suffix is provided, +// it is prepended to the definition's unqualified name. If the nameSuffix +// is provided, it is appended to the container's name. +// +std::string +SwiftGenerator::getAbsolute(const ContainedPtr& cont, + const string& pfx, + const string& suffix) +{ + string str = fixIdent(cont->scope() + pfx + cont->name() + suffix); + if(str.find("::") == 0) + { + str.erase(0, 2); + } + + return replace(str, "::", "."); +} + +std::string +SwiftGenerator::modeToString(Operation::Mode opMode) +{ + string mode; + switch(opMode) + { + case Operation::Normal: + { + mode = "Ice.OperationMode.Normal"; + break; + } + case Operation::Nonmutating: + { + mode = "Ice.OperationMode.Nonmutating"; + break; + } + case Operation::Idempotent: + { + mode = "Ice.OperationMode.Idempotent"; + break; + } + default: + { + assert(false); + break; + } + } + return mode; +} + +bool +SwiftGenerator::isObjcRepresentable(const TypePtr& type) +{ + return BuiltinPtr::dynamicCast(type); +} + +bool +SwiftGenerator::isObjcRepresentable(const DataMemberList& members) +{ + for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q) + { + if(!isObjcRepresentable((*q)->type())) + { + return false; + } + } + return true; +} + +bool +SwiftGenerator::isValueType(const TypePtr& type) +{ + if(!type) + { + return true; + } + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + switch(builtin->kind()) + { + case Builtin::KindObject: + case Builtin::KindObjectProxy: + case Builtin::KindLocalObject: + case Builtin::KindValue: + { + return false; + break; + } + default: + { + return true; + break; + } + } + } + + ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type); + if(cl) + { + return false; + } + + ProxyPtr proxy = ProxyPtr::dynamicCast(type); + if(proxy) + { + return false; + } + + DictionaryPtr dict = DictionaryPtr::dynamicCast(type); + if(dict) + { + return true; + } + + ContainedPtr contained = ContainedPtr::dynamicCast(type); + if(contained) + { + return true; + } + + return true; +} + +bool +SwiftGenerator::isProxyType(const TypePtr& p) +{ + const BuiltinPtr builtin = BuiltinPtr::dynamicCast(p); + return (builtin && builtin->kind() == Builtin::KindObjectProxy) || ProxyPtr::dynamicCast(p); +} + +void +SwiftGenerator::writeTuple(IceUtilInternal::Output& out, const StringList& tuple) +{ + if(tuple.size() > 1) + { + out << "("; + } + + for(StringList::const_iterator q = tuple.begin(); q != tuple.end(); ++q) + { + if(q != tuple.begin()) + { + out << ", "; + } + out << (*q); + } + if(tuple.size() > 1) + { + out << ")"; + } +} + +void +SwiftGenerator::writeDataMembers(IceUtilInternal::Output& out, const DataMemberList& members, bool writeGetter) +{ + for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q) + { + const TypePtr type = (*q)->type(); + out << nl; + + // not a protocol + if(!writeGetter) + { + out << "public "; + } + + out << "var " << (*q)->name() << ": " << typeToString(type, (*q)); + + // protocol + if(writeGetter) + { + out << " { get }"; + } + } +} + +void +SwiftGenerator::writeInitializer(IceUtilInternal::Output& out, + const DataMemberList& members, + const DataMemberList& allMembers) +{ + + DataMemberList baseMembers; + + for(DataMemberList::const_iterator q = allMembers.begin(); q != allMembers.end(); ++q) + { + if(find(members.begin(), members.end(), *q) == members.end()) + { + baseMembers.push_back(*q); + } + } + + // initializer + out << nl << "public init"; + writeInitializerMembers(out, allMembers.empty() ? members : allMembers); + out << sb; + + for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q) + { + const string n = (*q)->name(); + out << nl << "self." << n << " = " << n; + } + + // call base class init + if(!baseMembers.empty()) + { + out << nl << "super.init"; + writeInitializerMembers(out, baseMembers, false); + } + + out << eb; +} + +void +SwiftGenerator::writeInitializerMembers(IceUtilInternal::Output& out, const DataMemberList& members, bool useType) +{ + out << "("; + for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q) + { + if(q != members.begin()) + { + out << ", "; + } + out << (*q)->name(); + + if(useType) + { + out << ": " << typeToString((*q)->type(), *q); + } + else + { + out << ": " << (*q)->name(); + } + } + out << ")"; +} + +void +SwiftGenerator::writeOperation(IceUtilInternal::Output& out, const OperationPtr& op, bool) +{ + const string opName = op->name(); + StringList metaData = op->getMetaData(); + ExceptionList throws = op->throws(); + const ParamDeclList outParams = op->outParameters(); + StringList returns; + throws.sort(); + throws.unique(); + + out << nl << "func " << opName; + writeOperationsParameters(out, op->parameters()); + //TODO: remove cpp:noexcept once swift:nothrow is everywhere + if(!op->hasMetaData("cpp:noexcept") && !op->hasMetaData("swift:nothrow")) + { + out << " throws"; + } + + string ret = typeToString(op->returnType(), op); + if(!ret.empty()) + { + returns.push_back(ret); + } + + for(ParamDeclList::const_iterator p = outParams.begin(); p != outParams.end(); ++p) + { + returns.push_back(typeToString((*p)->type())); + } + + if(!returns.empty()) + { + out << " -> "; + writeTuple(out, returns); + } +} + +void +SwiftGenerator::writeOperationsParameters(IceUtilInternal::Output& out, const ParamDeclList& parameters) +{ + out << "("; + for(ParamDeclList::const_iterator q = parameters.begin(); q != parameters.end() && !(*q)->isOutParam(); ++q) + { + + if(q != parameters.begin()) + { + out << ", "; + } + + out << (*q)->name() << ": " << typeToString((*q)->type(), *q); + } + out << ")"; +} + +void +SwiftGenerator::writeCastFuncs(IceUtilInternal::Output& out, const ClassDefPtr& p) +{ + const string prx = fixName(p) + "Prx"; + const string prxImpl = "_" + prx + "I"; + out << nl << "public func checkedCast(prx: ObjectPrx, type: " << prx << ".Protocol, "; + out << "facet: String? = nil, context: Context? = nil) throws -> " << prx << "?"; + out << sb << nl; + out << "return try " << prxImpl << ".checkedCast(prx: prx, facet: facet, context: context) as " << prxImpl << "?"; + out << eb << nl; + + out << nl << "public func uncheckedCast(prx: ObjectPrx, type: " << prx << ".Protocol, "; + out << "facet: String? = nil, context: Context? = nil) -> " << prx << "?"; + out << sb << nl; + out << "return " << prxImpl << ".uncheckedCast(prx: prx, facet: facet, context: context) as " << prxImpl << "?"; + out << eb << nl; + + out << nl << "public extension Ice.InputStream"; + out << sb << nl; + out << "func read(proxy: " << prx << ".Protocol) throws -> " << prx << "?"; + out << sb << nl; + out << "return try " << prxImpl << ".ice_read(from: self)"; + out << eb << nl; + + out << "func read(proxyArray: " << prx << ".Protocol) throws -> [" << prx << "?]"; + out << sb << nl; + // out << "return try " << prxImpl << ".ice_read(from: self)"; + out << "#warning(\"add generated proxy arrays\")" << nl; + out << "preconditionFailure(\"TODO\")"; + out << eb << nl; + out << eb << nl; +} + +void +SwiftGenerator::writeStaticId(IceUtilInternal::Output& out, const ClassDefPtr& p) +{ + const string prx = fixName(p) + "Prx"; + const string prxImpl = "_" + prx + "I"; + + out << nl << "public func ice_staticId(_: " << prx << ".Protocol) -> String"; + out << sb << nl; + out << "return " << prxImpl << ".ice_staticId()"; + out << eb << nl; +} + +void +SwiftGenerator::writeMarshalUnmarshalCode(IceUtilInternal::Output& out, const ClassDefPtr& p, const OperationPtr& op) +{ + const string prx = fixName(p) + "Prx"; + const string proxyImpl = "_" + prx + "I"; + const string returnType = typeToString(op->returnType()); + const ParamDeclList inParams = op->inParameters(); + const ParamDeclList outParams = op->outParameters(); + const bool returnsData = op->returnsData(); + const bool returnsInputStream = !op->outParameters().empty() || op->returnType(); + StringList returnTuple; + + out << "let impl = self as! " << proxyImpl; + out << nl << "let os = impl._createOutputStream()"; + out << nl << "os.startEncapsulation()"; + + for(ParamDeclList::const_iterator q = inParams.begin(); q != inParams.end(); ++q) + { + const SequencePtr sequence = SequencePtr::dynamicCast(op->returnType()); + if(isProxyType((*q)->type())) + { + out << nl << "os.write(proxy: " << (*q)->name() << ")"; + } + else if(sequence && isProxyType(sequence->type())) + { + out << nl << "os.write(proxyArray: " << (*q)->name() << ")"; + } + else + { + out << nl << (*q)->name() << ".ice_write(to: os)"; + + } + } + out << nl << "os.endEncapsulation()"; + + out << nl << "let " << (returnsInputStream ? "ins " : "_ "); + out << "= try impl._invoke("; + + out.useCurrentPosAsIndent(); + out << "operation: \"" << op->name() << "\","; + out << nl << "mode: " << modeToString(op->mode()) << ","; + out << nl << "twowayOnly: " << (returnsData ? "true" : "false") << ","; + out << nl << "inParams: os,"; + out << nl << "hasOutParams: " << (op->outParameters().empty() ? "false" : "true"); + out << ")"; + out.restoreIndent(); + out << nl; + + // + // TODO: Sequence and optioanl read (eg. optional tags and array min size) + // + + for(ParamDeclList::const_iterator q = outParams.begin(); q != outParams.end(); ++q) + { + out << "let " << (*q)->name() << " = try " << typeToString((*q)->type()) << "(from: ins)"; + returnTuple.push_back((*q)->name()); + } + + if(!returnType.empty()) + { + const SequencePtr sequence = SequencePtr::dynamicCast(op->returnType()); + if(isProxyType(op->returnType())) + { + const ProxyPtr proxy = ProxyPtr::dynamicCast(op->returnType()); + if(proxy) + { + const string retPrx = getAbsolute(proxy->_class(), "", "Prx"); + out << nl << "let ret = try ins.read(proxy: " << retPrx << ".self)"; + } + else + { + out << nl << "let ret = try ins.read(proxy: ObjectPrx.self)"; + } + returnTuple.push_front("ret"); + } + else if(sequence && isProxyType(sequence->type())) + { + const ProxyPtr proxy = ProxyPtr::dynamicCast(op->returnType()); + if(proxy) + { + const string retPrx = getAbsolute(proxy->_class(), "", "Prx"); + out << nl << "let ret = try ins.read(proxyArray: " << retPrx << ".self)"; + } + else + { + out << nl << "let ret = try ins.read(proxyArray: ObjectPrx.self)"; + } + returnTuple.push_front("ret"); + } + else if(op->returnIsOptional()) + { + out << nl << "var ret: " << returnType; + out << nl << "try " << "ret.ice_read(from: ins)"; + returnTuple.push_front("ret"); + } + else + { + out << nl << "var ret = " << returnType << "()"; + out << nl << "try " << "ret.ice_read(from: ins)"; + returnTuple.push_front("ret"); + } + } + + if(!returnTuple.empty()) + { + out << nl << "return "; + writeTuple(out, returnTuple); + } +} + +const string SwiftGenerator::MetaDataVisitor::_msg= "ignoring invalid metadata"; + +bool +SwiftGenerator::MetaDataVisitor::visitModuleStart(const ModulePtr& p) +{ + validate(p); + return true; +} + +bool +SwiftGenerator::MetaDataVisitor::visitClassDefStart(const ClassDefPtr& p) +{ + validate(p); + return true; +} +bool +SwiftGenerator::MetaDataVisitor::visitExceptionStart(const ExceptionPtr& p) +{ + validate(p); + return true; +} + +bool +SwiftGenerator::MetaDataVisitor::visitStructStart(const StructPtr& p) +{ + validate(p); + return true; +} +void +SwiftGenerator::MetaDataVisitor::visitSequence(const SequencePtr& p) +{ + validate(p); +} +void +SwiftGenerator::MetaDataVisitor::visitDictionary(const DictionaryPtr& p) +{ + validate(p); +} +void +SwiftGenerator::MetaDataVisitor::visitEnum(const EnumPtr& p) +{ + validate(p); +} +void +SwiftGenerator::MetaDataVisitor::visitConst(const ConstPtr& p) +{ + validate(p); +} + +void +SwiftGenerator::MetaDataVisitor::validate(const ContainedPtr& cont) +{ + ModulePtr m = ModulePtr::dynamicCast(cont); + if(m) + { + bool error = false; + bool found = false; + StringList meta = cont->getMetaData(); + + for(StringList::iterator p = meta.begin(); p != meta.end();) + { + string s = *p++; + + // Module metadata must be of the form swift:module:{{modulename}}:{{prefix}} + const string modulePrefix = "swift:module:"; + string module; + string prefix; + if(s.find(modulePrefix) == 0) + { + found = true; + string metadata = trim(s.substr(modulePrefix.size())); + + size_t pos = metadata.find(":"); + if(pos != string::npos) + { + module = metadata.substr(0, pos); + prefix = metadata.substr(pos+1, metadata.size()); + } + + if(module.empty() || prefix.empty()) + { + m->definitionContext()->warning(InvalidMetaData, m->definitionContext()->filename(), + m->line(), _msg + " `" + s + "'"); + meta.remove(s); + error = true; + } + else + { + if(!addModule(m, module, prefix)) + { + modulePrefixError(m, s); + } + } + } + // else + // { + // m->definitionContext()->warning(InvalidMetaData, m->definitionContext()->filename(), + // m->line(), _msg + " `" + s + "'"); + // meta.remove(s); + // error = true; + // } + } + m->setMetaData(meta); + } +} + +void +SwiftGenerator::MetaDataVisitor::modulePrefixError(const ModulePtr& m, const std::string& metadata) +{ + string file = m->definitionContext()->filename(); + string line = m->line(); + ModulePrefix mp = modulePrefix(m); + string old_file = mp.m->definitionContext()->filename(); + string old_line = mp.m->line(); + ostringstream os; + if(!metadata.empty()) + { + os << _msg << " `" << metadata << "': "; + } + os << "inconsistent module prefix previously defined "; + if(old_file != file) + { + os << "in " << old_file << ":"; + } + else + { + os << "at line "; + } + os << line; + os << " as `" << mp.name << "'" << endl; + m->definitionContext()->warning(All, file, line, os.str()); +} |