diff options
author | Mark Spruiell <mes@zeroc.com> | 2014-03-19 12:45:55 -0700 |
---|---|---|
committer | Mark Spruiell <mes@zeroc.com> | 2014-03-19 12:45:55 -0700 |
commit | cdcffbcc3c3c052afdeb772ff0167e7a90b525bb (patch) | |
tree | 4f16ee41ef7d33394c44e9db81e4d6cd89908250 /cpp/src/slice2js | |
parent | fixing testicedist.py for 5487 (diff) | |
download | ice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.tar.bz2 ice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.tar.xz ice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.zip |
merging javascript branch
Diffstat (limited to 'cpp/src/slice2js')
-rw-r--r-- | cpp/src/slice2js/.depend | 3 | ||||
-rw-r--r-- | cpp/src/slice2js/.depend.mak | 3 | ||||
-rw-r--r-- | cpp/src/slice2js/Gen.cpp | 1782 | ||||
-rw-r--r-- | cpp/src/slice2js/Gen.h | 136 | ||||
-rw-r--r-- | cpp/src/slice2js/JsUtil.cpp | 723 | ||||
-rw-r--r-- | cpp/src/slice2js/JsUtil.h | 51 | ||||
-rw-r--r-- | cpp/src/slice2js/Main.cpp | 304 | ||||
-rw-r--r-- | cpp/src/slice2js/Makefile | 40 | ||||
-rw-r--r-- | cpp/src/slice2js/Makefile.mak | 52 | ||||
-rw-r--r-- | cpp/src/slice2js/Slice2Js.rc | 34 |
10 files changed, 3128 insertions, 0 deletions
diff --git a/cpp/src/slice2js/.depend b/cpp/src/slice2js/.depend new file mode 100644 index 00000000000..0427ab854dc --- /dev/null +++ b/cpp/src/slice2js/.depend @@ -0,0 +1,3 @@ +Gen$(OBJEXT): Gen.cpp $(includedir)/IceUtil/DisableWarnings.h $(includedir)/IceUtil/Functional.h $(includedir)/IceUtil/Handle.h $(includedir)/IceUtil/Exception.h $(includedir)/IceUtil/Config.h $(includedir)/IceUtil/StringUtil.h $(includedir)/IceUtil/InputUtil.h Gen.h JsUtil.h $(includedir)/Slice/Parser.h $(includedir)/IceUtil/Shared.h $(includedir)/IceUtil/OutputUtil.h $(includedir)/IceUtil/Iterator.h $(includedir)/IceUtil/UUID.h $(includedir)/Slice/Checksum.h $(includedir)/Slice/FileTracker.h $(includedir)/Slice/Util.h +JsUtil$(OBJEXT): JsUtil.cpp JsUtil.h $(includedir)/Slice/Parser.h $(includedir)/IceUtil/Shared.h $(includedir)/IceUtil/Config.h $(includedir)/IceUtil/Handle.h $(includedir)/IceUtil/Exception.h $(includedir)/IceUtil/OutputUtil.h $(includedir)/Slice/Util.h $(includedir)/IceUtil/Functional.h +Main$(OBJEXT): Main.cpp $(includedir)/IceUtil/Options.h $(includedir)/IceUtil/Config.h $(includedir)/IceUtil/RecMutex.h $(includedir)/IceUtil/Lock.h $(includedir)/IceUtil/ThreadException.h $(includedir)/IceUtil/Exception.h $(includedir)/IceUtil/Time.h $(includedir)/IceUtil/MutexProtocol.h $(includedir)/IceUtil/Shared.h $(includedir)/IceUtil/Handle.h $(includedir)/IceUtil/CtrlCHandler.h $(includedir)/IceUtil/Mutex.h $(includedir)/IceUtil/MutexPtrLock.h $(includedir)/Slice/Preprocessor.h $(includedir)/Slice/FileTracker.h $(includedir)/Slice/Parser.h $(includedir)/Slice/Util.h $(includedir)/IceUtil/OutputUtil.h Gen.h JsUtil.h diff --git a/cpp/src/slice2js/.depend.mak b/cpp/src/slice2js/.depend.mak new file mode 100644 index 00000000000..59072441311 --- /dev/null +++ b/cpp/src/slice2js/.depend.mak @@ -0,0 +1,3 @@ +Gen$(OBJEXT): Gen.cpp "$(includedir)/IceUtil/DisableWarnings.h" "$(includedir)/IceUtil/Functional.h" "$(includedir)/IceUtil/Handle.h" "$(includedir)/IceUtil/Exception.h" "$(includedir)/IceUtil/Config.h" "$(includedir)/IceUtil/StringUtil.h" "$(includedir)/IceUtil/InputUtil.h" Gen.h JsUtil.h "$(includedir)/Slice/Parser.h" "$(includedir)/IceUtil/Shared.h" "$(includedir)/IceUtil/OutputUtil.h" "$(includedir)/IceUtil/Iterator.h" "$(includedir)/IceUtil/UUID.h" "$(includedir)/Slice/Checksum.h" "$(includedir)/Slice/FileTracker.h" "$(includedir)/Slice/Util.h" +JsUtil$(OBJEXT): JsUtil.cpp JsUtil.h "$(includedir)/Slice/Parser.h" "$(includedir)/IceUtil/Shared.h" "$(includedir)/IceUtil/Config.h" "$(includedir)/IceUtil/Handle.h" "$(includedir)/IceUtil/Exception.h" "$(includedir)/IceUtil/OutputUtil.h" "$(includedir)/Slice/Util.h" "$(includedir)/IceUtil/Functional.h" +Main$(OBJEXT): Main.cpp "$(includedir)/IceUtil/Options.h" "$(includedir)/IceUtil/Config.h" "$(includedir)/IceUtil/RecMutex.h" "$(includedir)/IceUtil/Lock.h" "$(includedir)/IceUtil/ThreadException.h" "$(includedir)/IceUtil/Exception.h" "$(includedir)/IceUtil/Time.h" "$(includedir)/IceUtil/MutexProtocol.h" "$(includedir)/IceUtil/Shared.h" "$(includedir)/IceUtil/Handle.h" "$(includedir)/IceUtil/CtrlCHandler.h" "$(includedir)/IceUtil/Mutex.h" "$(includedir)/IceUtil/MutexPtrLock.h" "$(includedir)/Slice/Preprocessor.h" "$(includedir)/Slice/FileTracker.h" "$(includedir)/Slice/Parser.h" "$(includedir)/Slice/Util.h" "$(includedir)/IceUtil/OutputUtil.h" Gen.h JsUtil.h diff --git a/cpp/src/slice2js/Gen.cpp b/cpp/src/slice2js/Gen.cpp new file mode 100644 index 00000000000..a95735661f6 --- /dev/null +++ b/cpp/src/slice2js/Gen.cpp @@ -0,0 +1,1782 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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/DisableWarnings.h> +#include <IceUtil/Functional.h> +#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; +using namespace IceUtil; +using namespace IceUtilInternal; + +namespace +{ + +bool +isObjectType(const TypePtr& type) +{ + BuiltinPtr b = BuiltinPtr::dynamicCast(type); + ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type); + return cl || (b && b->kind() == Builtin::KindObject); +} + +string +sliceModeToIceMode(Operation::Mode opMode) +{ + switch(opMode) + { + case Operation::Normal: + return "0"; + case Operation::Nonmutating: + return "1"; + case Operation::Idempotent: + return "2"; + default: + assert(false); + } + + return "???"; +} + +string +opFormatTypeToString(const OperationPtr& op) +{ + switch(op->format()) + { + case DefaultFormat: + return "0"; + case CompactFormat: + return "1"; + case SlicedFormat: + return "2"; + default: + assert(false); + } + + return "???"; +} + +string +getDeprecateReason(const ContainedPtr& p1, const ContainedPtr& p2, const string& type) +{ + string deprecateMetadata, deprecateReason; + if(p1->findMetaData("deprecate", deprecateMetadata) || + (p2 != 0 && p2->findMetaData("deprecate", deprecateMetadata))) + { + deprecateReason = "This " + type + " has been deprecated."; + const string prefix = "deprecate:"; + if(deprecateMetadata.find(prefix) == 0 && deprecateMetadata.size() > prefix.size()) + { + deprecateReason = deprecateMetadata.substr(prefix.size()); + } + } + return deprecateReason; +} + +} + +Slice::JsVisitor::JsVisitor(Output& out) : _out(out) +{ +} + +Slice::JsVisitor::~JsVisitor() +{ +} + +void +Slice::JsVisitor::writeMarshalUnmarshalParams(const ParamDeclList& params, const OperationPtr& op, bool marshal) +{ + ParamDeclList optionals; + + for(ParamDeclList::const_iterator pli = params.begin(); pli != params.end(); ++pli) + { + string param = fixId((*pli)->name()); + TypePtr type = (*pli)->type(); + + if((*pli)->optional()) + { + optionals.push_back(*pli); + } + else + { + writeMarshalUnmarshalCode(_out, type, param, marshal); + } + } + + TypePtr ret; + + if(op && op->returnType()) + { + ret = op->returnType(); + + string param = "__ret"; + + if(!op->returnIsOptional()) + { + writeMarshalUnmarshalCode(_out, ret, param, marshal); + } + } + + // + // Sort optional parameters by tag. + // + class SortFn + { + public: + static bool compare(const ParamDeclPtr& lhs, const ParamDeclPtr& rhs) + { + return lhs->tag() < rhs->tag(); + } + }; + optionals.sort(SortFn::compare); + + // + // Handle optional parameters. + // + bool checkReturnType = op && op->returnIsOptional(); + + for(ParamDeclList::const_iterator pli = optionals.begin(); pli != optionals.end(); ++pli) + { + if(checkReturnType && op->returnTag() < (*pli)->tag()) + { + writeOptionalMarshalUnmarshalCode(_out, ret, "__ret", op->returnTag(), marshal); + checkReturnType = false; + } + + string param = fixId((*pli)->name()); + TypePtr type = (*pli)->type(); + + writeOptionalMarshalUnmarshalCode(_out, type, param, (*pli)->tag(), marshal); + } + + if(checkReturnType) + { + writeOptionalMarshalUnmarshalCode(_out, ret, "__ret", op->returnTag(), marshal); + } +} + +void +Slice::JsVisitor::writeMarshalDataMember(const DataMemberPtr& member) +{ + if(member->optional()) + { + writeOptionalMarshalUnmarshalCode(_out, member->type(), "this." + fixId(member->name()), member->tag(), true); + } + else + { + writeMarshalUnmarshalCode(_out, member->type(), "this." + fixId(member->name()), true); + } +} + +void +Slice::JsVisitor::writeUnmarshalDataMember(const DataMemberPtr& member) +{ + if(member->optional()) + { + writeOptionalMarshalUnmarshalCode(_out, member->type(), "this." + fixId(member->name()), + member->tag(), false); + } + else + { + writeMarshalUnmarshalCode(_out, member->type(), "this." + fixId(member->name()), false); + } +} + +void +Slice::JsVisitor::writeDispatchAndMarshalling(const ClassDefPtr& p) +{ +} + +vector<string> +Slice::JsVisitor::getParams(const OperationPtr& op) +{ + vector<string> params; + ParamDeclList paramList = op->parameters(); + for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q) + { + if(!(*q)->isOutParam()) + { + params.push_back(fixId((*q)->name())); + } + } + return params; +} + +vector<string> +Slice::JsVisitor::getParamsAsync(const OperationPtr& op, bool amd, bool newAMI) +{ + vector<string> params; + + string name = fixId(op->name()); + ContainerPtr container = op->container(); + ClassDefPtr cl = ClassDefPtr::dynamicCast(container); // Get the class containing the op. + string scope = fixId(cl->scope()); + if(!newAMI) + { + params.push_back(scope + (amd ? "AMD_" : "AMI_") + cl->name() + '_' + op->name() + " cb__"); + } + + ParamDeclList paramList = op->parameters(); + for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q) + { + if(!(*q)->isOutParam()) + { + params.push_back(typeToString((*q)->type(), (*q)->optional()) + " " + fixId((*q)->name())); + } + } + return params; +} + +vector<string> +Slice::JsVisitor::getParamsAsyncCB(const OperationPtr& op, bool newAMI, bool outKeyword) +{ + vector<string> params; + + if(!newAMI) + { + TypePtr ret = op->returnType(); + if(ret) + { + params.push_back(typeToString(ret, op->returnIsOptional()) + " ret__"); + } + } + + ParamDeclList paramList = op->parameters(); + for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q) + { + if((*q)->isOutParam()) + { + if(!newAMI) + { + params.push_back(typeToString((*q)->type(), (*q)->optional()) + ' ' + fixId((*q)->name())); + } + else + { + string s; + if(outKeyword) + { + s += "out "; + } + s += typeToString((*q)->type(), (*q)->optional()) + ' ' + fixId((*q)->name()); + params.push_back(s); + } + } + } + + return params; +} + +vector<string> +Slice::JsVisitor::getArgs(const OperationPtr& op) +{ + vector<string> args; + ParamDeclList paramList = op->parameters(); + for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q) + { + string arg = fixId((*q)->name()); + if((*q)->isOutParam()) + { + arg = "out " + arg; + } + args.push_back(arg); + } + return args; +} + +vector<string> +Slice::JsVisitor::getArgsAsync(const OperationPtr& op, bool newAMI) +{ + vector<string> args; + + if(!newAMI) + { + args.push_back("cb__"); + } + + ParamDeclList paramList = op->parameters(); + for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q) + { + if(!(*q)->isOutParam()) + { + args.push_back(fixId((*q)->name())); + } + } + return args; +} + +vector<string> +Slice::JsVisitor::getArgsAsyncCB(const OperationPtr& op, bool newAMI, bool outKeyword) +{ + vector<string> args; + + if(!newAMI) + { + TypePtr ret = op->returnType(); + if(ret) + { + args.push_back("ret__"); + } + } + + ParamDeclList paramList = op->parameters(); + for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q) + { + if((*q)->isOutParam()) + { + string s; + if(outKeyword) + { + s = "out "; + } + s += fixId((*q)->name()); + args.push_back(s); + } + } + + return args; +} + +string +Slice::JsVisitor::getValue(const string& scope, const TypePtr& type) +{ + assert(type); + + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + switch(builtin->kind()) + { + case Builtin::KindBool: + { + return "false"; + break; + } + case Builtin::KindByte: + case Builtin::KindShort: + case Builtin::KindInt: + case Builtin::KindLong: + { + return "0"; + break; + } + case Builtin::KindFloat: + case Builtin::KindDouble: + { + return "0.0"; + break; + } + default: + { + return "null"; + break; + } + } + } + + EnumPtr en = EnumPtr::dynamicCast(type); + if(en) + { + return getReference(scope, (*en->getEnumerators().begin())->scoped()); + } + + return "null"; +} + +void +Slice::JsVisitor::writeConstantValue(const string& scope, const TypePtr& type, const SyntaxTreeBasePtr& valueType, + const string& value) +{ + ConstPtr constant = ConstPtr::dynamicCast(valueType); + if(constant) + { + _out << getReference(scope, constant->scoped()); + } + else + { + BuiltinPtr bp = BuiltinPtr::dynamicCast(type); + EnumPtr ep; + if(bp && bp->kind() == Builtin::KindString) + { + // + // Expand strings into the basic source character set. We can't use isalpha() and the like + // here because they are sensitive to the current locale. + // + static const string basicSourceChars = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "_{}[]#()<>%:;.?*+-/^&|~!=,\\\"' "; + static const set<char> charSet(basicSourceChars.begin(), basicSourceChars.end()); + + _out << "\""; // Opening " + + for(string::const_iterator c = value.begin(); c != value.end(); ++c) + { + if(charSet.find(*c) == charSet.end()) + { + unsigned char uc = *c; // char may be signed, so make it positive + ostringstream s; + s << "\\u"; // Print as unicode if not in basic source character set + s << hex; + s.width(4); + s.fill('0'); + s << static_cast<unsigned>(uc); + _out << s.str(); + } + else + { + switch(*c) + { + case '\\': + case '"': + { + _out << "\\"; + break; + } + } + _out << *c; // Print normally if in basic source character set + } + } + + _out << "\""; // Closing " + } + else if(bp && bp->kind() == Builtin::KindLong) + { + IceUtil::Int64 l = IceUtilInternal::strToInt64(value.c_str(), 0, 0); + + // + // JavaScript doesn't support 64 bit integer so long types are written as + // two 32 bit words hi, low wrapped in the Ice.Long class. + // + // If slice2js runs in a big endian machine we need to swap the words, we do not + // need to swap the word bytes as we just write each word as a number to the + // output file. + // +#ifdef ICE_BIG_ENDIAN + _out << "new Ice.Long(" << (l & 0xFFFFFFFF) << ", " << ((l >> 32) & 0xFFFFFFFF) << ")"; +#else + _out << "new Ice.Long(" << ((l >> 32) & 0xFFFFFFFF) << ", " << (l & 0xFFFFFFFF) << ")"; +#endif + } + else if((ep = EnumPtr::dynamicCast(type))) + { + string::size_type colon = value.rfind(':'); + string enumerator; + if(colon != string::npos) + { + enumerator = fixId(value.substr(colon + 1)); + } + else + { + enumerator = fixId(value); + } + _out << getReference(scope, ep->scoped()) << '.' << enumerator; + } + else + { + _out << value; + } + } +} + +StringList +Slice::JsVisitor::splitComment(const ContainedPtr& p) +{ + StringList result; + + string comment = p->comment(); + string::size_type pos = 0; + string::size_type nextPos; + while((nextPos = comment.find_first_of('\n', pos)) != string::npos) + { + result.push_back(string(comment, pos, nextPos - pos)); + pos = nextPos + 1; + } + string lastLine = string(comment, pos); + if(lastLine.find_first_not_of(" \t\n\r") != string::npos) + { + result.push_back(lastLine); + } + + return result; +} + +void +Slice::JsVisitor::writeDocComment(const ContainedPtr& p, const string& deprecateReason, const string& extraParam) +{ + StringList lines = splitComment(p); + if(lines.empty()) + { + if(!deprecateReason.empty()) + { + _out << nl << "/**"; + _out << nl << " * @deprecated " << deprecateReason; + _out << nl << " **/"; + } + return; + } + + _out << nl << "/**"; + + bool doneExtraParam = false; + for(StringList::const_iterator i = lines.begin(); i != lines.end(); ++i) + { + // + // @param must precede @return, so emit any extra parameter + // when @return is seen. + // + if(i->find("@return") != string::npos && !extraParam.empty()) + { + _out << nl << " * " << extraParam; + doneExtraParam = true; + } + _out << nl << " * " << *i; + } + + if(!doneExtraParam && !extraParam.empty()) + { + // + // Above code doesn't emit the comment for the extra parameter + // if the operation returns a void or doesn't have an @return. + // + _out << nl << " * " << extraParam; + } + + if(!deprecateReason.empty()) + { + _out << nl << " * @deprecated " << deprecateReason; + } + + _out << nl << " **/"; +} + +Slice::Gen::Gen(const string& base, const vector<string>& includePaths, const string& dir) : + _includePaths(includePaths) +{ + _fileBase = base; + string::size_type pos = base.find_last_of("/\\"); + if(pos != string::npos) + { + _fileBase = base.substr(pos + 1); + } + string file = _fileBase + ".js"; + + if(!dir.empty()) + { + file = dir + '/' + file; + } + + _out.open(file.c_str()); + if(!_out) + { + ostringstream os; + os << "cannot open `" << file << "': " << strerror(errno); + throw FileException(__FILE__, __LINE__, os.str()); + } + FileTracker::instance()->addFile(file); + printHeader(); + + printGeneratedHeader(_out, _fileBase + ".ice"); +} + +Slice::Gen::~Gen() +{ + if(_out.isOpen()) + { + _out << '\n'; + } +} + +void +Slice::Gen::generate(const UnitPtr& p) +{ + if(p->hasOnlyClassDecls()) + { + // Don't generate any code if the Slice file only contains + // forward declarations. + return; + } + + _out << nl << "(function(global, r)"; + _out << sb; + _out << nl << "var require = typeof(r) === \"function\" ? r : function(){};"; + + RequireVisitor requireVisitor(_out, _includePaths); + p->visit(&requireVisitor, false); + vector<string> seenModules = requireVisitor.writeRequires(p); + + TypesVisitor typesVisitor(_out, seenModules); + p->visit(&typesVisitor, false); + + // + // Export the top-level modules. + // + ExportVisitor exportVisitor(_out); + p->visit(&exportVisitor, false); + + _out << eb; + _out << nl << "(typeof (global) === \"undefined\" ? window : global, " + << "typeof (require) === \"undefined\" ? undefined : require));"; +} + +void +Slice::Gen::closeOutput() +{ + _out.close(); +} + +void +Slice::Gen::printHeader() +{ + static const char* header = +"// **********************************************************************\n" +"//\n" +"// Copyright (c) 2003-2014 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) + : JsVisitor(out), + _seenClass(false), + _seenCompactId(false), + _seenOperation(false), + _seenStruct(false), + _seenUserException(false), + _seenLocalException(false), + _seenEnum(false), + _seenObjectSeq(false), + _seenObjectDict(false), + _includePaths(includePaths) +{ + for(vector<string>::iterator p = _includePaths.begin(); p != _includePaths.end(); ++p) + { + *p = fullPath(*p); + } +} + +bool +Slice::Gen::RequireVisitor::visitClassDefStart(const ClassDefPtr& p) +{ + _seenClass = true; // Set regardless of whether p->isLocal() + if(p->compactId() >= 0) + { + _seenCompactId = true; + } + return !p->isLocal(); // Look for operations. +} + +bool +Slice::Gen::RequireVisitor::visitStructStart(const StructPtr& p) +{ + _seenStruct = true; // Set regardless of whether p->isLocal() + return false; +} + +void +Slice::Gen::RequireVisitor::visitOperation(const OperationPtr& p) +{ + _seenOperation = true; +} + +bool +Slice::Gen::RequireVisitor::visitExceptionStart(const ExceptionPtr& p) +{ + if(p->isLocal()) + { + _seenLocalException = true; + } + else + { + _seenUserException = true; + } + + return false; +} + +void +Slice::Gen::RequireVisitor::visitSequence(const SequencePtr& seq) +{ + BuiltinPtr builtin = BuiltinPtr::dynamicCast(seq->type()); + if(builtin && builtin->kind() == Builtin::KindObject) + { + _seenObjectSeq = true; + } +} + +void +Slice::Gen::RequireVisitor::visitDictionary(const DictionaryPtr& dict) +{ + BuiltinPtr builtin = BuiltinPtr::dynamicCast(dict->valueType()); + if(builtin && builtin->kind() == Builtin::KindObject) + { + _seenObjectDict = true; + } +} + +void +Slice::Gen::RequireVisitor::visitEnum(const EnumPtr& p) +{ + _seenEnum = true; +} + +vector<string> +Slice::Gen::RequireVisitor::writeRequires(const UnitPtr& p) +{ + vector<string> seenModules; + + // + // Generate require() statements for all of the run-time code needed by the generated code. + // + if(_seenClass || _seenObjectSeq || _seenObjectDict) + { + _out << nl << "require(\"Ice/Object\");"; + } + if(_seenClass) + { + _out << nl << "require(\"Ice/ObjectPrx\");"; + } + if(_seenOperation) + { + _out << nl << "require(\"Ice/Operation\");"; + } + if(_seenStruct) + { + _out << nl << "require(\"Ice/Struct\");"; + } + + if(_seenLocalException || _seenUserException) + { + _out << nl << "require(\"Ice/Exception\");"; + } + + if(_seenEnum) + { + _out << nl << "require(\"Ice/EnumBase\");"; + } + + if(_seenCompactId) + { + _out << nl << "require(\"Ice/CompactIdRegistry\");"; + } + + _out << nl << "require(\"Ice/Long\");"; + _out << nl << "require(\"Ice/HashMap\");"; + _out << nl << "require(\"Ice/HashUtil\");"; + _out << nl << "require(\"Ice/ArrayUtil\");"; + _out << nl << "require(\"Ice/StreamHelpers\");"; + _out << nl; + _out << nl << "var Ice = global.Ice || {};"; + seenModules.push_back("Ice"); + + StringList includes = p->includeFiles(); + for(StringList::const_iterator i = includes.begin(); i != includes.end(); ++i) + { + _out << nl << "require(\"" << changeInclude(*i, _includePaths) << "\");"; + + set<string> modules = p->getTopLevelModules(*i); + for(set<string>::const_iterator j = modules.begin(); j != modules.end(); ++j) + { + vector<string>::const_iterator k = find(seenModules.begin(), seenModules.end(), *j); + if(k == seenModules.end()) + { + seenModules.push_back(*j); + _out << nl; + _out << nl << "var " << (*j) << " = global." << (*j) << " || {};"; + } + } + } + return seenModules; +} + +Slice::Gen::TypesVisitor::TypesVisitor(IceUtilInternal::Output& out, vector<string> seenModules) : + JsVisitor(out), + _seenModules(seenModules) +{ +} + +bool +Slice::Gen::TypesVisitor::visitModuleStart(const ModulePtr& p) +{ + // + // For a top-level module we write the following: + // + // var Foo = global.Foo || {}; + // + // For an inner module we write + // + // Foo.Bar = global.Foo ? (global.Foo.Bar || {}) : {}; + // + + const string scoped = getLocalScope(p->scoped()); + vector<string>::const_iterator i = find(_seenModules.begin(), _seenModules.end(), scoped); + if(i == _seenModules.end()) + { + _seenModules.push_back(scoped); + const bool topLevel = UnitPtr::dynamicCast(p->container()); + if(topLevel) + { + _out << nl << "var " << scoped << " = global." << scoped << " || {};"; + } + else + { + _out << nl << scoped << " = global." << getLocalScope(p->scope()) << " ? (global." << scoped + << " || {}) : {};"; + } + } + return true; +} + +void +Slice::Gen::TypesVisitor::visitModuleEnd(const ModulePtr& p) +{ +} + +bool +Slice::Gen::TypesVisitor::visitClassDefStart(const ClassDefPtr& p) +{ + const string scope = p->scope(); + const string scoped = p->scoped(); + const string localScope = getLocalScope(scope); + const string name = fixId(p->name()); + const string prxName = p->name() + "Prx"; + const string objectRef = "Ice.Object"; + const string prxRef = "Ice.ObjectPrx"; + const string defineObject = p->isLocal() ? "Slice.defineLocalObject" : "Slice.defineObject"; + + ClassList bases = p->bases(); + ClassDefPtr base; + string baseRef; + string basePrxRef; + const bool hasBaseClass = !bases.empty() && !bases.front()->isInterface(); + if(hasBaseClass) + { + base = bases.front(); + bases.erase(bases.begin()); + baseRef = getReference(scope, base->scoped()); + basePrxRef = getReference(scope, base->scoped() + "Prx"); + } + else + { + baseRef = objectRef; + basePrxRef = prxRef; + } + + const DataMemberList allDataMembers = p->allDataMembers(); + const DataMemberList dataMembers = p->dataMembers(); + const DataMemberList optionalMembers = p->orderedOptionalDataMembers(); + + vector<string> allParamNames; + for(DataMemberList::const_iterator q = allDataMembers.begin(); q != allDataMembers.end(); ++q) + { + allParamNames.push_back(fixId((*q)->name())); + } + + bool hasClassMembers = false; + vector<string> paramNames; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + paramNames.push_back(fixId((*q)->name())); + if(!hasClassMembers && isClassType((*q)->type())) + { + hasClassMembers = true; + } + } + + vector<string> baseParamNames; + DataMemberList baseDataMembers; + + if(base) + { + baseDataMembers = base->allDataMembers(); + for(DataMemberList::const_iterator q = baseDataMembers.begin(); q != baseDataMembers.end(); ++q) + { + baseParamNames.push_back(fixId((*q)->name())); + } + } + + _out << sp; + writeDocComment(p, getDeprecateReason(p, 0, "type")); + _out << nl << localScope << '.' << name << " = " << defineObject << "("; + _out.inc(); + + if(!allParamNames.empty()) + { + _out << nl << "function" << spar << allParamNames << epar; + _out << sb; + if(!p->isLocal() || hasBaseClass) + { + _out << nl << baseRef << ".call" << spar << "this" << baseParamNames << epar << ';'; + } + + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + const string m = fixId((*q)->name()); + if((*q)->optional()) + { + if((*q)->defaultValueType()) + { + _out << nl << "this." << m << " = " << m << " !== undefined ? " << m << " : "; + writeConstantValue(scope, (*q)->type(), (*q)->defaultValueType(), (*q)->defaultValue()); + _out << ';'; + } + else + { + _out << nl << "this." << m << " = " << m << ';'; + } + } + else + { + _out << nl << "this." << m << " = " << m << " !== undefined ? " << m << " : "; + if((*q)->defaultValueType()) + { + writeConstantValue(scope, (*q)->type(), (*q)->defaultValueType(), (*q)->defaultValue()); + } + else + { + _out << getValue(scope, (*q)->type()); + } + _out << ';'; + } + } + _out << eb; + } + else + { + if(hasBaseClass || !p->isLocal()) + { + _out << nl << "undefined"; + } + } + if(!p->isLocal() || hasBaseClass) + { + _out << ","; + _out << nl << baseRef; + } + + if(!p->isLocal()) + { + ClassList allBases = p->allBases(); + StringList ids; + + if(!bases.empty()) + { + _out << ","; + _out << nl << "["; + _out.inc(); + for(ClassList::const_iterator q = bases.begin(); q != bases.end();) + { + ClassDefPtr base = *q; + if(base->isInterface()) + { + _out << nl << getLocalScope(base->scope()) << "." << base->name(); + if(++q != bases.end()) + { + _out << ", "; + } + } + else + { + q++; + } + } + _out.dec(); + _out << nl << "]"; + } + else + { + _out << ", undefined"; + } + +#if defined(__IBMCPP__) && defined(NDEBUG) + // + // VisualAge C++ 6.0 does not see that ClassDef is a Contained, + // when inlining is on. The code below issues a warning: better + // than an error! + // + transform(allBases.begin(), allBases.end(), back_inserter(ids), + ::IceUtil::constMemFun<string,ClassDef>(&Contained::scoped)); +#else + transform(allBases.begin(), allBases.end(), back_inserter(ids), ::IceUtil::constMemFun(&Contained::scoped)); +#endif + StringList other; + other.push_back(scoped); + other.push_back("::Ice::Object"); + other.sort(); + ids.merge(other); + ids.unique(); + + StringList::const_iterator firstIter = ids.begin(); + StringList::const_iterator scopedIter = find(ids.begin(), ids.end(), scoped); + assert(scopedIter != ids.end()); + StringList::difference_type scopedPos = IceUtilInternal::distance(firstIter, scopedIter); + + _out << ", " << scopedPos << ","; + _out << nl << "["; + _out.inc(); + for(StringList::const_iterator q = ids.begin(); q != ids.end(); ++q) + { + if(q != ids.begin()) + { + _out << ','; + } + _out << nl << '"' << *q << '"'; + } + _out.dec(); + _out << nl << "],"; + _out << nl << p->compactId() << ","; + if(dataMembers.empty()) + { + _out << " undefined, undefined, "; + } + else + { + _out << nl << "function(__os)"; + _out << sb; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + writeMarshalDataMember(*q); + } + _out << eb << ","; + + _out << nl << "function(__is)"; + _out << sb; + if(hasClassMembers) + { + _out << nl << "var self = this;"; + } + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + writeUnmarshalDataMember(*q); + } + _out << eb << ","; + _out << nl; + } + _out << (p->hasMetaData("preserve-slice") && !p->inheritsMetaData("preserve-slice") ? "true" : "false"); + } + _out.dec(); + _out << ");"; + + if(!p->isLocal()) + { + const string staticId = localScope + "." + name + ".ice_staticId"; + const string baseProxy = + !p->isInterface() && base ? (getLocalScope(base->scope()) + "." + base->name() + "Prx") : "Ice.ObjectPrx"; + + _out << sp; + _out << nl << localScope << '.' << prxName << " = " << "Slice.defineProxy(" << baseProxy << ", " << staticId; + if(!bases.empty()) + { + _out << ", ["; + _out.inc(); + for(ClassList::const_iterator q = bases.begin(); q != bases.end();) + { + ClassDefPtr base = *q; + if(base->isInterface()) + { + _out << nl << getLocalScope(base->scope()) << "." << base->name() << "Prx"; + if(++q != bases.end()) + { + _out << ", "; + } + } + else + { + q++; + } + } + _out.dec(); + _out << "]"; + } + else + { + _out << ", undefined"; + } + _out << ");"; + + // + // Register the compact id + // + if(p->compactId() >= 0) + { + // + // Also register the type using the stringified compact ID. + // + _out << nl << "Ice.CompactIdRegistry.set(" << p->compactId() << ", " << localScope << "." + << name << ".ice_staticId());"; + } + + _out << sp << nl << "Slice.defineOperations(" << localScope << '.' << name << ", " << localScope << '.' + << prxName; + const OperationList ops = p->operations(); + if(!ops.empty()) + { + _out << ','; + _out << sb; + for(OperationList::const_iterator q = ops.begin(); q != ops.end(); ++q) + { + if(q != ops.begin()) + { + _out << ','; + } + + OperationPtr op = *q; + const string name = fixId(op->name()); + const ParamDeclList paramList = op->parameters(); + const TypePtr ret = op->returnType(); + ParamDeclList inParams, outParams; + for(ParamDeclList::const_iterator pli = paramList.begin(); pli != paramList.end(); ++pli) + { + if((*pli)->isOutParam()) + { + outParams.push_back(*pli); + } + else + { + inParams.push_back(*pli); + } + } + + // + // Each operation descriptor is a property. The key is the "on-the-wire" + // name, and the value is an array consisting of the following elements: + // + // 0: servant method name in case of a keyword conflict (e.g., "_while"), + // otherwise an empty string + // 1: mode (undefined == Normal or int) + // 2: sendMode (undefined == Normal or int) + // 3: amd (undefined or 1) + // 4: format (undefined == Default or int) + // 5: return type (undefined if void, or [type, tag]) + // 6: in params (undefined if none, or array of [type, tag]) + // 7: out params (undefined if none, or array of [type, tag]) + // 8: exceptions (undefined if none, or array of types) + // 9: sends classes (true or undefined) + // 10: returns classes (true or undefined) + // + _out << nl << "\"" << op->name() << "\": ["; // Operation name over-the-wire. + + if(name != op->name()) + { + _out << name; // Native method name. + } + _out << ", "; + + if(op->mode() != Operation::Normal) + { + _out << sliceModeToIceMode(op->mode()); // Mode. + } + _out << ", "; + + if(op->sendMode() != Operation::Normal) + { + _out << sliceModeToIceMode(op->sendMode()); // Send mode. + } + _out << ", "; + + if(p->hasMetaData("amd") || op->hasMetaData("amd")) + { + _out << "1"; // AMD. + } + _out << ", "; + + if(op->format() != DefaultFormat) + { + _out << opFormatTypeToString(op); // Format. + } + _out << ", "; + + // + // Return type. + // + if(ret) + { + _out << '[' << encodeTypeForOperation(ret); + const bool isObj = isObjectType(ret); + if(isObj) + { + _out << ", true"; + } + if(op->returnIsOptional()) + { + if(!isObj) + { + _out << ", "; + } + _out << ", " << op->returnTag(); + } + _out << ']'; + } + _out << ", "; + + // + // In params. + // + if(!inParams.empty()) + { + _out << '['; + for(ParamDeclList::const_iterator pli = inParams.begin(); pli != inParams.end(); ++pli) + { + if(pli != inParams.begin()) + { + _out << ", "; + } + TypePtr t = (*pli)->type(); + _out << '[' << encodeTypeForOperation(t); + const bool isObj = isObjectType(t); + if(isObj) + { + _out << ", true"; + } + if((*pli)->optional()) + { + if(!isObj) + { + _out << ", "; + } + _out << ", " << (*pli)->tag(); + } + _out << ']'; + } + _out << ']'; + } + _out << ", "; + + // + // Out params. + // + if(!outParams.empty()) + { + _out << '['; + for(ParamDeclList::const_iterator pli = outParams.begin(); pli != outParams.end(); ++pli) + { + if(pli != outParams.begin()) + { + _out << ", "; + } + TypePtr t = (*pli)->type(); + _out << '[' << encodeTypeForOperation(t); + const bool isObj = isObjectType(t); + if(isObj) + { + _out << ", true"; + } + if((*pli)->optional()) + { + if(!isObj) + { + _out << ", "; + } + _out << ", " << (*pli)->tag(); + } + _out << ']'; + } + _out << ']'; + } + _out << ", "; + + // + // User exceptions. + // + ExceptionList throws = op->throws(); + throws.sort(); + throws.unique(); +#if defined(__SUNPRO_CC) + throws.sort(derivedToBaseCompare); +#else + throws.sort(Slice::DerivedToBaseCompare()); +#endif + if(!throws.empty()) + { + _out << nl << '['; + _out.inc(); + for(ExceptionList::const_iterator eli = throws.begin(); eli != throws.end(); ++eli) + { + if(eli != throws.begin()) + { + _out << ','; + } + _out << nl << fixId((*eli)->scoped()); + } + _out.dec(); + _out << nl << ']'; + } + _out << ", "; + + if(op->sendsClasses(false)) + { + _out << "true"; + } + _out << ", "; + + if(op->returnsClasses(false)) + { + _out << "true"; + } + + _out << ']'; + } + _out << eb; + } + _out << ");"; + } + + return false; +} + +void +Slice::Gen::TypesVisitor::visitSequence(const SequencePtr& p) +{ + const TypePtr type = p->type(); + + // + // Stream helpers for sequences are lazy initialized as the required + // types might not be available until later. + // + const string scope = getLocalScope(p->scope()); + const string name = fixId(p->name()); + const string propertyName = name + "Helper"; + const bool fixed = !type->isVariableLength(); + + _out << nl << "Slice.defineSequence(" << scope << ", \"" << propertyName << "\", " + << "\"" << getHelper(type) << "\"" << ", " << (fixed ? "true" : "false"); + if(isClassType(type)) + { + _out<< ", \"" << typeToString(type) << "\""; + } + _out << ");"; +} + +bool +Slice::Gen::TypesVisitor::visitExceptionStart(const ExceptionPtr& p) +{ + const string scope = p->scope(); + const string localScope = getLocalScope(scope); + const string name = fixId(p->name()); + const ExceptionPtr base = p->base(); + string baseRef; + string defineException = p->isLocal() ? "Slice.defineLocalException" : "Slice.defineUserException"; + if(base) + { + baseRef = getReference(scope, base->scoped()); + } + else + { + baseRef = p->isLocal() ? "Ice.LocalException" : "Ice.UserException"; + } + + const DataMemberList allDataMembers = p->allDataMembers(); + const DataMemberList dataMembers = p->dataMembers(); + + vector<string> allParamNames; + for(DataMemberList::const_iterator q = allDataMembers.begin(); q != allDataMembers.end(); ++q) + { + allParamNames.push_back(fixId((*q)->name())); + } + + vector<string> paramNames; + bool hasClassMembers = false; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + paramNames.push_back(fixId((*q)->name())); + if(!hasClassMembers && ClassDeclPtr::dynamicCast((*q)->type())) + { + hasClassMembers = true; + } + } + + vector<string> baseParamNames; + DataMemberList baseDataMembers; + + if(p->base()) + { + baseDataMembers = p->base()->allDataMembers(); + for(DataMemberList::const_iterator q = baseDataMembers.begin(); q != baseDataMembers.end(); ++q) + { + baseParamNames.push_back(fixId((*q)->name())); + } + } + + _out << sp; + writeDocComment(p, getDeprecateReason(p, 0, "type")); + _out << nl << localScope << '.' << name << " = " << defineException << "("; + _out.inc(); + + _out << nl << "function" << spar << allParamNames << "_cause" << epar; + _out << sb; + _out << nl << baseRef << ".call" << spar << "this" << baseParamNames << "_cause" << epar << ';'; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + const string m = fixId((*q)->name()); + if((*q)->optional()) + { + if((*q)->defaultValueType()) + { + _out << nl << "this." << m << " = " << m << " !== undefined ? " << m << " : "; + writeConstantValue(scope, (*q)->type(), (*q)->defaultValueType(), (*q)->defaultValue()); + _out << ';'; + } + else + { + _out << nl << "this." << m << " = " << m << ';'; + } + } + else + { + _out << nl << "this." << m << " = " << m << " !== undefined ? " << m << " : "; + if((*q)->defaultValueType()) + { + writeConstantValue(scope, (*q)->type(), (*q)->defaultValueType(), (*q)->defaultValue()); + } + else + { + _out << getValue(scope, (*q)->type()); + } + _out << ';'; + } + } + _out << eb << ","; + _out << nl << baseRef << ","; + _out << nl << "\"" << p->scoped().substr(2) << "\""; + + // TODO: equals? + + if(!p->isLocal()) + { + _out << ","; + if(!dataMembers.empty()) + { + _out << nl << "function(__os)"; + _out << sb; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + writeMarshalDataMember(*q); + } + _out << eb << ","; + _out << nl << "function(__is)"; + _out << sb; + if(hasClassMembers) + { + _out << nl << "var self = this;"; + } + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + writeUnmarshalDataMember(*q); + } + _out << eb; + } + else + { + _out << nl << "undefined, undefined"; + } + bool basePreserved = p->inheritsMetaData("preserve-slice"); + bool preserved = p->hasMetaData("preserve-slice"); + + _out << ","; + if(preserved && !basePreserved) + { + _out << nl << "true"; + } + else + { + _out << nl << "false"; + } + + const bool usesClasses = p->usesClasses(false) && (!base || (base && !base->usesClasses(false))); + _out << "," << nl << (usesClasses ? "true" : "false"); + } + _out << ");"; + _out.dec(); + return false; +} + +bool +Slice::Gen::TypesVisitor::visitStructStart(const StructPtr& p) +{ + const string scope = p->scope(); + const string localScope = getLocalScope(scope); + const string name = fixId(p->name()); + + const DataMemberList dataMembers = p->dataMembers(); + + vector<string> paramNames; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + paramNames.push_back(fixId((*q)->name())); + } + + // + // Only generate hashCode if this structure type is a legal dictionary key type. + // + bool containsSequence = false; + bool legalKeyType = Dictionary::legalKeyType(p, containsSequence); + + _out << sp; + writeDocComment(p, getDeprecateReason(p, 0, "type")); + _out << nl << localScope << '.' << name << " = Slice.defineStruct("; + _out.inc(); + _out << nl << "function" << spar << paramNames << epar; + _out << sb; + bool hasClassMembers = false; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + if(!hasClassMembers && isClassType((*q)->type())) + { + hasClassMembers = true; + } + + string memberName = fixId((*q)->name()); + _out << nl << "this." << memberName << " = " << memberName << " !== undefined ? " << memberName << " : "; + if((*q)->defaultValueType()) + { + writeConstantValue(scope, (*q)->type(), (*q)->defaultValueType(), (*q)->defaultValue()); + } + else + { + _out << getValue(scope, (*q)->type()); + } + _out << ';'; + } + _out << eb << ","; + + _out << nl << (legalKeyType ? "true" : "false"); + + if(!p->isLocal()) + { + _out << ","; + _out << nl << "function(__os)"; + _out << sb; + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + writeMarshalDataMember(*q); + } + _out << eb << ","; + + _out << nl << "function(__is)"; + _out << sb; + if(hasClassMembers) + { + _out << nl << "var self = this;"; + } + for(DataMemberList::const_iterator q = dataMembers.begin(); q != dataMembers.end(); ++q) + { + writeUnmarshalDataMember(*q); + } + _out << eb << "," + << nl << p->minWireSize() << ", " + << nl << (p->isVariableLength() ? "false" : "true"); + _out.dec(); + _out << ");"; + } + else + { + _out.dec(); + _out << ");"; + } + return false; +} + +void +Slice::Gen::TypesVisitor::visitDictionary(const DictionaryPtr& p) +{ + const TypePtr keyType = p->keyType(); + const TypePtr valueType = p->valueType(); + + // + // For some key types, we have to use an equals() method to compare keys + // rather than the native comparison operators. + // + bool useEquals = false; + const BuiltinPtr b = BuiltinPtr::dynamicCast(keyType); + if((b && b->kind() == Builtin::KindLong) || StructPtr::dynamicCast(keyType)) + { + useEquals = true; + } + + // + // Stream helpers for dictionaries of objects are lazy initialized + // as the required object type might not be available until later. + // + const string scope = getLocalScope(p->scope()); + const string name = fixId(p->name()); + const string propertyName = name + "Helper"; + bool fixed = !keyType->isVariableLength() && !valueType->isVariableLength(); + + _out << nl << "Slice.defineDictionary(" << scope << ", \"" << name << "\", \"" << propertyName << "\", " + << "\"" << getHelper(keyType) << "\", " + << "\"" << getHelper(valueType) << "\", " + << (fixed ? "true" : "false") << ", " + << (useEquals ? "true" : "false"); + if(isClassType(valueType)) + { + _out<< ", \"" << typeToString(valueType) << "\""; + } + _out << ");"; +} + +void +Slice::Gen::TypesVisitor::visitEnum(const EnumPtr& p) +{ + const string scope = p->scope(); + const string localScope = getLocalScope(scope); + const string name = fixId(p->name()); + + _out << sp; + writeDocComment(p, getDeprecateReason(p, 0, "type")); + _out << nl << localScope << '.' << name << " = Slice.defineEnum({"; + _out.inc(); + _out << nl; + + const EnumeratorList enumerators = p->getEnumerators(); + int i = 0; + for(EnumeratorList::const_iterator en = enumerators.begin(); en != enumerators.end(); ++en) + { + if(en != enumerators.begin()) + { + if(++i % 5 == 0) + { + _out << ',' << nl; + } + else + { + _out << ", "; + } + } + _out << "'" << fixId((*en)->name()) << "':" << (*en)->value(); + } + _out << "});"; + _out.dec(); + + // + // EnumBase provides an equals() method + // +} + +void +Slice::Gen::TypesVisitor::visitConst(const ConstPtr& p) +{ + const string scope = p->scope(); + const string localScope = getLocalScope(scope); + const string name = fixId(p->name()); + + _out << sp; + _out << nl << "Object.defineProperty(" << localScope << ", '" << name << "', {"; + _out.inc(); + _out << nl << "value: "; + writeConstantValue(scope, p->type(), p->valueType(), p->value()); + _out.dec(); + _out << nl << "});"; +} + +string +Slice::Gen::TypesVisitor::encodeTypeForOperation(const TypePtr& type) +{ + assert(type); + + static const char* builtinTable[] = + { + "0", // byte + "1", // bool + "2", // short + "3", // int + "4", // long + "5", // float + "6", // double + "7", // string + "8", // Ice.Object + "9", // Ice.ObjectPrx + "??" // LocalObject + }; + + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + return builtinTable[builtin->kind()]; + } + + ProxyPtr proxy = ProxyPtr::dynamicCast(type); + if(proxy) + { + return "\"" + fixId(proxy->_class()->scoped() + "Prx") + "\""; + } + + SequencePtr seq = SequencePtr::dynamicCast(type); + if(seq) + { + return "\"" + fixId(seq->scoped() + "Helper") + "\""; + } + + DictionaryPtr d = DictionaryPtr::dynamicCast(type); + if(d) + { + return "\"" + fixId(d->scoped() + "Helper") + "\""; + } + + EnumPtr e = EnumPtr::dynamicCast(type); + if(e) + { + return fixId(e->scoped()) + ".__helper"; + } + + StructPtr st = StructPtr::dynamicCast(type); + if(st) + { + return fixId(st->scoped()); + } + + ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type); + if(cl) + { + return "\"" + fixId(cl->scoped()) + "\""; + } + + return "???"; +} + +Slice::Gen::ExportVisitor::ExportVisitor(IceUtilInternal::Output& out) + : JsVisitor(out) +{ +} + +bool +Slice::Gen::ExportVisitor::visitModuleStart(const ModulePtr& p) +{ + const bool topLevel = UnitPtr::dynamicCast(p->container()); + if(topLevel) + { + const string localScope = getLocalScope(p->scope()); + const string name = localScope.empty() ? fixId(p->name()) : localScope + "." + p->name(); + _out << nl << "global." << name << " = " << name << ";"; + } + return false; +} diff --git a/cpp/src/slice2js/Gen.h b/cpp/src/slice2js/Gen.h new file mode 100644 index 00000000000..d337e3eee5c --- /dev/null +++ b/cpp/src/slice2js/Gen.h @@ -0,0 +1,136 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#ifndef GEN_H +#define GEN_H + +#include <JsUtil.h> + +namespace Slice +{ + +class JsVisitor : public JsGenerator, public ParserVisitor +{ +public: + + JsVisitor(::IceUtilInternal::Output&); + virtual ~JsVisitor(); + +protected: + + void writeMarshalUnmarshalParams(const ParamDeclList&, const OperationPtr&, bool); + void writePostUnmarshalParams(const ParamDeclList&, const OperationPtr&); + void writeMarshalDataMember(const DataMemberPtr&); + void writeUnmarshalDataMember(const DataMemberPtr&); + + virtual void writeDispatchAndMarshalling(const ClassDefPtr&); + virtual std::vector<std::string> getParams(const OperationPtr&); + virtual std::vector<std::string> getParamsAsync(const OperationPtr&, bool, bool = false); + virtual std::vector<std::string> getParamsAsyncCB(const OperationPtr&, bool = false, bool = true); + virtual std::vector<std::string> getArgs(const OperationPtr&); + virtual std::vector<std::string> getArgsAsync(const OperationPtr&, bool = false); + virtual std::vector<std::string> getArgsAsyncCB(const OperationPtr&, bool = false, bool = false); + + std::string getValue(const std::string&, const TypePtr&); + + void writeConstantValue(const std::string&, const TypePtr&, const SyntaxTreeBasePtr&, const std::string&); + + static StringList splitComment(const ContainedPtr&); + void writeDocComment(const ContainedPtr&, const std::string&, const std::string& = ""); + + ::IceUtilInternal::Output& _out; +}; + +class Gen : public JsGenerator +{ +public: + + Gen(const std::string&, + const std::vector<std::string>&, + const std::string&); + ~Gen(); + + void generate(const UnitPtr&); + void closeOutput(); + +private: + + IceUtilInternal::Output _out; + + std::vector<std::string> _includePaths; + std::string _fileBase; + + void printHeader(); + + class RequireVisitor : public JsVisitor + { + public: + + RequireVisitor(::IceUtilInternal::Output&, std::vector<std::string>); + + virtual bool visitClassDefStart(const ClassDefPtr&); + virtual bool visitStructStart(const StructPtr&); + virtual void visitOperation(const OperationPtr&); + virtual bool visitExceptionStart(const ExceptionPtr&); + virtual void visitSequence(const SequencePtr&); + virtual void visitDictionary(const DictionaryPtr&); + virtual void visitEnum(const EnumPtr&); + + std::vector< std::string> writeRequires(const UnitPtr&); + + private: + + bool _seenClass; + bool _seenCompactId; + bool _seenOperation; + bool _seenStruct; + bool _seenUserException; + bool _seenLocalException; + bool _seenEnum; + bool _seenObjectSeq; + bool _seenObjectDict; + std::vector<std::string> _includePaths; + }; + + class TypesVisitor : public JsVisitor + { + public: + + TypesVisitor(::IceUtilInternal::Output&, std::vector< 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&); + + private: + + std::string encodeTypeForOperation(const TypePtr&); + + std::vector<std::string> _seenModules; + }; + + class ExportVisitor : public JsVisitor + { + public: + + ExportVisitor(::IceUtilInternal::Output&); + + virtual bool visitModuleStart(const ModulePtr&); + }; +}; + +} + +#endif diff --git a/cpp/src/slice2js/JsUtil.cpp b/cpp/src/slice2js/JsUtil.cpp new file mode 100644 index 00000000000..8b637629c56 --- /dev/null +++ b/cpp/src/slice2js/JsUtil.cpp @@ -0,0 +1,723 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +// +// Disable unreferenced formal parameter warnings +// for VC90 binary_search. +// +#if defined(_MSC_VER) && (_MSC_VER <= 1500) +# pragma warning( push ) +# pragma warning( disable : 4100 ) +# include <algorithm> +# pragma warning( pop ) +#endif + +#include <JsUtil.h> +#include <Slice/Util.h> +#include <IceUtil/Functional.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef _WIN32 +#include <direct.h> +#endif + +#ifndef _WIN32 +#include <unistd.h> +#endif + +using namespace std; +using namespace Slice; +using namespace IceUtil; +using namespace IceUtilInternal; + +static string +lookupKwd(const string& name, bool mangleCasts = false) +{ + // + // Keyword list. *Must* be kept in alphabetical order. + // + static const string keywordList[] = + { + "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else", + "enum", "export", "extends", "false", "finally", "for", "function", "if", "implements", "import", "in", + "instanceof", "interface", "let", "new", "null", "package", "private", "protected", "public", "return", + "static", "super", "switch", "this", "throw", "true", "try", "typeof", "var", "void", "while", "with", + "yield" + }; + bool found = binary_search(&keywordList[0], + &keywordList[sizeof(keywordList) / sizeof(*keywordList)], + name, + Slice::CICompare()); + if(found) + { + return "_" + name; + } + if(mangleCasts && (name == "checkedCast" || name == "uncheckedCast")) + { + return "_" + name; + } + return name; +} + +// +// 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; +} + +static string +fixSuffix(const string& param) +{ + const string thisSuffix = "this."; + string p = param; + if(p.find(thisSuffix) == 0) + { + p = "self." + p.substr(thisSuffix.size()); + } + return p; +} + +bool +Slice::JsGenerator::isClassType(const TypePtr& type) +{ + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + return (builtin && builtin->kind() == Builtin::KindObject) || ClassDeclPtr::dynamicCast(type); +} + +// +// If the passed name is a scoped name, return the identical scoped name, +// but with all components that are JS keywords replaced by +// their "_"-prefixed version; otherwise, if the passed name is +// not scoped, but a JS keyword, return the "_"-prefixed name. +// +string +Slice::JsGenerator::fixId(const string& name, bool mangleCasts) +{ + if(name.empty()) + { + return name; + } + if(name[0] != ':') + { + return lookupKwd(name, mangleCasts); + } + + const StringList ids = splitScopedName(name); + const StringList newIds = fixIds(ids); + + stringstream result; + for(StringList::const_iterator j = newIds.begin(); j != newIds.end(); ++j) + { + if(j != newIds.begin()) + { + result << '.'; + } + result << *j; + } + return result.str(); +} + +string +Slice::JsGenerator::fixId(const ContainedPtr& cont, bool mangleCasts) +{ + return fixId(cont->name(), mangleCasts); +} + +string +Slice::JsGenerator::getOptionalFormat(const TypePtr& type) +{ + BuiltinPtr bp = BuiltinPtr::dynamicCast(type); + if(bp) + { + switch(bp->kind()) + { + case Builtin::KindByte: + case Builtin::KindBool: + { + return "Ice.OptionalFormat.F1"; + } + case Builtin::KindShort: + { + return "Ice.OptionalFormat.F2"; + } + case Builtin::KindInt: + case Builtin::KindFloat: + { + return "Ice.OptionalFormat.F4"; + } + case Builtin::KindLong: + case Builtin::KindDouble: + { + return "Ice.OptionalFormat.F8"; + } + case Builtin::KindString: + { + return "Ice.OptionalFormat.VSize"; + } + case Builtin::KindObject: + { + return "Ice.OptionalFormat.Class"; + } + case Builtin::KindObjectProxy: + { + return "Ice.OptionalFormat.FSize"; + } + case Builtin::KindLocalObject: + { + assert(false); + break; + } + } + } + + if(EnumPtr::dynamicCast(type)) + { + return "Ice.OptionalFormat.Size"; + } + + SequencePtr seq = SequencePtr::dynamicCast(type); + if(seq) + { + return seq->type()->isVariableLength() ? "Ice.OptionalFormat.FSize" : "Ice.OptionalFormat.VSize"; + } + + DictionaryPtr d = DictionaryPtr::dynamicCast(type); + if(d) + { + return (d->keyType()->isVariableLength() || d->valueType()->isVariableLength()) ? + "Ice.OptionalFormat.FSize" : "Ice.OptionalFormat.VSize"; + } + + StructPtr st = StructPtr::dynamicCast(type); + if(st) + { + return st->isVariableLength() ? "Ice.OptionalFormat.FSize" : "Ice.OptionalFormat.VSize"; + } + + if(ProxyPtr::dynamicCast(type)) + { + return "Ice.OptionalFormat.FSize"; + } + + ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type); + assert(cl); + return "Ice.OptionalFormat.Class"; +} + +string +Slice::JsGenerator::getStaticId(const TypePtr& type) +{ + BuiltinPtr b = BuiltinPtr::dynamicCast(type); + ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type); + + assert((b && b->kind() == Builtin::KindObject) || cl); + + if(b) + { + return "Ice.ObjectImpl.ice_staticId()"; + } + else if(cl->isInterface()) + { + ContainedPtr cont = ContainedPtr::dynamicCast(cl->container()); + assert(cont); + return fixId(cont->scoped()) + "." + cl->name() + "Disp_.ice_staticId()"; + } + else + { + return fixId(cl->scoped()) + ".ice_staticId()"; + } +} + +string +Slice::JsGenerator::typeToString(const TypePtr& type, bool optional) +{ + if(!type) + { + return "void"; + } + + static const char* builtinTable[] = + { + "Number", // byte + "Boolean", // bool + "Number", // short + "Number", // int + "Number", // long + "Number", // float + "Number", // double + "String", + "Ice.Object", + "Ice.ObjectPrx", + "Object" + }; + + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + return builtinTable[builtin->kind()]; + } + + ProxyPtr proxy = ProxyPtr::dynamicCast(type); + if(proxy) + { + return fixId(proxy->_class()->scoped() + "Prx"); + } + + SequencePtr seq = SequencePtr::dynamicCast(type); + if(seq) + { + return typeToString(seq->type()) + "[]"; + } + + DictionaryPtr d = DictionaryPtr::dynamicCast(type); + if(d) + { + return "Ice.HashMap"; + } + + ContainedPtr contained = ContainedPtr::dynamicCast(type); + if(contained) + { + return fixId(contained->scoped()); + } + + return "???"; +} + +string +Slice::JsGenerator::getLocalScope(const string& scope) +{ + 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 << '.'; + } + result << *i; + } + 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, + const string& param, + bool marshal) +{ + string stream = marshal ? "__os" : "__is"; + + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + switch(builtin->kind()) + { + case Builtin::KindByte: + { + if(marshal) + { + out << nl << stream << ".writeByte(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readByte()" << ';'; + } + return; + } + case Builtin::KindBool: + { + if(marshal) + { + out << nl << stream << ".writeBool(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readBool()" << ';'; + } + return; + } + case Builtin::KindShort: + { + if(marshal) + { + out << nl << stream << ".writeShort(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readShort()" << ';'; + } + return; + } + case Builtin::KindInt: + { + if(marshal) + { + out << nl << stream << ".writeInt(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readInt()" << ';'; + } + return; + } + case Builtin::KindLong: + { + if(marshal) + { + out << nl << stream << ".writeLong(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readLong()" << ';'; + } + return; + } + case Builtin::KindFloat: + { + if(marshal) + { + out << nl << stream << ".writeFloat(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readFloat()" << ';'; + } + return; + } + case Builtin::KindDouble: + { + if(marshal) + { + out << nl << stream << ".writeDouble(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readDouble()" << ';'; + } + return; + } + case Builtin::KindString: + { + if(marshal) + { + out << nl << stream << ".writeString(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readString()" << ';'; + } + return; + } + case Builtin::KindObject: + { + // Handle by isClassType below. + break; + } + case Builtin::KindObjectProxy: + { + if(marshal) + { + out << nl << stream << ".writeProxy(" << param << ");"; + } + else + { + out << nl << param << " = " << stream << ".readProxy();"; + } + return; + } + case Builtin::KindLocalObject: + { + assert(false); + return; + } + } + } + + if(EnumPtr::dynamicCast(type)) + { + if(marshal) + { + out << nl << typeToString(type) << ".__write(" << stream << ", " << param << ");"; + } + else + { + out << nl << param << " = " << typeToString(type) << ".__read(" << stream << ");"; + } + return; + } + + if(ProxyPtr::dynamicCast(type) || StructPtr::dynamicCast(type)) + { + if(marshal) + { + out << nl << typeToString(type) << ".write(" << stream << ", " << param << ");"; + } + else + { + out << nl << param << " = " << typeToString(type) << ".read(" << stream << ");"; + } + return; + } + + if(isClassType(type)) + { + if(marshal) + { + out << nl << stream << ".writeObject(" << param << ");"; + } + else + { + out << nl << stream << ".readObject(function(__o){ " << fixSuffix(param) << " = __o; }, " + << typeToString(type) << ");"; + } + return; + } + + if(SequencePtr::dynamicCast(type) || DictionaryPtr::dynamicCast(type)) + { + if(marshal) + { + out << nl << getHelper(type) <<".write(" << stream << ", " << param << ");"; + } + else + { + out << nl << param << " = " << getHelper(type) << ".read(" << stream << ");"; + } + return; + } + + assert(false); +} + +void +Slice::JsGenerator::writeOptionalMarshalUnmarshalCode(Output &out, + const TypePtr& type, + const string& param, + int tag, + bool marshal) +{ + string stream = marshal ? "__os" : "__is"; + + if(isClassType(type)) + { + if(marshal) + { + out << nl << stream << ".writeOptObject(" << tag << ", " << param << ");"; + } + else + { + out << nl << stream << ".readOptObject(" << tag << ", function(__o){ " << fixSuffix(param) + << " = __o; }, " << typeToString(type) << ");"; + } + return; + } + + if(EnumPtr::dynamicCast(type)) + { + if(marshal) + { + out << nl << typeToString(type) <<".__writeOpt(" << stream << ", " << tag << ", " << param << ");"; + } + else + { + out << nl << param << " = " << typeToString(type) << ".__readOpt(" << stream << ", " << tag << ");"; + } + return; + } + + if(marshal) + { + out << nl << getHelper(type) <<".writeOpt(" << stream << ", " << tag << ", " << param << ");"; + } + else + { + out << nl << param << " = " << getHelper(type) << ".readOpt(" << stream << ", " << tag << ");"; + } +} + +std::string +Slice::JsGenerator::getHelper(const TypePtr& type) +{ + BuiltinPtr builtin = BuiltinPtr::dynamicCast(type); + if(builtin) + { + switch(builtin->kind()) + { + case Builtin::KindByte: + { + return "Ice.ByteHelper"; + } + case Builtin::KindBool: + { + return "Ice.BoolHelper"; + } + case Builtin::KindShort: + { + return "Ice.ShortHelper"; + } + case Builtin::KindInt: + { + return "Ice.IntHelper"; + } + case Builtin::KindLong: + { + return "Ice.LongHelper"; + } + case Builtin::KindFloat: + { + return "Ice.FloatHelper"; + } + case Builtin::KindDouble: + { + return "Ice.DoubleHelper"; + } + case Builtin::KindString: + { + return "Ice.StringHelper"; + } + case Builtin::KindObject: + { + return "Ice.ObjectHelper"; + } + case Builtin::KindObjectProxy: + { + return "Ice.ObjectPrx"; + } + case Builtin::KindLocalObject: + { + assert(false); + break; + } + } + } + + if(EnumPtr::dynamicCast(type)) + { + return typeToString(type) + ".__helper"; + } + + if(ProxyPtr::dynamicCast(type) || StructPtr::dynamicCast(type)) + { + return typeToString(type); + } + + if(SequencePtr::dynamicCast(type) || DictionaryPtr::dynamicCast(type)) + { + stringstream s; + s << getLocalScope(ContainedPtr::dynamicCast(type)->scoped()) << "Helper"; + return s.str(); + } + + if(ClassDeclPtr::dynamicCast(type)) + { + return "Ice.ObjectHelper"; + } + + assert(false); + return "???"; +} diff --git a/cpp/src/slice2js/JsUtil.h b/cpp/src/slice2js/JsUtil.h new file mode 100644 index 00000000000..0368c62a5dc --- /dev/null +++ b/cpp/src/slice2js/JsUtil.h @@ -0,0 +1,51 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#ifndef JS_UTIL_H +#define JS_UTIL_H + +#include <Slice/Parser.h> +#include <IceUtil/OutputUtil.h> + +namespace Slice +{ + +class JsGenerator : private ::IceUtil::noncopyable +{ +public: + + virtual ~JsGenerator() {}; + +protected: + + static bool isClassType(const TypePtr&); + static std::string localProxyHelper(const TypePtr&); + static std::string fixId(const std::string&, bool = false); + static std::string fixId(const ContainedPtr&, bool = false); + static std::string getOptionalFormat(const TypePtr&); + static std::string getStaticId(const TypePtr&); + static std::string typeToString(const TypePtr&, bool = false); + static std::string getLocalScope(const std::string&); + static std::string getReference(const std::string&, const std::string&); + + static std::string getHelper(const TypePtr&); + // + // Generate code to marshal or unmarshal a type + // + void writeMarshalUnmarshalCode(::IceUtilInternal::Output&, const TypePtr&, const std::string&, bool); + void writeOptionalMarshalUnmarshalCode(::IceUtilInternal::Output&, const TypePtr&, const std::string&, int, bool); + +private: + + std::vector< std::string> _seenProxy; +}; + +} + +#endif diff --git a/cpp/src/slice2js/Main.cpp b/cpp/src/slice2js/Main.cpp new file mode 100644 index 00000000000..456d01dcbb1 --- /dev/null +++ b/cpp/src/slice2js/Main.cpp @@ -0,0 +1,304 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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/Options.h> +#include <IceUtil/CtrlCHandler.h> +#include <IceUtil/Mutex.h> +#include <IceUtil/MutexPtrLock.h> +#include <Slice/Preprocessor.h> +#include <Slice/FileTracker.h> +#include <Slice/Util.h> +#include <Gen.h> + +using namespace std; +using namespace Slice; + +namespace +{ + +IceUtil::Mutex* globalMutex = 0; +bool interrupted = false; + +class Init +{ +public: + + Init() + { + globalMutex = new IceUtil::Mutex; + } + + ~Init() + { + delete globalMutex; + globalMutex = 0; + } +}; + +Init init; + +} + +void +interruptedCallback(int /*signal*/) +{ + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex); + + interrupted = true; +} + +void +usage(const char* n) +{ + getErrorStream() << "Usage: " << n << " [options] slice-files...\n"; + getErrorStream() << + "Options:\n" + "-h, --help Show this message.\n" + "-v, --version Display the Ice version.\n" + "-DNAME Define NAME as 1.\n" + "-DNAME=DEF Define NAME as DEF.\n" + "-UNAME Remove any definition for NAME.\n" + "-IDIR Put DIR in the include file search path.\n" + "-E Print preprocessor output on stdout.\n" + "--output-dir DIR Create files in the directory DIR.\n" + "--depend Generate Makefile dependencies.\n" + "-d, --debug Print debug messages.\n" + "--ice Permit `Ice' prefix (for building Ice source code only).\n" + "--underscore Permit underscores in Slice identifiers.\n" + ; +} + +int +compile(int argc, char* argv[]) +{ + IceUtilInternal::Options opts; + opts.addOpt("h", "help"); + opts.addOpt("v", "version"); + opts.addOpt("D", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat); + opts.addOpt("U", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat); + opts.addOpt("I", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat); + opts.addOpt("E"); + opts.addOpt("", "output-dir", IceUtilInternal::Options::NeedArg); + opts.addOpt("", "depend"); + opts.addOpt("d", "debug"); + opts.addOpt("", "ice"); + opts.addOpt("", "underscore"); + + vector<string> args; + try + { + args = opts.parse(argc, (const char**)argv); + } + catch(const IceUtilInternal::BadOptException& e) + { + getErrorStream() << argv[0] << ": error: " << e.reason << endl; + usage(argv[0]); + return EXIT_FAILURE; + } + + if(opts.isSet("help")) + { + usage(argv[0]); + return EXIT_SUCCESS; + } + + if(opts.isSet("version")) + { + getErrorStream() << ICE_STRING_VERSION << endl; + return EXIT_SUCCESS; + } + + vector<string> cppArgs; + vector<string> optargs = opts.argVec("D"); + for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i) + { + cppArgs.push_back("-D" + *i); + } + + optargs = opts.argVec("U"); + for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i) + { + cppArgs.push_back("-U" + *i); + } + + vector<string> includePaths = opts.argVec("I"); + for(vector<string>::const_iterator i = includePaths.begin(); i != includePaths.end(); ++i) + { + cppArgs.push_back("-I" + Preprocessor::normalizeIncludePath(*i)); + } + + bool preprocess = opts.isSet("E"); + + string output = opts.optArg("output-dir"); + + bool depend = opts.isSet("depend"); + + bool debug = opts.isSet("debug"); + + bool ice = opts.isSet("ice"); + + bool underscore = opts.isSet("underscore"); + + if(args.empty()) + { + getErrorStream() << argv[0] << ": error: no input file" << endl; + usage(argv[0]); + return EXIT_FAILURE; + } + + int status = EXIT_SUCCESS; + + IceUtil::CtrlCHandler ctrlCHandler; + ctrlCHandler.setCallback(interruptedCallback); + + for(vector<string>::const_iterator i = args.begin(); i != args.end(); ++i) + { + // + // Ignore duplicates. + // + vector<string>::iterator p = find(args.begin(), args.end(), *i); + if(p != i) + { + continue; + } + + if(depend) + { + PreprocessorPtr icecpp = Preprocessor::create(argv[0], *i, cppArgs); + FILE* cppHandle = icecpp->preprocess(false, "-D__SLICE2CS__"); + + if(cppHandle == 0) + { + return EXIT_FAILURE; + } + + UnitPtr u = Unit::createUnit(false, false, ice, underscore); + int parseStatus = u->parse(*i, cppHandle, debug); + u->destroy(); + + if(parseStatus == EXIT_FAILURE) + { + return EXIT_FAILURE; + } + + if(!icecpp->printMakefileDependencies(Preprocessor::JS, includePaths, + "-D__SLICE2JS__")) + { + return EXIT_FAILURE; + } + + if(!icecpp->close()) + { + return EXIT_FAILURE; + } + } + 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]; + while(fgets(buf, static_cast<int>(sizeof(buf)), cppHandle) != NULL) + { + if(fputs(buf, stdout) == EOF) + { + return EXIT_FAILURE; + } + } + if(!icecpp->close()) + { + return EXIT_FAILURE; + } + } + else + { + UnitPtr p = Unit::createUnit(false, false, ice, underscore); + int parseStatus = p->parse(*i, cppHandle, debug); + + if(!icecpp->close()) + { + p->destroy(); + return EXIT_FAILURE; + } + + if(parseStatus == EXIT_FAILURE) + { + status = EXIT_FAILURE; + } + else + { + try + { + Gen gen(icecpp->getBaseName(), includePaths, output); + gen.generate(p); + } + catch(const Slice::FileException& ex) + { + // + // If a file could not be created, then clean up any created files. + // + FileTracker::instance()->cleanup(); + p->destroy(); + getErrorStream() << argv[0] << ": error: " << ex.reason() << endl; + return EXIT_FAILURE; + } + } + + p->destroy(); + } + } + + { + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex); + + if(interrupted) + { + FileTracker::instance()->cleanup(); + return EXIT_FAILURE; + } + } + } + + return status; +} + +int +main(int argc, char* argv[]) +{ + try + { + return compile(argc, argv); + } + catch(const std::exception& ex) + { + getErrorStream() << argv[0] << ": error:" << ex.what() << endl; + return EXIT_FAILURE; + } + catch(const std::string& msg) + { + getErrorStream() << argv[0] << ": error:" << msg << endl; + return EXIT_FAILURE; + } + catch(const char* msg) + { + getErrorStream() << argv[0] << ": error:" << msg << endl; + return EXIT_FAILURE; + } + catch(...) + { + getErrorStream() << argv[0] << ": error:" << "unknown exception" << endl; + return EXIT_FAILURE; + } +} diff --git a/cpp/src/slice2js/Makefile b/cpp/src/slice2js/Makefile new file mode 100644 index 00000000000..cd1ae09a511 --- /dev/null +++ b/cpp/src/slice2js/Makefile @@ -0,0 +1,40 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 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. +# +# ********************************************************************** + +top_srcdir = ../.. + +NAME = $(bindir)/slice2js + +TARGETS = $(NAME) + +OBJS = Gen.o \ + JsUtil.o \ + Main.o + +SRCS = $(OBJS:.o=.cpp) + +ifeq ($(CPP11),yes) +RPATH_DIR = @loader_path/../../lib/c++11 +else +RPATH_DIR = @loader_path/../lib +endif + +include $(top_srcdir)/config/Make.rules + +CPPFLAGS := -I. $(CPPFLAGS) + +$(NAME): $(OBJS) + rm -f $@ + $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(SLICE_LIBS) $(MCPP_RPATH_LINK) + +install:: all + $(call installprogram,$(NAME),$(DESTDIR)$(install_bindir)) + $(call installdata,$(top_srcdir)/../man/man1/slice2js.1,$(DESTDIR)$(install_mandir)) + +include .depend diff --git a/cpp/src/slice2js/Makefile.mak b/cpp/src/slice2js/Makefile.mak new file mode 100644 index 00000000000..58bf485669a --- /dev/null +++ b/cpp/src/slice2js/Makefile.mak @@ -0,0 +1,52 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2014 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. +# +# ********************************************************************** + +top_srcdir = ..\.. + +NAME = $(top_srcdir)\bin\slice2js.exe + +TARGETS = $(NAME) + +OBJS = Gen.obj \ + JsUtil.obj \ + Main.obj + +SRCS = $(OBJS:.obj=.cpp) + +!include $(top_srcdir)/config/Make.rules.mak + +CPPFLAGS = -I. $(CPPFLAGS) -DWIN32_LEAN_AND_MEAN + +!if "$(GENERATE_PDB)" == "yes" +PDBFLAGS = /pdb:$(NAME:.exe=.pdb) +!endif + +RES_FILE = Slice2Js.res + +$(NAME): $(OBJS) Slice2Js.res + $(LINK) $(LD_EXEFLAGS) $(PDBFLAGS) $(OBJS) $(SETARGV) $(PREOUT)$@ $(PRELIBS)slice$(LIBSUFFIX).lib \ + $(BASELIBS) $(RES_FILE) + @if exist $@.manifest echo ^ ^ ^ Embedding manifest using $(MT) && \ + $(MT) -nologo -manifest $@.manifest -outputresource:$@;#1 && del /q $@.manifest + +clean:: + del /q $(NAME:.exe=.*) + del /q Slice2Js.res + +install:: all + copy $(NAME) "$(install_bindir)" + +!if "$(GENERATE_PDB)" == "yes" + +install:: all + copy $(NAME:.exe=.pdb) "$(install_bindir)" + +!endif + +!include .depend.mak diff --git a/cpp/src/slice2js/Slice2Js.rc b/cpp/src/slice2js/Slice2Js.rc new file mode 100644 index 00000000000..d24779820df --- /dev/null +++ b/cpp/src/slice2js/Slice2Js.rc @@ -0,0 +1,34 @@ +#include "winver.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,5,1,0 + PRODUCTVERSION 3,5,1,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "ZeroC, Inc.\0" + VALUE "FileDescription", "Slice To JavaScript Translator\0" + VALUE "FileVersion", "3.5.1\0" + VALUE "InternalName", "slice2js\0" + VALUE "LegalCopyright", "Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.\0" + VALUE "OriginalFilename", "slice2js.exe\0" + VALUE "ProductName", "Ice\0" + VALUE "ProductVersion", "3.5.1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END |