// ********************************************************************** // // 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 #include #include 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()); }