diff options
author | Jose <jose@zeroc.com> | 2018-10-16 00:53:00 +0200 |
---|---|---|
committer | Jose <jose@zeroc.com> | 2018-10-16 00:53:00 +0200 |
commit | cf8292f9cdadcb6619287ada4f5273c25c5a3cfb (patch) | |
tree | 4a227f287d2a5e22e36b1f488f00a3c96add7a44 /cpp/src/slice2js | |
parent | Updated VS2017 requirement (diff) | |
download | ice-cf8292f9cdadcb6619287ada4f5273c25c5a3cfb.tar.bz2 ice-cf8292f9cdadcb6619287ada4f5273c25c5a3cfb.tar.xz ice-cf8292f9cdadcb6619287ada4f5273c25c5a3cfb.zip |
Typescript support
Diffstat (limited to 'cpp/src/slice2js')
-rw-r--r-- | cpp/src/slice2js/Gen.cpp | 1605 | ||||
-rw-r--r-- | cpp/src/slice2js/Gen.h | 93 | ||||
-rw-r--r-- | cpp/src/slice2js/JsUtil.cpp | 496 | ||||
-rw-r--r-- | cpp/src/slice2js/JsUtil.h | 34 | ||||
-rw-r--r-- | cpp/src/slice2js/Main.cpp | 31 |
5 files changed, 2025 insertions, 234 deletions
diff --git a/cpp/src/slice2js/Gen.cpp b/cpp/src/slice2js/Gen.cpp index 7a4e61f3115..be159aa206b 100644 --- a/cpp/src/slice2js/Gen.cpp +++ b/cpp/src/slice2js/Gen.cpp @@ -11,19 +11,11 @@ #include <IceUtil/StringUtil.h> #include <IceUtil/InputUtil.h> #include <Gen.h> -#include <limits> -#include <sys/stat.h> -#ifndef _WIN32 -#include <unistd.h> -#else -#include <direct.h> -#endif #include <IceUtil/Iterator.h> #include <IceUtil/UUID.h> #include <Slice/Checksum.h> #include <Slice/FileTracker.h> #include <Slice/Util.h> -#include <string.h> using namespace std; using namespace Slice; @@ -86,9 +78,289 @@ getDeprecateReason(const ContainedPtr& p1, const ContainedPtr& p2, const string& return deprecateReason; } +void +printHeader(IceUtilInternal::Output& out) +{ + static const char* header = + "// **********************************************************************\n" + "//\n" + "// Copyright (c) 2003-2018 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"; +} + +string +escapeParam(const ParamDeclList& params, const string& name) +{ + string r = name; + for(ParamDeclList::const_iterator p = params.begin(); p != params.end(); ++p) + { + if(Slice::JsGenerator::fixId((*p)->name()) == name) + { + r = name + "_"; + break; + } + } + return r; +} + +void +writeDocLines(Output& out, const StringList& lines, bool commentFirst, const string& space = " ") +{ + StringList l = lines; + if(!commentFirst) + { + out << l.front(); + l.pop_front(); + } + for(StringList::const_iterator i = l.begin(); i != l.end(); ++i) + { + out << nl << " *"; + if(!i->empty()) + { + out << space << *i; + } + } +} + +void +writeSeeAlso(Output& out, const StringList& lines, const string& space = " ") +{ + for(StringList::const_iterator i = lines.begin(); i != lines.end(); ++i) + { + out << nl << " *"; + if(!i->empty()) + { + out << space << "@see " << *i; + } + } +} + +string +getDocSentence(const StringList& lines) +{ + // + // Extract the first sentence. + // + ostringstream ostr; + for(StringList::const_iterator i = lines.begin(); i != lines.end(); ++i) + { + const string ws = " \t"; + + if(i->empty()) + { + break; + } + if(i != lines.begin() && i->find_first_not_of(ws) == 0) + { + ostr << " "; + } + string::size_type pos = i->find('.'); + if(pos == string::npos) + { + ostr << *i; + } + else if(pos == i->size() - 1) + { + ostr << *i; + break; + } + else + { + // + // Assume a period followed by whitespace indicates the end of the sentence. + // + while(pos != string::npos) + { + if(ws.find((*i)[pos + 1]) != string::npos) + { + break; + } + pos = i->find('.', pos + 1); + } + if(pos != string::npos) + { + ostr << i->substr(0, pos + 1); + break; + } + else + { + ostr << *i; + } + } + } + + return ostr.str(); +} + +void +writeDocSummary(Output& out, const ContainedPtr& p) +{ + if(p->comment().empty()) + { + return; + } + + CommentPtr doc = p->parseComment(false); + + out << nl << "/**"; + + if(!doc->overview().empty()) + { + writeDocLines(out, doc->overview(), true); + } + + if(!doc->misc().empty()) + { + writeDocLines(out, doc->misc(), true); + } + + if(!doc->seeAlso().empty()) + { + writeSeeAlso(out, doc->seeAlso()); + } + + if(!doc->deprecated().empty()) + { + out << nl << " *"; + out << nl << " * @deprecated "; + writeDocLines(out, doc->deprecated(), false); + } + else if(doc->isDeprecated()) + { + out << nl << " *"; + out << nl << " * @deprecated"; + } + + out << nl << " */"; +} + +enum OpDocParamType { OpDocInParams, OpDocOutParams, OpDocAllParams }; + +void +writeOpDocParams(Output& out, const OperationPtr& op, const CommentPtr& doc, OpDocParamType type, + const StringList& preParams = StringList(), const StringList& postParams = StringList()) +{ + ParamDeclList params; + switch (type) + { + case OpDocInParams: + params = op->inParameters(); + break; + case OpDocOutParams: + params = op->outParameters(); + break; + case OpDocAllParams: + params = op->parameters(); + break; + } + + if (!preParams.empty()) + { + writeDocLines(out, preParams, true); + } + + map<string, StringList> paramDoc = doc->parameters(); + for (ParamDeclList::iterator p = params.begin(); p != params.end(); ++p) + { + map<string, StringList>::iterator q = paramDoc.find((*p)->name()); + if(q != paramDoc.end()) + { + out << nl << " * @param " << Slice::JsGenerator::fixId(q->first) << " "; + writeDocLines(out, q->second, false); + } + } + + if(!postParams.empty()) + { + writeDocLines(out, postParams, true); + } +} + +void +writeOpDocExceptions(Output& out, const OperationPtr& op, const CommentPtr& doc) +{ + map<string, StringList> exDoc = doc->exceptions(); + for (map<string, StringList>::iterator p = exDoc.begin(); p != exDoc.end(); ++p) + { + // + // Try to locate the exception's definition using the name given in the comment. + // + string name = p->first; + ExceptionPtr ex = op->container()->lookupException(name, false); + if (ex) + { + name = ex->scoped().substr(2); + } + out << nl << " * @throws " << name << " "; + writeDocLines(out, p->second, false); + } } -Slice::JsVisitor::JsVisitor(Output& out) : _out(out) +void +writeOpDocSummary(Output& out, const OperationPtr& op, const CommentPtr& doc, OpDocParamType type, bool showExceptions, + const StringList& preParams = StringList(), const StringList& postParams = StringList(), + const StringList& returns = StringList()) +{ + out << nl << "/**"; + + if (!doc->overview().empty()) + { + writeDocLines(out, doc->overview(), true); + } + + writeOpDocParams(out, op, doc, type, preParams, postParams); + + if(!returns.empty()) + { + out << nl << " * @return "; + writeDocLines(out, returns, false); + } + + if(showExceptions) + { + writeOpDocExceptions(out, op, doc); + } + + if(!doc->misc().empty()) + { + writeDocLines(out, doc->misc(), true); + } + + if(!doc->seeAlso().empty()) + { + writeSeeAlso(out, doc->seeAlso()); + } + + if(!doc->deprecated().empty()) + { + out << nl << " *"; + out << nl << " * @deprecated "; + writeDocLines(out, doc->deprecated(), false); + } + else if(doc->isDeprecated()) + { + out << nl << " *"; + out << nl << " * @deprecated"; + } + + out << nl << " */"; +} + +} + +Slice::JsVisitor::JsVisitor(Output& out, const vector<pair<string, string>>& imports) : + _out(out), + _imports(imports) { } @@ -96,6 +368,12 @@ Slice::JsVisitor::~JsVisitor() { } +vector<pair<string, string>> +Slice::JsVisitor::imports() const +{ + return _imports; +} + void Slice::JsVisitor::writeMarshalDataMembers(const DataMemberList& dataMembers, const DataMemberList& optionalMembers) { @@ -131,7 +409,7 @@ Slice::JsVisitor::writeUnmarshalDataMembers(const DataMemberList& dataMembers, c } void -Slice::JsVisitor::writeInitDataMembers(const DataMemberList& dataMembers, const string& scope) +Slice::JsVisitor::writeInitDataMembers(const DataMemberList& dataMembers) { for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) { @@ -188,7 +466,7 @@ Slice::JsVisitor::getValue(const string& scope, const TypePtr& type) EnumPtr en = EnumPtr::dynamicCast(type); if(en) { - return getReference(scope, en->scoped()) + '.' + fixId((*en->enumerators().begin())->name()); + return fixId(en->scoped()) + '.' + fixId((*en->enumerators().begin())->name()); } StructPtr st = StructPtr::dynamicCast(type); @@ -208,7 +486,7 @@ Slice::JsVisitor::writeConstantValue(const string& scope, const TypePtr& type, c ConstPtr constant = ConstPtr::dynamicCast(valueType); if(constant) { - os << getReference(scope, constant->scoped()); + os << fixId(constant->scoped()); } else { @@ -243,7 +521,7 @@ Slice::JsVisitor::writeConstantValue(const string& scope, const TypePtr& type, c { EnumeratorPtr lte = EnumeratorPtr::dynamicCast(valueType); assert(lte); - os << getReference(scope, ep->scoped()) << '.' << fixId(lte->name()); + os << fixId(ep->scoped()) << '.' << fixId(lte->name()); } else { @@ -331,18 +609,20 @@ Slice::JsVisitor::writeDocComment(const ContainedPtr& p, const string& deprecate _out << nl << " **/"; } -Slice::Gen::Gen(const string& base, const vector<string>& includePaths, const string& dir) : +Slice::Gen::Gen(const string& base, const vector<string>& includePaths, const string& dir, bool typeScript) : _includePaths(includePaths), - _useStdout(false) + _useStdout(false), + _typeScript(typeScript) { _fileBase = base; + string::size_type pos = base.find_last_of("/\\"); if(pos != string::npos) { _fileBase = base.substr(pos + 1); } - string file = _fileBase + ".js"; + string file = _fileBase + (typeScript ? ".d.ts" : ".js"); if(!dir.empty()) { @@ -358,14 +638,16 @@ Slice::Gen::Gen(const string& base, const vector<string>& includePaths, const st } FileTracker::instance()->addFile(file); - printHeader(); + printHeader(_out); printGeneratedHeader(_out, _fileBase + ".ice"); } -Slice::Gen::Gen(const string& base, const vector<string>& includePaths, const string& dir, ostream& out) : +Slice::Gen::Gen(const string& base, const vector<string>& includePaths, const string& dir, bool typeScript, + ostream& out) : _out(out), _includePaths(includePaths), - _useStdout(true) + _useStdout(true), + _typeScript(typeScript) { _fileBase = base; string::size_type pos = base.find_last_of("/\\"); @@ -374,7 +656,7 @@ Slice::Gen::Gen(const string& base, const vector<string>& includePaths, const st _fileBase = base.substr(pos + 1); } - printHeader(); + printHeader(_out); printGeneratedHeader(_out, _fileBase + ".ice"); } @@ -390,70 +672,106 @@ void Slice::Gen::generate(const UnitPtr& p) { // - // Check for global "js:ice-build" and "js:es6-module" - // metadata. If this is set then we are building Ice. + // Check for "js:module:ice" metadata. If this + // is set then we are building Ice. + // + ModuleList modules = p->modules(); + bool icejs = false; + for(ModuleList::const_iterator i = modules.begin(); i != modules.end(); i++) + { + if(p->topLevelFile() == (*i)->definitionContext()->filename() && + getModuleMetadata(ContainedPtr::dynamicCast(*i)) == "ice") + { + icejs = true; + break; + } + } + + // + // Check for global "js:es6-module" metadata. // DefinitionContextPtr dc = p->findDefinitionContext(p->topLevelFile()); assert(dc); StringList globalMetaData = dc->getMetaData(); - bool icejs = find(globalMetaData.begin(), globalMetaData.end(), "js:ice-build") != globalMetaData.end(); bool es6module = find(globalMetaData.begin(), globalMetaData.end(), "js:es6-module") != globalMetaData.end(); - _out << nl << "/* eslint-disable */"; - _out << nl << "/* jshint ignore: start */"; - _out << nl; + if(_typeScript) + { + TypeScriptRequireVisitor requireVisitor(_out); + p->visit(&requireVisitor, false); + requireVisitor.writeRequires(p); + + // + // If at some point TypeScript adds an operator to refer to a type in the global scope + // we can get rid of the TypeScriptAliasVisitor and use this. For now we need to generate + // a type alias when there is an abiguity. + // see: https://github.com/Microsoft/TypeScript/issues/983 + // + TypeScriptAliasVisitor aliasVisitor(_out); + p->visit(&aliasVisitor, false); + aliasVisitor.writeAlias(p); - if(!es6module) + TypeScriptVisitor typeScriptVisitor(_out, requireVisitor.imports()); + p->visit(&typeScriptVisitor, false); + } + else { - if(icejs) - { - _out.zeroIndent(); - _out << nl << "/* slice2js browser-bundle-skip */"; - _out.restoreIndent(); - } - _out << nl << "(function(module, require, exports)"; - _out << sb; - if(icejs) + _out << nl << "/* eslint-disable */"; + _out << nl << "/* jshint ignore: start */"; + _out << nl; + + if(!es6module) { - _out.zeroIndent(); - _out << nl << "/* slice2js browser-bundle-skip-end */"; - _out.restoreIndent(); + if(icejs) + { + _out.zeroIndent(); + _out << nl << "/* slice2js browser-bundle-skip */"; + _out.restoreIndent(); + } + _out << nl << "(function(module, require, exports)"; + _out << sb; + if(icejs) + { + _out.zeroIndent(); + _out << nl << "/* slice2js browser-bundle-skip-end */"; + _out.restoreIndent(); + } } - } - RequireVisitor requireVisitor(_out, _includePaths, icejs, es6module); - p->visit(&requireVisitor, false); - vector<string> seenModules = requireVisitor.writeRequires(p); + RequireVisitor requireVisitor(_out, _includePaths, icejs, es6module); + p->visit(&requireVisitor, false); + vector<string> seenModules = requireVisitor.writeRequires(p); - TypesVisitor typesVisitor(_out, seenModules, icejs); - p->visit(&typesVisitor, false); + TypesVisitor typesVisitor(_out, seenModules, icejs); + p->visit(&typesVisitor, false); - // - // Export the top-level modules. - // - ExportVisitor exportVisitor(_out, icejs, es6module); - p->visit(&exportVisitor, false); + // + // Export the top-level modules. + // + ExportVisitor exportVisitor(_out, icejs, es6module); + p->visit(&exportVisitor, false); - if(!es6module) - { - if(icejs) + if(!es6module) { - _out.zeroIndent(); - _out << nl << "/* slice2js browser-bundle-skip */"; - _out.restoreIndent(); - } + if(icejs) + { + _out.zeroIndent(); + _out << nl << "/* slice2js browser-bundle-skip */"; + _out.restoreIndent(); + } - _out << eb; - _out << nl << "(typeof(global) !== \"undefined\" && typeof(global.process) !== \"undefined\" ? module : undefined," - << nl << " typeof(global) !== \"undefined\" && typeof(global.process) !== \"undefined\" ? require :" - << nl << " (typeof WorkerGlobalScope !== \"undefined\" && self instanceof WorkerGlobalScope) ? self.Ice._require : window.Ice._require," - << nl << " typeof(global) !== \"undefined\" && typeof(global.process) !== \"undefined\" ? exports :" - << nl << " (typeof WorkerGlobalScope !== \"undefined\" && self instanceof WorkerGlobalScope) ? self : window));"; + _out << eb; + _out << nl << "(typeof(global) !== \"undefined\" && typeof(global.process) !== \"undefined\" ? module : undefined," + << nl << " typeof(global) !== \"undefined\" && typeof(global.process) !== \"undefined\" ? require :" + << nl << " (typeof WorkerGlobalScope !== \"undefined\" && self instanceof WorkerGlobalScope) ? self.Ice._require : window.Ice._require," + << nl << " typeof(global) !== \"undefined\" && typeof(global.process) !== \"undefined\" ? exports :" + << nl << " (typeof WorkerGlobalScope !== \"undefined\" && self instanceof WorkerGlobalScope) ? self : window));"; - if(icejs) - { - _out.zeroIndent(); - _out << nl << "/* slice2js browser-bundle-skip-end */"; - _out.restoreIndent(); + if(icejs) + { + _out.zeroIndent(); + _out << nl << "/* slice2js browser-bundle-skip-end */"; + _out.restoreIndent(); + } } } } @@ -464,26 +782,6 @@ Slice::Gen::closeOutput() _out.close(); } -void -Slice::Gen::printHeader() -{ - static const char* header = -"// **********************************************************************\n" -"//\n" -"// Copyright (c) 2003-2018 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"; -} - Slice::Gen::RequireVisitor::RequireVisitor(IceUtilInternal::Output& out, vector<string> includePaths, bool icejs, bool es6modules) : JsVisitor(out), @@ -549,9 +847,17 @@ void Slice::Gen::RequireVisitor::visitSequence(const SequencePtr& seq) { BuiltinPtr builtin = BuiltinPtr::dynamicCast(seq->type()); - if(builtin && builtin->kind() == Builtin::KindObject) + if(builtin) { - _seenObjectSeq = true; + switch(builtin->kind()) + { + case Builtin::KindObject: + _seenObjectSeq = true; + case Builtin::KindObjectProxy: + _seenObjectProxySeq = true; + default: + break; + } } } @@ -559,9 +865,17 @@ void Slice::Gen::RequireVisitor::visitDictionary(const DictionaryPtr& dict) { BuiltinPtr builtin = BuiltinPtr::dynamicCast(dict->valueType()); - if(builtin && builtin->kind() == Builtin::KindObject) + if(builtin) { - _seenObjectDict = true; + switch(builtin->kind()) + { + case Builtin::KindObject: + _seenObjectDict = true; + case Builtin::KindObjectProxy: + _seenObjectProxyDict = true; + default: + break; + } } } @@ -579,66 +893,12 @@ bool iceBuiltinModule(const string& name) return name == "Glacier2" || name == "Ice" || name == "IceGrid" || name == "IceMX" || name == "IceStorm"; } -string -relativePath(string p1, string p2) -{ - vector<string> tokens1; - vector<string> tokens2; - - splitString(p1, "/\\", tokens1); - splitString(p2, "/\\", tokens2); - - string f1 = tokens1.back(); - string f2 = tokens2.back(); - - tokens1.pop_back(); - tokens2.pop_back(); - - vector<string>::const_iterator i1 = tokens1.begin(); - vector<string>::const_iterator i2 = tokens2.begin(); - - while(i1 != tokens1.end() && i2 != tokens2.end() && *i1 == *i2) - { - i1++; - i2++; - } - - // - // Different volumes, relative path not possible. - // - if(i1 == tokens1.begin() && i2 == tokens2.begin()) - { - return p1; - } - - string newPath; - if(i2 == tokens2.end()) - { - newPath += "./"; - for(; i1 != tokens1.end(); ++i1) - { - newPath += *i1 + "/"; - } - } - else - { - for(;i2 != tokens2.end();++i2) - { - newPath += "../"; - } - } - newPath += f1; - - return newPath; -} - } vector<string> Slice::Gen::RequireVisitor::writeRequires(const UnitPtr& p) { vector<string> seenModules; - map<string, list<string> > requires; if(_icejs) { @@ -925,7 +1185,7 @@ Slice::Gen::TypesVisitor::visitClassDefStart(const ClassDefPtr& p) { base = bases.front(); bases.erase(bases.begin()); - baseRef = getReference(scope, base->scoped()); + baseRef = fixId(base->scoped()); } else { @@ -1040,7 +1300,7 @@ Slice::Gen::TypesVisitor::visitClassDefStart(const ClassDefPtr& p) { _out << nl << "super" << spar << baseParamNames << epar << ';'; } - writeInitDataMembers(dataMembers, scope); + writeInitDataMembers(dataMembers); _out << eb; if(!p->isLocal()) @@ -1091,12 +1351,12 @@ Slice::Gen::TypesVisitor::visitClassDefStart(const ClassDefPtr& p) // // Define servant an proxy types for non local classes // - if(!p->isLocal()) + if(!p->isLocal() && (p->isInterface() || !p->allOperations().empty())) { _out << sp; writeDocComment(p, getDeprecateReason(p, 0, "type")); _out << nl << localScope << "." << (p->isInterface() ? p->name() : p->name() + "Disp") << " = class extends "; - if(hasBaseClass) + if(hasBaseClass && !base->allOperations().empty()) { _out << getLocalScope(base->scope()) << "." << base->name() << "Disp"; } @@ -1140,7 +1400,7 @@ Slice::Gen::TypesVisitor::visitClassDefStart(const ClassDefPtr& p) // Generate a proxy class for interfaces or classes with operations. // string proxyType = "undefined"; - if(p->isInterface() || p->allOperations().size() > 0) + if(p->isInterface() || !p->allOperations().empty()) { proxyType = localScope + '.' + prxName; string baseProxy = "Ice.ObjectPrx"; @@ -1442,7 +1702,7 @@ Slice::Gen::TypesVisitor::visitExceptionStart(const ExceptionPtr& p) string baseRef; if(base) { - baseRef = getReference(scope, base->scoped()); + baseRef = fixId(base->scoped()); } else { @@ -1514,7 +1774,7 @@ Slice::Gen::TypesVisitor::visitExceptionStart(const ExceptionPtr& p) _out << "_cause = \"\"" << epar; _out << sb; _out << nl << "super" << spar << baseParamNames << "_cause" << epar << ';'; - writeInitDataMembers(dataMembers, scope); + writeInitDataMembers(dataMembers); _out << eb; _out << sp; @@ -1529,8 +1789,6 @@ Slice::Gen::TypesVisitor::visitExceptionStart(const ExceptionPtr& p) _out << nl << "return \"" << p->scoped() << "\";"; _out << eb; - // TODO: equals? - if(!p->isLocal()) { _out << sp; @@ -1630,7 +1888,7 @@ Slice::Gen::TypesVisitor::visitStructStart(const StructPtr& p) _out << epar; _out << sb; - writeInitDataMembers(dataMembers, scope); + writeInitDataMembers(dataMembers); _out << eb; if(!p->isLocal()) @@ -1900,3 +2158,1038 @@ Slice::Gen::ExportVisitor::visitModuleStart(const ModulePtr& p) } return false; } + +Slice::Gen::TypeScriptRequireVisitor::TypeScriptRequireVisitor(IceUtilInternal::Output& out) : + JsVisitor(out), + _nextImport(0) +{ +} + +string +Slice::Gen::TypeScriptRequireVisitor::nextImportPrefix() +{ + ostringstream ns; + ns << "iceNS" << _nextImport++; + return ns.str(); +} + +void +Slice::Gen::TypeScriptRequireVisitor::addImport(const TypePtr& definition, const ContainedPtr& toplevel) +{ + if(!BuiltinPtr::dynamicCast(definition)) + { + const string m1 = getModuleMetadata(definition); + const string m2 = getModuleMetadata(toplevel); + + const string p1 = definition->definitionContext()->filename(); + const string p2 = toplevel->definitionContext()->filename(); + + addImport(m1, m2, p1, p2); + } +} + +void +Slice::Gen::TypeScriptRequireVisitor::addImport(const ContainedPtr& definition, const ContainedPtr& toplevel) +{ + const string m1 = getModuleMetadata(definition); + const string m2 = getModuleMetadata(toplevel); + + const string p1 = definition->definitionContext()->filename(); + const string p2 = toplevel->definitionContext()->filename(); + + addImport(m1, m2, p1, p2); +} + +void +Slice::Gen::TypeScriptRequireVisitor::addImport(const string& m1, const string& m2, + const string& p1, const string& p2) +{ + // + // Generate an import for a definition that is outside a JS module and comes from + // a different definition context or for a definition defined in a module different + // than the current module. + // + if(m1.empty()) + { + if(p1 != p2) + { + string relpath = relativePath(p1, p2); + + string::size_type pos = relpath.rfind('.'); + if(pos != string::npos) + { + relpath.erase(pos); + } + + for(vector<pair<string, string>>::const_iterator i = _imports.begin(); i != _imports.end(); ++i) + { + if(i->first == relpath) + { + return; + } + } + _imports.push_back(make_pair(relpath, nextImportPrefix())); + } + } + else if(m1 != m2) + { + for(vector<pair<string, string>>::const_iterator i = _imports.begin(); i != _imports.end(); ++i) + { + if(i->first == m1) + { + return; + } + } + _imports.push_back(make_pair(m1, nextImportPrefix())); + } +} + +bool +Slice::Gen::TypeScriptRequireVisitor::visitModuleStart(const ModulePtr& p) +{ + // + // Import ice module if not building Ice + // + if(UnitPtr::dynamicCast(p->container())) + { + const string prefix = "js:module:"; + string m; + findMetaData(prefix, p->getMetaData(), m); + if(_imports.empty() && m != "ice") + { + _imports.push_back(make_pair("ice", nextImportPrefix())); + } + } + return true; +} + +bool +Slice::Gen::TypeScriptRequireVisitor::visitClassDefStart(const ClassDefPtr& p) +{ + // + // Add imports required for base classes + // + ClassList bases = p->bases(); + for(ClassList::const_iterator i = bases.begin(); i != bases.end(); ++i) + { + addImport(ContainedPtr::dynamicCast(*i), p); + } + + // + // Add imports required for data members + // + const DataMemberList allDataMembers = p->allDataMembers(); + for(DataMemberList::const_iterator i = allDataMembers.begin(); i != allDataMembers.end(); ++i) + { + addImport((*i)->type(), p); + } + + // + // Add imports required for operation parameters and return type + // + const OperationList operationList = p->allOperations(); + for(OperationList::const_iterator i = operationList.begin(); i != operationList.end(); ++i) + { + const TypePtr ret = (*i)->returnType(); + if(ret && ret->definitionContext()) + { + addImport(ret, p); + } + + const ParamDeclList paramList = (*i)->parameters(); + for(ParamDeclList::const_iterator j = paramList.begin(); j != paramList.end(); ++j) + { + addImport((*j)->type(), p); + } + } + return false; +} + +bool +Slice::Gen::TypeScriptRequireVisitor::visitStructStart(const StructPtr& p) +{ + // + // Add imports required for data members + // + const DataMemberList dataMembers = p->dataMembers(); + for(DataMemberList::const_iterator i = dataMembers.begin(); i != dataMembers.end(); ++i) + { + addImport((*i)->type(), p); + } + return false; +} + +bool +Slice::Gen::TypeScriptRequireVisitor::visitExceptionStart(const ExceptionPtr& p) +{ + // + // Add imports required for base exceptions + // + ExceptionPtr base = p->base(); + if(base) + { + addImport(ContainedPtr::dynamicCast(base), p); + } + + // + // Add imports required for data members + // + const DataMemberList allDataMembers = p->allDataMembers(); + for(DataMemberList::const_iterator i = allDataMembers.begin(); i != allDataMembers.end(); ++i) + { + addImport((*i)->type(), p); + } + return false; +} + +void +Slice::Gen::TypeScriptRequireVisitor::visitSequence(const SequencePtr& seq) +{ + // + // Add import required for the sequence element type + // + addImport(seq->type(), seq); +} + +void +Slice::Gen::TypeScriptRequireVisitor::visitDictionary(const DictionaryPtr& dict) +{ + // + // Add imports required for the dictionary key and value types + // + addImport(dict->keyType(), dict); + addImport(dict->valueType(), dict); +} + +void +Slice::Gen::TypeScriptRequireVisitor::writeRequires(const UnitPtr& p) +{ + for(vector<pair<string, string>>::const_iterator i = _imports.begin(); i != _imports.end(); ++i) + { + _out << nl << "import * as " << i->second << " from \"" << i->first << "\""; + } +} + +Slice::Gen::TypeScriptAliasVisitor::TypeScriptAliasVisitor(IceUtilInternal::Output& out) : + JsVisitor(out) +{ +} + +void +Slice::Gen::TypeScriptAliasVisitor::addAlias(const ExceptionPtr& type, const ContainedPtr& toplevel) +{ + string m1 = getModuleMetadata(ContainedPtr::dynamicCast(type)); + string m2 = getModuleMetadata(toplevel); + + // + // Do not add alias for a type defined in the current module + // + if(!m1.empty() && m1 == m2) + { + return; + } + + const string prefix = importPrefix(ContainedPtr::dynamicCast(type), toplevel, imports()); + const string typeS = prefix + getUnqualified(fixId(type->scoped()), toplevel->scope(), prefix); + + addAlias(typeS, prefix, toplevel); +} + +void +Slice::Gen::TypeScriptAliasVisitor::addAlias(const TypePtr& type, const ContainedPtr& toplevel) +{ + string m1 = getModuleMetadata(type); + string m2 = getModuleMetadata(toplevel); + + // + // Do not add alias for a type defined in the current module + // + if(!m1.empty() && m1 == m2) + { + return; + } + + addAlias(typeToString(type, toplevel, imports(), true), + importPrefix(type, toplevel, imports()), + toplevel); +} + +void +Slice::Gen::TypeScriptAliasVisitor::addAlias(const string& type, const string& prefix, + const ContainedPtr& toplevel) +{ + const string scope = fixId(toplevel->scoped()) + "."; + // + // When using an import prefix we don't need an alias, prefixes use iceNSXX that is reserved + // name prefix + // + string::size_type i = type.find("."); + if(prefix.empty() && i != string::npos) + { + if(scope.find("." + type.substr(0, i + 1)) != string::npos) + { + for(vector<pair<string, string>>::const_iterator j = _aliases.begin(); j != _aliases.end(); ++j) + { + if(j->first == type) + { + return; + } + } + string alias = type; + replace(alias.begin(), alias.end(), '.', '_'); + // + // We prefix alias with iceA this avoid conflict with iceNSX used for + // import prefixes + // + _aliases.push_back(make_pair(type, "iceA_" + alias)); + } + } +} + +bool +Slice::Gen::TypeScriptAliasVisitor::visitModuleStart(const ModulePtr& p) +{ + return true; +} + +bool +Slice::Gen::TypeScriptAliasVisitor::visitClassDefStart(const ClassDefPtr& p) +{ + ModulePtr module = ModulePtr::dynamicCast(p->container()); + // + // Add alias required for base classes + // + ClassList bases = p->bases(); + for(ClassList::const_iterator i = bases.begin(); i != bases.end(); ++i) + { + addAlias(TypePtr::dynamicCast((*i)->declaration()), module); + } + + // + // Add alias required for data members + // + const DataMemberList allDataMembers = p->allDataMembers(); + for(DataMemberList::const_iterator i = allDataMembers.begin(); i != allDataMembers.end(); ++i) + { + addAlias((*i)->type(), module); + } + + // + // Add alias required for operation parameters + // + const OperationList operationList = p->allOperations(); + for(OperationList::const_iterator i = operationList.begin(); i != operationList.end(); ++i) + { + const TypePtr ret = (*i)->returnType(); + if(ret && ret->definitionContext()) + { + addAlias(ret, module); + } + + const ParamDeclList paramList = (*i)->parameters(); + for(ParamDeclList::const_iterator j = paramList.begin(); j != paramList.end(); ++j) + { + addAlias((*j)->type(), module); + } + } + return false; +} + +bool +Slice::Gen::TypeScriptAliasVisitor::visitStructStart(const StructPtr& p) +{ + ModulePtr module = ModulePtr::dynamicCast(p->container()); + // + // Add alias required for data members + // + const DataMemberList dataMembers = p->dataMembers(); + for(DataMemberList::const_iterator i = dataMembers.begin(); i != dataMembers.end(); ++i) + { + addAlias((*i)->type(), module); + } + return false; +} + +bool +Slice::Gen::TypeScriptAliasVisitor::visitExceptionStart(const ExceptionPtr& p) +{ + ModulePtr module = ModulePtr::dynamicCast(p->container()); + // + // Add alias required for base exception + // + ExceptionPtr base = p->base(); + if(base) + { + addAlias(base, module); + } + + // + // Add alias required for data members + // + const DataMemberList allDataMembers = p->allDataMembers(); + for(DataMemberList::const_iterator i = allDataMembers.begin(); i != allDataMembers.end(); ++i) + { + addAlias((*i)->type(), module); + } + return false; +} + +void +Slice::Gen::TypeScriptAliasVisitor::visitSequence(const SequencePtr& seq) +{ + addAlias(seq->type(), ModulePtr::dynamicCast(seq->container())); +} + +void +Slice::Gen::TypeScriptAliasVisitor::visitDictionary(const DictionaryPtr& dict) +{ + ModulePtr module = ModulePtr::dynamicCast(dict->container()); + addAlias(dict->keyType(), module); + addAlias(dict->valueType(), module); +} + +void +Slice::Gen::TypeScriptAliasVisitor::writeAlias(const UnitPtr& p) +{ + if(!_aliases.empty()) + { + _out << sp; + for(vector<pair<string, string>>::const_iterator i = _aliases.begin(); i != _aliases.end(); ++i) + { + _out << nl << "type " << i->second << " = " << i->first << ";"; + } + } +} + +Slice::Gen::TypeScriptVisitor::TypeScriptVisitor(::IceUtilInternal::Output& out, + const vector<pair<string, string>>& imports) : + JsVisitor(out, imports) +{ +} + +bool +Slice::Gen::TypeScriptVisitor::visitModuleStart(const ModulePtr& p) +{ + UnitPtr unit = UnitPtr::dynamicCast(p->container()); + if(unit) + { + string module = getModuleMetadata(ContainedPtr::dynamicCast(p)); + + _out << sp; + if(module.empty()) + { + _out << nl << "export namespace " << fixId(p->name()) << sb; + } + else + { + _out << nl << "declare module \"" << fixId(module) << "\"" << sb; + _out << nl << "namespace " << fixId(p->name()) << sb; + } + } + else + { + _out << nl << "namespace " << fixId(p->name()) << sb; + } + return true; +} + +void +Slice::Gen::TypeScriptVisitor::visitModuleEnd(const ModulePtr& p) +{ + _out << eb; // namespace end + + if(UnitPtr::dynamicCast(p->container())) + { + string module = getModuleMetadata(ContainedPtr::dynamicCast(p)); + if(!module.empty()) + { + _out << eb; // module end + } + } +} + +bool +Slice::Gen::TypeScriptVisitor::visitClassDefStart(const ClassDefPtr& p) +{ + const string toplevelModule = getModuleMetadata(ContainedPtr::dynamicCast(p)); + const string icePrefix = importPrefix("Ice.", p); + if(p->isDelegate()) + { + // A delegate only has one operation + OperationPtr op = p->allOperations().front(); + CommentPtr comment = op->parseComment(false); + if(comment) + { + writeOpDocSummary(_out, op, comment, OpDocAllParams, true, StringList(), StringList(), comment->returns()); + } + _out << nl << "type " << fixId(p->name()) << " = " << spar; + ParamDeclList paramList = op->parameters(); + for(ParamDeclList::iterator q = paramList.begin(); q != paramList.end(); ++q) + { + ostringstream os; + os << fixId((*q)->name()) << ":"; + if((*q)->isOutParam()) + { + os << icePrefix << getUnqualified("Ice.Holder", p->scope(), icePrefix) << "<"; + } + os << typeToString((*q)->type(), p, imports(), true, false, true); + if((*q)->isOutParam()) + { + os << ">"; + } + _out << os.str(); + } + _out << epar << " => "; + TypePtr ret = op->returnType(); + if(ret) + { + _out << typeToString(ret, p, imports(), true, false, true) << ";"; + } + else + { + _out << "void;"; + } + } + else if(p->isLocal()) + { + const ClassList bases = p->bases(); + const DataMemberList dataMembers = p->dataMembers(); + const DataMemberList allDataMembers = p->allDataMembers(); + + _out << sp; + writeDocSummary(_out, p); + _out << nl; + _out << (p->isInterface() ? "interface" : "class") << " " << fixId(p->name()); + if(!bases.empty() && !bases.front()->isInterface()) + { + const string prefix = importPrefix(ContainedPtr::dynamicCast(bases.front()), p, imports()); + _out << " extends " << prefix << getUnqualified(fixId(bases.front()->scoped()), p->scope(), prefix); + } + _out << sb; + if(!p->isInterface()) + { + _out << nl << "/**"; + _out << nl << " * One-shot constructor to initialize all data members."; + for(DataMemberList::const_iterator q = allDataMembers.begin(); q != allDataMembers.end(); ++q) + { + CommentPtr comment = (*q)->parseComment(false); + if(comment) + { + _out << nl << " * @param " << fixId((*q)->name()) << " " << getDocSentence(comment->overview()); + } + } + _out << nl << " */"; + _out << nl << "constructor" << spar; + for(DataMemberList::const_iterator q = allDataMembers.begin(); q != allDataMembers.end(); ++q) + { + _out << (fixId((*q)->name()) + "?:" + typeToString((*q)->type(), p, imports(), true, false, true)); + } + _out << epar << ";"; + + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + writeDocSummary(_out, *q); + _out << nl << fixId((*q)->name()) << ":" << typeToString((*q)->type(), p, imports(), true, false, true) + << ";"; + } + } + + OperationList allOperations = p->allOperations(); + for(OperationList::const_iterator q = allOperations.begin(); q != allOperations.end(); ++q) + { + OperationPtr op = *q; + ParamDeclList params = op->parameters(); + ParamDeclList inParams, outParams; + for(ParamDeclList::const_iterator r = params.begin(); r != params.end(); ++r) + { + if((*r)->isOutParam()) + { + outParams.push_back(*r); + } + else + { + inParams.push_back(*r); + } + } + + TypePtr ret = op->returnType(); + bool async = op->hasMetaData("js:async") || op->hasMetaData("async-oneway"); + CommentPtr comment = op->parseComment(false); + if(comment) + { + StringList returns; + if(async) + { + returns.push_back("@returns The asynchronous result object for the invocation."); + } + else if(ret) + { + returns = comment->returns(); + } + writeOpDocSummary(_out, op, comment, OpDocAllParams, true, StringList(), StringList(), returns); + } + + _out << nl << fixId((*q)->name()) << spar; + for(ParamDeclList::const_iterator r = inParams.begin(); r != inParams.end(); r++) + { + _out << (fixId((*r)->name()) + ":" + typeToString((*r)->type(), p, imports(), true, false, true)); + } + + for(ParamDeclList::const_iterator r = outParams.begin(); r != outParams.end(); r++) + { + const string prefix = importPrefix("Ice.Holder", p); + _out << (fixId((*r)->name()) + ":" + prefix + + getUnqualified("Ice.Holder", p->scope(), prefix) + "<" + + typeToString((*r)->type(), p, imports(), true, false, true) + ">"); + } + + _out << epar; + + _out << ":"; + if(async) + { + _out << icePrefix << getUnqualified("Ice.AsyncResultBase", p->scope(), icePrefix) << "<"; + } + + if(ret) + { + _out << typeToString(ret, p, imports(), true, false, true); + } + else + { + _out << "void"; + } + + if (async) + { + _out << ">"; + } + _out << ";"; + } + + if(p->hasMetaData("js:comparable")) + { + _out << nl << "equals(rhs:any):boolean"; + } + _out << eb; + } + else + { + // + // Define servant an proxy types for non local classes + // + _out << sp; + _out << nl << "abstract class " << fixId(p->name() + "Prx") + << " extends " << icePrefix << getUnqualified("Ice.ObjectPrx", p->scope(), icePrefix); + _out << sb; + const OperationList ops = p->allOperations(); + for(OperationList::const_iterator q = ops.begin(); q != ops.end(); ++q) + { + const OperationPtr op = *q; + const ParamDeclList paramList = op->parameters(); + const TypePtr ret = op->returnType(); + ParamDeclList inParams, outParams; + for(ParamDeclList::const_iterator r = paramList.begin(); r != paramList.end(); ++r) + { + if((*r)->isOutParam()) + { + outParams.push_back(*r); + } + else + { + inParams.push_back(*r); + } + } + + const string contextParam = escapeParam(paramList, "context"); + CommentPtr comment = op->parseComment(false); + const string contextDoc = "@param " + contextParam + " The Context map to send with the invocation."; + const string asyncDoc = "The asynchronous result object for the invocation."; + if(comment) + { + StringList postParams, returns; + postParams.push_back(contextDoc); + returns.push_back(asyncDoc); + writeOpDocSummary(_out, op, comment, OpDocInParams, false, StringList(), postParams, returns); + } + _out << nl << fixId((*q)->name()) << spar; + for(ParamDeclList::const_iterator r = inParams.begin(); r != inParams.end(); ++r) + { + _out << (fixId((*r)->name()) + + ((*r)->optional() ? "?" : "") + + ":" + + typeToString((*r)->type(), p, imports(), true, false, true)); + } + _out << "context?:Map<string, string>"; + _out << epar; + + _out << ":" << icePrefix << getUnqualified("Ice.AsyncResult", p->scope(), icePrefix); + if(!ret && outParams.empty()) + { + _out << "<void>"; + } + else if((ret && outParams.empty()) || (!ret && outParams.size() == 1)) + { + TypePtr t = ret ? ret : outParams.front()->type(); + _out << "<" << typeToString(t, p, imports(), true, false, true) << ">"; + } + else + { + _out << "<["; + if(ret) + { + _out << typeToString(ret, p, imports(), true, false, true) << ", "; + } + + for(ParamDeclList::const_iterator i = outParams.begin(); i != outParams.end();) + { + _out << typeToString((*i)->type(), p, imports(), true, false, true); + if(++i != outParams.end()) + { + _out << ", "; + } + } + + _out << "]>"; + } + + _out << ";"; + } + + const string icePrefix = importPrefix("Ice.ObjectPrx", p); + _out << sp; + _out << nl << "/**"; + _out << nl << " * Downcasts a proxy without confirming the target object's type via a remote invocation."; + _out << nl << " * @param prx The target proxy."; + _out << nl << " * @return A proxy with the requested type."; + _out << nl << " */"; + _out << nl << "static uncheckedCast(prx:" << icePrefix + << getUnqualified("Ice.ObjectPrx", p->scope(), icePrefix) << ", " + << "facet?:string):" + << fixId(p->name() + "Prx") << ";"; + _out << nl << "/**"; + _out << nl << " * Downcasts a proxy after confirming the target object's type via a remote invocation."; + _out << nl << " * @param prx The target proxy."; + _out << nl << " * @param facet A facet name."; + _out << nl << " * @param context The context map for the invocation."; + _out << nl << " * @return A proxy with the requested type and facet, or nil if the target proxy is nil or the target"; + _out << nl << " * object does not support the requested type."; + _out << nl << " */"; + _out << nl << "static checkedCast(prx:" << icePrefix + << getUnqualified("Ice.ObjectPrx", p->scope(), icePrefix) << ", " + << "facet?:string, contex?:Map<string, string>):" << icePrefix + << getUnqualified("Ice.AsyncResult", p->scope(), icePrefix) << "<" << fixId(p->name() + "Prx") << ">;"; + _out << eb; + + if(p->isInterface() || !ops.empty()) + { + _out << sp; + _out << nl << "abstract class " << fixId(p->name() + (p->isInterface() ? "" : "Disp")) + << " extends " << icePrefix << getUnqualified("Ice.Object", p->scope(), icePrefix); + _out << sb; + for(OperationList::const_iterator q = ops.begin(); q != ops.end(); ++q) + { + const OperationPtr op = *q; + const ParamDeclList paramList = op->parameters(); + const TypePtr ret = op->returnType(); + ParamDeclList inParams, outParams; + for(ParamDeclList::const_iterator r = paramList.begin(); r != paramList.end(); ++r) + { + if((*r)->isOutParam()) + { + outParams.push_back(*r); + } + else + { + inParams.push_back(*r); + } + } + + const string currentParam = escapeParam(inParams, "current"); + CommentPtr comment = p->parseComment(false); + const string currentDoc = "@param " + currentParam + " The Current object for the invocation."; + const string resultDoc = "The result or a promise like object that will " + "be resolved with the result of the invocation."; + if(comment) + { + StringList postParams, returns; + postParams.push_back(currentDoc); + returns.push_back(resultDoc); + writeOpDocSummary(_out, op, comment, OpDocInParams, false, StringList(), postParams, returns); + } + _out << nl << "abstract " << fixId((*q)->name()) << spar; + for(ParamDeclList::const_iterator r = inParams.begin(); r != inParams.end(); ++r) + { + _out << (fixId((*r)->name()) + ":" + typeToString((*r)->type(), p, imports(), true, false, true)); + } + _out << ("current:" + icePrefix + getUnqualified("Ice.Current", p->scope(), icePrefix)); + _out << epar << ":"; + + if(!ret && outParams.empty()) + { + _out << "PromiseLike<void>|void"; + } + else if((ret && outParams.empty()) || (!ret && outParams.size() == 1)) + { + TypePtr t = ret ? ret : outParams.front()->type(); + string returnType = typeToString(t, p, imports(), true, false, true); + _out << "PromiseLike<" << returnType << ">|" << returnType; + } + else + { + ostringstream os; + if(ret) + { + os << typeToString(ret, p, imports(), true, false, true) << ", "; + } + + for(ParamDeclList::const_iterator i = outParams.begin(); i != outParams.end();) + { + os << typeToString((*i)->type(), p, imports(), true, false, true); + if(++i != outParams.end()) + { + os << ", "; + } + } + _out << "PromiseLike<[" << os.str() << "]>|[" << os.str() << "]"; + } + _out << ";"; + } + _out << nl << "/**"; + _out << nl << " * Obtains the Slice type ID of this type."; + _out << nl << " * @return The return value is always \"" + p->scoped() + "\"."; + _out << nl << " */"; + _out << nl << "static ice_staticId():string;"; + _out << eb; + } + + if(!p->isInterface()) + { + const DataMemberList dataMembers = p->dataMembers(); + const DataMemberList allDataMembers = p->allDataMembers(); + _out << sp; + writeDocSummary(_out, p); + _out << nl << "class " << fixId(p->name()) << " extends "; + const string scope = p->scope(); + const string scoped = p->scoped(); + ClassList bases = p->bases(); + if(!bases.empty() && !bases.front()->isInterface()) + { + ClassDefPtr base = bases.front(); + const string prefix = importPrefix(ContainedPtr::dynamicCast(base), p, imports()); + _out << prefix << getUnqualified(fixId(base->scoped()), p->scope(), prefix); + } + else + { + _out << icePrefix << getUnqualified("Ice.Value", p->scope(), icePrefix); + } + _out << sb; + _out << nl << "/**"; + _out << nl << " * One-shot constructor to initialize all data members."; + for(DataMemberList::const_iterator q = allDataMembers.begin(); q != allDataMembers.end(); ++q) + { + CommentPtr comment = (*q)->parseComment(false); + if(comment) + { + _out << nl << " * @param " << fixId((*q)->name()) << " " << getDocSentence(comment->overview()); + } + } + _out << nl << " */"; + _out << nl << "constructor" << spar; + for(DataMemberList::const_iterator q = allDataMembers.begin(); q != allDataMembers.end(); ++q) + { + _out << (fixId((*q)->name()) + "?:" + typeToString((*q)->type(), p, imports(), true, false, true)); + } + _out << epar << ";"; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + writeDocSummary(_out, *q); + _out << nl << fixId((*q)->name()) << ":" << typeToString((*q)->type(), p, imports(), true, false, true) + << ";"; + } + _out << eb; + } + } + return false; +} + +bool +Slice::Gen::TypeScriptVisitor::visitExceptionStart(const ExceptionPtr& p) +{ + const string name = fixId(p->name()); + const DataMemberList dataMembers = p->dataMembers(); + const DataMemberList allDataMembers = p->allDataMembers(); + const string toplevelModule = getModuleMetadata(ContainedPtr::dynamicCast(p)); + const string icePrefix = importPrefix("Ice.", p); + + ExceptionPtr base = p->base(); + string baseRef; + if(base) + { + const string prefix = importPrefix(ContainedPtr::dynamicCast(base), p, imports()); + baseRef = prefix + getUnqualified(fixId(base->scoped()), p->scope(), prefix); + } + else + { + baseRef = p->isLocal() ? + icePrefix + getUnqualified("Ice.LocalException", p->scope(), icePrefix) : + icePrefix + getUnqualified("Ice.UserException", p->scope(), icePrefix); + } + + _out << sp; + writeDocSummary(_out, p); + _out << nl << "class " << name << " extends " << baseRef << sb; + if(!allDataMembers.empty()) + { + _out << nl << "/**"; + _out << nl << " * One-shot constructor to initialize all data members."; + for(DataMemberList::const_iterator q = allDataMembers.begin(); q != allDataMembers.end(); ++q) + { + CommentPtr comment = (*q)->parseComment(false); + if(comment) + { + _out << nl << " * @param " << fixId((*q)->name()) << " " << getDocSentence(comment->overview()); + } + } + _out << nl << " */"; + _out << nl << "constructor" << spar; + for(DataMemberList::const_iterator q = allDataMembers.begin(); q != allDataMembers.end(); ++q) + { + _out << (fixId((*q)->name()) + "?:" + typeToString((*q)->type(), p, imports(), true, false, true)); + } + _out << epar << ";"; + } + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + _out << nl << fixId((*q)->name()) << ":" << typeToString((*q)->type(), p, imports(), true, false, true) << ";"; + } + _out << eb; + return false; +} + +bool +Slice::Gen::TypeScriptVisitor::visitStructStart(const StructPtr& p) +{ + const string icePrefix = importPrefix("Ice.", p); + const string name = fixId(p->name()); + const DataMemberList dataMembers = p->dataMembers(); + const string toplevelModule = getModuleMetadata(ContainedPtr::dynamicCast(p)); + _out << sp; + writeDocSummary(_out, p); + _out << nl << "class " << name << sb; + _out << nl << "constructor" << spar; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + _out << (fixId((*q)->name()) + "?:" + typeToString((*q)->type(), p, imports(), true, false, true)); + } + _out << epar << ";"; + + _out << nl << "clone():" << name << ";"; + _out << nl << "equals(rhs:any):boolean;"; + + // + // Only generate hashCode if this structure type is a legal dictionary key type. + // + bool containsSequence = false; + bool legalKeyType = Dictionary::legalKeyType(p, containsSequence); + if(legalKeyType) + { + _out << nl << "hashCode():number;"; + } + + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + _out << nl << fixId((*q)->name()) << ":" << typeToString((*q)->type(), p, imports(), true, false, true) << ";"; + } + + // + // Streaming API + // + _out << nl << "static write(outs:" << icePrefix << getUnqualified("Ice.OutputStream", p->scope(), icePrefix) + << ", value:" << name << "):void;"; + _out << nl << "static read(ins:" << icePrefix << getUnqualified("Ice.InputStream", p->scope(), icePrefix) << "):" + << name << ";"; + _out << eb; + return false; +} + +void +Slice::Gen::TypeScriptVisitor::visitSequence(const SequencePtr& p) +{ + const string icePrefix = importPrefix("Ice.", p); + const string toplevelModule = getModuleMetadata(ContainedPtr::dynamicCast(p)); + const string name = fixId(p->name()); + _out << sp; + writeDocSummary(_out, p); + _out << nl << "type " << name << " = " << typeToString(p, p, imports(), true, true) << ";"; + + _out << sp; + _out << nl << "class " << fixId(p->name() + "Helper"); + _out << sb; + // + // Streaming API + // + _out << nl << "static write(outs:" << icePrefix << getUnqualified("Ice.OutputStream", p->scope(), icePrefix) + << ", value:" << name << "):void;"; + _out << nl << "static read(ins:" << icePrefix << getUnqualified("Ice.InputStream", p->scope(), icePrefix) << "):" + << name << ";"; + _out << eb; +} + +void +Slice::Gen::TypeScriptVisitor::visitDictionary(const DictionaryPtr& p) +{ + const string icePrefix = importPrefix("Ice.", p); + const string toplevelModule = getModuleMetadata(ContainedPtr::dynamicCast(p)); + const string name = fixId(p->name()); + _out << sp; + writeDocSummary(_out, p); + _out << nl << "class " << name << " extends " << typeToString(p, p, imports(), true, true); + _out << sb; + _out << eb; + + _out << sp; + _out << nl << "class " << fixId(p->name() + "Helper"); + _out << sb; + // + // Streaming API + // + _out << nl << "static write(outs:" << icePrefix << getUnqualified("Ice.OutputStream", p->scope(), icePrefix) + << ", value:" << name << "):void;"; + _out << nl << "static read(ins:" << icePrefix << getUnqualified("Ice.InputStream", p->scope(), icePrefix) << "):" + << name << ";"; + _out << eb; +} + +void +Slice::Gen::TypeScriptVisitor::visitEnum(const EnumPtr& p) +{ + _out << sp; + writeDocSummary(_out, p); + _out << nl << "class " << fixId(p->name()); + _out << sb; + const EnumeratorList enumerators = p->enumerators(); + for(EnumeratorList::const_iterator en = enumerators.begin(); en != enumerators.end(); ++en) + { + writeDocSummary(_out, *en); + _out << nl << "static readonly " << fixId((*en)->name()) << ":" << fixId(p->name()) << ";"; + } + _out << nl; + _out << nl << "static valueOf(value:number):" << fixId(p->name()) << ";"; + _out << nl << "equals(other:any):boolean;"; + _out << nl << "hashCode():number;"; + _out << nl << "toString():string;"; + _out << nl; + _out << nl << "readonly name:string;"; + _out << nl << "readonly value:number;"; + _out << eb; +} + +void +Slice::Gen::TypeScriptVisitor::visitConst(const ConstPtr& p) +{ + const string toplevelModule = getModuleMetadata(p->type()); + _out << sp; + writeDocSummary(_out, p); + _out << nl << "const " << fixId(p->name()) << ":" << typeToString(p->type(), p, imports(), true) << ";"; +} diff --git a/cpp/src/slice2js/Gen.h b/cpp/src/slice2js/Gen.h index a778ba65fd4..f5bc6216a52 100644 --- a/cpp/src/slice2js/Gen.h +++ b/cpp/src/slice2js/Gen.h @@ -19,14 +19,18 @@ class JsVisitor : public JsGenerator, public ParserVisitor { public: - JsVisitor(::IceUtilInternal::Output&); + JsVisitor(::IceUtilInternal::Output&, + const std::vector<std::pair<std::string, std::string>>& imports = + std::vector<std::pair<std::string, std::string>>()); virtual ~JsVisitor(); + std::vector<std::pair<std::string, std::string>> imports() const; + protected: void writeMarshalDataMembers(const DataMemberList&, const DataMemberList&); void writeUnmarshalDataMembers(const DataMemberList&, const DataMemberList&); - void writeInitDataMembers(const DataMemberList&, const std::string&); + void writeInitDataMembers(const DataMemberList&); std::string getValue(const std::string&, const TypePtr&); @@ -36,6 +40,8 @@ protected: void writeDocComment(const ContainedPtr&, const std::string&, const std::string& = ""); ::IceUtilInternal::Output& _out; + + std::vector<std::pair<std::string, std::string>> _imports; }; class Gen : public JsGenerator @@ -44,11 +50,13 @@ public: Gen(const std::string&, const std::vector<std::string>&, - const std::string&); + const std::string&, + bool); Gen(const std::string&, const std::vector<std::string>&, const std::string&, + bool, std::ostream&); ~Gen(); @@ -58,14 +66,14 @@ public: private: - std::ofstream _stdout; IceUtilInternal::Output _out; std::vector<std::string> _includePaths; std::string _fileBase; bool _useStdout; - - void printHeader(); + bool _typeScript; + bool _buildModule; + bool _noModule; class RequireVisitor : public JsVisitor { @@ -95,7 +103,9 @@ private: bool _seenLocalException; bool _seenEnum; bool _seenObjectSeq; + bool _seenObjectProxySeq; bool _seenObjectDict; + bool _seenObjectProxyDict; std::vector<std::string> _includePaths; }; @@ -103,7 +113,7 @@ private: { public: - TypesVisitor(::IceUtilInternal::Output&, std::vector< std::string>, bool); + TypesVisitor(::IceUtilInternal::Output&, std::vector<std::string>, bool); virtual bool visitModuleStart(const ModulePtr&); virtual void visitModuleEnd(const ModulePtr&); @@ -130,12 +140,81 @@ private: ExportVisitor(::IceUtilInternal::Output&, bool, bool); virtual bool visitModuleStart(const ModulePtr&); + private: bool _icejs; bool _es6modules; std::vector<std::string> _exported; }; + + class TypeScriptRequireVisitor : public JsVisitor + { + public: + + TypeScriptRequireVisitor(::IceUtilInternal::Output&); + + virtual bool visitModuleStart(const ModulePtr&); + virtual bool visitClassDefStart(const ClassDefPtr&); + virtual bool visitStructStart(const StructPtr&); + virtual bool visitExceptionStart(const ExceptionPtr&); + virtual void visitSequence(const SequencePtr&); + virtual void visitDictionary(const DictionaryPtr&); + + void writeRequires(const UnitPtr&); + + private: + + void addImport(const TypePtr&, const ContainedPtr&); + void addImport(const ContainedPtr&, const ContainedPtr&); + void addImport(const std::string&, const std::string&, const std::string&, const std::string&); + + std::string nextImportPrefix(); + + int _nextImport; + std::map<std::string, std::string> _modulePrefix; + }; + + class TypeScriptAliasVisitor : public JsVisitor + { + public: + + TypeScriptAliasVisitor(::IceUtilInternal::Output&); + + virtual bool visitModuleStart(const ModulePtr&); + virtual bool visitClassDefStart(const ClassDefPtr&); + virtual bool visitStructStart(const StructPtr&); + virtual bool visitExceptionStart(const ExceptionPtr&); + virtual void visitSequence(const SequencePtr&); + virtual void visitDictionary(const DictionaryPtr&); + + void writeAlias(const UnitPtr&); + + private: + + void addAlias(const ExceptionPtr&, const ContainedPtr&); + void addAlias(const TypePtr&, const ContainedPtr&); + void addAlias(const std::string&, const std::string&, const ContainedPtr&); + std::vector<std::pair<std::string, std::string>> _aliases; + }; + + class TypeScriptVisitor : public JsVisitor + { + public: + + TypeScriptVisitor(::IceUtilInternal::Output&, + const std::vector<std::pair<std::string, std::string>>&); + + virtual bool visitModuleStart(const ModulePtr&); + virtual void visitModuleEnd(const ModulePtr&); + 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&); + }; }; } diff --git a/cpp/src/slice2js/JsUtil.cpp b/cpp/src/slice2js/JsUtil.cpp index b33dfacb9d6..ec262b7c648 100644 --- a/cpp/src/slice2js/JsUtil.cpp +++ b/cpp/src/slice2js/JsUtil.cpp @@ -10,6 +10,7 @@ #include <JsUtil.h> #include <Slice/Util.h> #include <IceUtil/Functional.h> +#include <IceUtil/StringUtil.h> #include <sys/types.h> #include <sys/stat.h> @@ -27,6 +28,64 @@ using namespace Slice; using namespace IceUtil; using namespace IceUtilInternal; +string +Slice::relativePath(const string& p1, const string& p2) +{ + vector<string> tokens1; + vector<string> tokens2; + + splitString(p1, "/\\", tokens1); + splitString(p2, "/\\", tokens2); + + string f1 = tokens1.back(); + string f2 = tokens2.back(); + + tokens1.pop_back(); + tokens2.pop_back(); + + vector<string>::const_iterator i1 = tokens1.begin(); + vector<string>::const_iterator i2 = tokens2.begin(); + + while(i1 != tokens1.end() && i2 != tokens2.end() && *i1 == *i2) + { + i1++; + i2++; + } + + // + // Different volumes, relative path not possible. + // + if(i1 == tokens1.begin() && i2 == tokens2.begin()) + { + return p1; + } + + string newPath; + if(i2 == tokens2.end()) + { + newPath += "./"; + for (; i1 != tokens1.end(); ++i1) + { + newPath += *i1 + "/"; + } + } + else + { + for(size_t i = tokens2.end() - i2; i > 0; i--) + { + newPath += "../"; + } + + for(; i1 != tokens1.end(); ++i1) + { + newPath += *i1 + "/"; + } + } + newPath += f1; + + return newPath; +} + static string lookupKwd(const string& name) { @@ -99,11 +158,71 @@ fixIds(const StringList& ids) return newIds; } +string +Slice::JsGenerator::getModuleMetadata(const TypePtr& type) +{ + static const char* builtinModuleTable[] = + { + "", // byte + "", // bool + "", // short + "", // int + "ice", // long + "", // float + "", // double + "", // string + "ice", // Ice.Value + "ice", // Ice.ObjectPrx + "", // LocalObject + "ice" // Ice.Object + }; + + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + return builtinModuleTable[builtin->kind()]; + } + + ProxyPtr proxy = ProxyPtr::dynamicCast(type); + return getModuleMetadata(proxy ? ContainedPtr::dynamicCast(proxy->_class()->definition()) : + ContainedPtr::dynamicCast(type)); +} + +string +Slice::JsGenerator::getModuleMetadata(const ContainedPtr& cont) +{ + // + // Traverse to the top-level module. + // + ModulePtr m; + ContainedPtr p = cont; + while (true) + { + if(ModulePtr::dynamicCast(p)) + { + m = ModulePtr::dynamicCast(p); + } + + ContainerPtr c = p->container(); + p = ContainedPtr::dynamicCast(c); // This cast fails for Unit. + if(!p) + { + break; + } + } + + const string prefix = "js:module:"; + string value; + findMetaData(prefix, m->getMetaData(), value); + return value; +} + bool Slice::JsGenerator::isClassType(const TypePtr& type) { BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); - return (builtin && (builtin->kind() == Builtin::KindObject || builtin->kind() == Builtin::KindValue)) || ClassDeclPtr::dynamicCast(type); + return (builtin && (builtin->kind() == Builtin::KindObject || builtin->kind() == Builtin::KindValue)) || + ClassDeclPtr::dynamicCast(type); } // @@ -146,20 +265,182 @@ Slice::JsGenerator::fixId(const ContainedPtr& cont) } string -Slice::JsGenerator::typeToString(const TypePtr& type) +Slice::JsGenerator::importPrefix(const TypePtr& type, + const ContainedPtr& toplevel, + const vector<pair<string, string>>& imports) +{ + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + return typeToString(type, toplevel, imports, true); + } + else if(ProxyPtr::dynamicCast(type)) + { + ProxyPtr proxy = ProxyPtr::dynamicCast(type); + return importPrefix(ContainedPtr::dynamicCast(proxy->_class()->definition()), toplevel, imports); + } + else if(ContainedPtr::dynamicCast(type)) + { + bool local = false; + if(toplevel) + { + if(ConstructedPtr::dynamicCast(toplevel)) + { + local = ConstructedPtr::dynamicCast(toplevel)->isLocal(); + } + else if(ClassDefPtr::dynamicCast(toplevel)) + { + local = ClassDefPtr::dynamicCast(toplevel)->isLocal(); + } + } + + ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type); + if(cl && cl->isInterface() && !local) + { + return "iceNS0."; + } + else + { + return importPrefix(ContainedPtr::dynamicCast(type), toplevel, imports); + } + } + return ""; +} + +string +Slice::JsGenerator::importPrefix(const ContainedPtr& contained, + const ContainedPtr& toplevel, + const vector<pair<string, string>>& imports) +{ + string m1 = getModuleMetadata(contained); + string m2 = getModuleMetadata(toplevel); + + string p; + + if(m1.empty()) + { + string p1 = contained->definitionContext()->filename(); + string p2 = toplevel->definitionContext()->filename(); + + p = relativePath(p1, p2); + + string::size_type pos = p.rfind('.'); + if (pos != string::npos) + { + p.erase(pos); + } + } + else if(m1 == "ice" && m1 != m2) + { + return "iceNS0."; + } + else if(m1 != m2) + { + p = m1; + } + + if(!p.empty()) + { + for (vector<pair<string, string>>::const_iterator i = imports.begin(); i != imports.end(); ++i) + { + if (i->first == p) + { + return i->second + "."; + } + } + } + + return ""; +} + +bool +Slice::JsGenerator::findMetaData(const string& prefix, const StringList& metaData, string& value) +{ + for(StringList::const_iterator i = metaData.begin(); i != metaData.end(); i++) + { + string s = *i; + if(s.find(prefix) == 0) + { + value = s.substr(prefix.size()); + return true; + } + } + return false; +} + +string +Slice::JsGenerator::importPrefix(const string& type, const ContainedPtr& toplevel) +{ + const string module = getModuleMetadata(toplevel); + return (type.find("Ice.") == 0 && module != "ice") ? "iceNS0." : ""; +} + +string +Slice::JsGenerator::getUnqualified(const string& type, const string& scope, const string& importPrefix) +{ + if(importPrefix.empty()) + { + const string localScope = getLocalScope(scope) + "."; + if(type.find(localScope) == 0) + { + string t = type.substr(localScope.size()); + if(t.find(".") == string::npos) + { + return t; + } + } + } + return type; +} + +string +Slice::JsGenerator::typeToString(const TypePtr& type, + const ContainedPtr& toplevel, + const vector<pair<string, string>>& imports, + bool typescript, + bool definition) { if(!type) { return "void"; } - static const char* builtinTable[] = + bool local = false; + if(toplevel) + { + if(ConstructedPtr::dynamicCast(toplevel)) + { + local = ConstructedPtr::dynamicCast(toplevel)->isLocal(); + } + else if(ClassDefPtr::dynamicCast(toplevel)) + { + local = ClassDefPtr::dynamicCast(toplevel)->isLocal(); + } + } + + static const char* typeScriptBuiltinTable[] = + { + "number", // byte + "boolean", // bool + "number", // short + "number", // int + "Ice.Long", // long + "number", // float + "number", // double + "string", + "Ice.Object", + "Ice.ObjectPrx", + "Object", + "Ice.Value" + }; + + static const char* javaScriptBuiltinTable[] = { "Number", // byte "Boolean", // bool "Number", // short "Number", // int - "Number", // long + "Ice.Long", // long "Number", // float "Number", // double "String", @@ -172,39 +453,186 @@ Slice::JsGenerator::typeToString(const TypePtr& type) BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); if(builtin) { - return builtinTable[builtin->kind()]; + if(typescript) + { + int kind = (!local && builtin->kind() == Builtin::KindObject) ? Builtin::KindValue : builtin->kind(); + ostringstream os; + if(getModuleMetadata(type) == "ice" && getModuleMetadata(toplevel) != "ice") + { + os << "iceNS0."; + } + os << getUnqualified(typeScriptBuiltinTable[kind], toplevel->scope(), "iceNS0."); + return os.str(); + } + else + { + return javaScriptBuiltinTable[builtin->kind()]; + } + } + + ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type); + if(cl) + { + string prefix; + ostringstream os; + if(typescript) + { + if(cl->isInterface() && !local) + { + prefix = importPrefix("Ice.Value", toplevel); + } + else + { + prefix = importPrefix(ContainedPtr::dynamicCast(cl), toplevel, imports); + } + } + os << prefix; + if(!prefix.empty() && typescript) + { + if(cl->isInterface() && !local) + { + os << getUnqualified("Ice.Value", toplevel->scope(), prefix); + } + else + { + os << getUnqualified(fixId(cl->scoped()), toplevel->scope(), prefix); + } + } + else + { + os << fixId(cl->scoped()); + } + return os.str(); } ProxyPtr proxy = ProxyPtr::dynamicCast(type); if(proxy) { - return fixId(proxy->_class()->scoped() + "Prx"); - } + ostringstream os; + string prefix; + if(typescript) + { + prefix = importPrefix(ContainedPtr::dynamicCast(proxy->_class()->definition()), toplevel, imports); + os << prefix; + } - SequencePtr seq = SequencePtr::dynamicCast(type); - if(seq) - { - return typeToString(seq->type()) + "[]"; + if(prefix.empty() && typescript) + { + os << getUnqualified(fixId(proxy->_class()->scoped() + "Prx"), toplevel->scope(), prefix); + } + else + { + os << fixId(proxy->_class()->scoped() + "Prx"); + } + return os.str(); } - DictionaryPtr d = DictionaryPtr::dynamicCast(type); - if(d) + if(!typescript || definition) { - const TypePtr keyType = d->keyType(); - BuiltinPtr b = BuiltinPtr::dynamicCast(keyType); - return ((b && b->kind() == Builtin::KindLong) || StructPtr::dynamicCast(keyType)) ? "Ice.HashMap" : "Map"; + SequencePtr seq = SequencePtr::dynamicCast(type); + if (seq) + { + BuiltinPtr b = BuiltinPtr::dynamicCast(seq->type()); + if (b && b->kind() == Builtin::KindByte) + { + return "Uint8Array"; + } + else + { + return typeToString(seq->type(), toplevel, imports, typescript) + "[]"; + } + } + + DictionaryPtr d = DictionaryPtr::dynamicCast(type); + if(d) + { + const TypePtr keyType = d->keyType(); + BuiltinPtr builtin = BuiltinPtr::dynamicCast(keyType); + ostringstream os; + if ((builtin && builtin->kind() == Builtin::KindLong) || StructPtr::dynamicCast(keyType)) + { + const string prefix = importPrefix("Ice.HashMap", toplevel); + os << prefix << getUnqualified("Ice.HashMap", toplevel->scope(), prefix); + } + else + { + os << "Map"; + } + + if (typescript) + { + os << "<" + << typeToString(keyType, toplevel, imports, true) << ", " + << typeToString(d->valueType(), toplevel, imports, true) << ">"; + } + return os.str(); + } } ContainedPtr contained = ContainedPtr::dynamicCast(type); if(contained) { - return fixId(contained->scoped()); + ostringstream os; + string prefix; + if(typescript) + { + prefix = importPrefix(contained, toplevel, imports); + os << prefix; + } + + if(prefix.empty() && typescript) + { + os << getUnqualified(fixId(contained->scoped()), toplevel->scope(), prefix); + } + else + { + os << fixId(contained->scoped()); + } + return os.str(); } return "???"; } string +Slice::JsGenerator::typeToString(const TypePtr& type, + const ContainedPtr& toplevel, + const std::vector<std::pair<std::string, std::string>>& imports, + bool typeScript, + bool definition, + bool usealias) +{ + string t = typeToString(type, toplevel, imports, typeScript, definition); + if(usealias) + { + string m1 = getModuleMetadata(type); + string m2 = getModuleMetadata(toplevel); + if (!m1.empty() && m1 == m2) + { + // we are using the same module + return t; + } + string p = importPrefix(type, toplevel, imports); + + // + // When using an import prefix we don't need an alias, prefixes use iceNSXX that is reserved + // name prefix + // + string::size_type i = t.find("."); + if(p.empty() && i != string::npos) + { + const string scoped = fixId(toplevel->scoped()) + "."; + if(scoped.find("." + t.substr(0, i + 1)) != string::npos) + { + replace(t.begin(), t.end(), '.', '_'); + t = "iceA_" + t; + } + } + } + return t; +} + +string Slice::JsGenerator::getLocalScope(const string& scope, const string& separator) { assert(!scope.empty()); @@ -222,6 +650,7 @@ Slice::JsGenerator::getLocalScope(const string& scope, const string& separator) { fixedScope = scope; } + if(fixedScope.empty()) { return ""; @@ -243,39 +672,6 @@ Slice::JsGenerator::getLocalScope(const string& scope, const string& separator) return result.str(); } -string -Slice::JsGenerator::getReference(const string& scope, const string& target) -{ - // - // scope and target should be fully-qualified symbols. - // - assert(!scope.empty() && scope[0] == ':' && !target.empty() && target[0] == ':'); - - // - // Check whether the target is in the given scope. - // - if(target.find(scope) == 0) - { - // - // Remove scope from target, but keep the leading "::". - // - const string rem = target.substr(scope.size() - 2); - assert(!rem.empty()); - const StringList ids = fixIds(splitScopedName(rem)); - stringstream result; - result << getLocalScope(scope); - for(StringList::const_iterator i = ids.begin(); i != ids.end(); ++i) - { - result << '.' << *i; - } - return result.str(); - } - else - { - return fixId(target); - } -} - void Slice::JsGenerator::writeMarshalUnmarshalCode(Output &out, const TypePtr& type, diff --git a/cpp/src/slice2js/JsUtil.h b/cpp/src/slice2js/JsUtil.h index 69d9ad6e9c6..e56162a698d 100644 --- a/cpp/src/slice2js/JsUtil.h +++ b/cpp/src/slice2js/JsUtil.h @@ -16,20 +16,46 @@ namespace Slice { +std::string relativePath(const std::string&, const std::string&); + class JsGenerator : private ::IceUtil::noncopyable { public: virtual ~JsGenerator() {}; -protected: - static bool isClassType(const TypePtr&); + static std::string getModuleMetadata(const TypePtr&); + static std::string getModuleMetadata(const ContainedPtr&); static std::string fixId(const std::string&); static std::string fixId(const ContainedPtr&); - static std::string typeToString(const TypePtr&); + static bool findMetaData(const std::string&, const StringList&, std::string&); + static std::string importPrefix(const TypePtr&, + const ContainedPtr&, + const std::vector<std::pair<std::string, std::string>>&); + + static std::string importPrefix(const ContainedPtr&, + const ContainedPtr&, + const std::vector<std::pair<std::string, std::string>>&); + static std::string importPrefix(const std::string&, const ContainedPtr&); + + static std::string getUnqualified(const std::string&, const std::string&, const std::string&); + + static std::string typeToString(const TypePtr&, + const ContainedPtr& = 0, + const std::vector<std::pair<std::string, std::string>>& = + std::vector<std::pair<std::string, std::string>>(), + bool typeScript = false, + bool definition = false); + + static std::string typeToString(const TypePtr&, + const ContainedPtr&, + const std::vector<std::pair<std::string, std::string>>&, + bool typeScript, + bool definition, + bool usealias); + static std::string getLocalScope(const std::string&, const std::string& separator = "."); - static std::string getReference(const std::string&, const std::string&); static std::string getHelper(const TypePtr&); // diff --git a/cpp/src/slice2js/Main.cpp b/cpp/src/slice2js/Main.cpp index 46cf1671d48..062abe2083f 100644 --- a/cpp/src/slice2js/Main.cpp +++ b/cpp/src/slice2js/Main.cpp @@ -75,6 +75,7 @@ usage(const string& n) "--depend-file FILE Write dependencies to FILE instead of standard output.\n" "--validate Validate command line options.\n" "--stdout Print generated code to stdout.\n" + "--typescript Generate TypeScript declaration file\n" "--depend-json Generate dependency information in JSON format.\n" "--ice Allow reserved Ice prefix in Slice identifiers\n" " deprecated: use instead [[\"ice-prefix\"]] metadata.\n" @@ -95,6 +96,7 @@ compile(const vector<string>& argv) opts.addOpt("I", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat); opts.addOpt("E"); opts.addOpt("", "stdout"); + opts.addOpt("", "typescript"); opts.addOpt("", "output-dir", IceUtilInternal::Options::NeedArg); opts.addOpt("", "depend"); opts.addOpt("", "depend-json"); @@ -172,6 +174,8 @@ compile(const vector<string>& argv) bool underscore = opts.isSet("underscore"); + bool typeScript = opts.isSet("typescript"); + if(args.empty()) { consoleErr << argv[0] << ": error: no input file" << endl; @@ -247,16 +251,16 @@ compile(const vector<string>& argv) for(vector<string>::const_iterator i = sources.begin(); i != sources.end();) { - if(depend || dependJSON || dependxml) - { - PreprocessorPtr icecpp = Preprocessor::create(argv[0], *i, cppArgs); - FILE* cppHandle = icecpp->preprocess(false, "-D__SLICE2JS__"); + PreprocessorPtr icecpp = Preprocessor::create(argv[0], *i, cppArgs); + FILE* cppHandle = icecpp->preprocess(true, "-D__SLICE2JS__"); - if(cppHandle == 0) - { - return EXIT_FAILURE; - } + if(cppHandle == 0) + { + return EXIT_FAILURE; + } + if(depend || dependJSON || dependxml) + { UnitPtr u = Unit::createUnit(false, false, ice, underscore); int parseStatus = u->parse(*i, cppHandle, debug); u->destroy(); @@ -292,13 +296,6 @@ compile(const vector<string>& argv) } else { - PreprocessorPtr icecpp = Preprocessor::create(argv[0], *i, cppArgs); - FILE* cppHandle = icecpp->preprocess(true, "-D__SLICE2JS__"); - - if(cppHandle == 0) - { - return EXIT_FAILURE; - } if(preprocess) { char buf[4096]; @@ -335,12 +332,12 @@ compile(const vector<string>& argv) { if(useStdout) { - Gen gen(icecpp->getBaseName(), includePaths, output, cout); + Gen gen(icecpp->getBaseName(), includePaths, output, typeScript, cout); gen.generate(p); } else { - Gen gen(icecpp->getBaseName(), includePaths, output); + Gen gen(icecpp->getBaseName(), includePaths, output, typeScript); gen.generate(p); } } |