summaryrefslogtreecommitdiff
path: root/cpp/src/slice2js
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/slice2js')
-rw-r--r--cpp/src/slice2js/.depend3
-rw-r--r--cpp/src/slice2js/.depend.mak3
-rw-r--r--cpp/src/slice2js/Gen.cpp1782
-rw-r--r--cpp/src/slice2js/Gen.h136
-rw-r--r--cpp/src/slice2js/JsUtil.cpp723
-rw-r--r--cpp/src/slice2js/JsUtil.h51
-rw-r--r--cpp/src/slice2js/Main.cpp304
-rw-r--r--cpp/src/slice2js/Makefile40
-rw-r--r--cpp/src/slice2js/Makefile.mak52
-rw-r--r--cpp/src/slice2js/Slice2Js.rc34
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