diff options
author | Joe George <joe@zeroc.com> | 2020-07-07 16:57:51 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-07 16:57:51 -0400 |
commit | 6c0e7e6fcabde691e7c38a814b6171f9f4e77d09 (patch) | |
tree | aed41fdff6561e134c73da214e580be0910e6f6a | |
parent | Copy python dependencies to the extension directory - Close #926 (#927) (diff) | |
download | ice-6c0e7e6fcabde691e7c38a814b6171f9f4e77d09.tar.bz2 ice-6c0e7e6fcabde691e7c38a814b6171f9f4e77d09.tar.xz ice-6c0e7e6fcabde691e7c38a814b6171f9f4e77d09.zip |
Add class cycle detection during unmarshaling (#946)
Add support for detection of class cycles during unmarshaling in
languages which do no have garbage collection: C++, Swift, and Objective-C.
A `MarshalException` is thrown when a cycle is detected.
The property `Ice.AcceptClassCycles` can be set to a value greater than `0`
to change this behavior.
71 files changed, 477 insertions, 64 deletions
diff --git a/CHANGELOG-3.7.md b/CHANGELOG-3.7.md index 54517319da2..b8bf240dee4 100644 --- a/CHANGELOG-3.7.md +++ b/CHANGELOG-3.7.md @@ -8,54 +8,69 @@ We recommend that you use the release notes as a guide for migrating your applications to this release, and the manual for complete details on a particular aspect of Ice. +- [Changes in Ice 3.7.5](#changes-in-ice-375) + - [General Changes](#general-changes) - [Changes in Ice 3.7.4](#changes-in-ice-374) - - [General Changes](#general-changes) - - [C++ Changes](#c-changes) - - [C# Changes](#c-changes-1) - - [JavaScript Changes](#javascript-changes) - - [MATLAB Changes](#matlab-changes) - - [Python Changes](#python-changes) - - [Ruby Changes](#ruby-changes) - - [Swift Changes](#swift-changes) + - [General Changes](#general-changes-1) + - [C++ Changes](#c-changes) + - [C# Changes](#c-changes-1) + - [JavaScript Changes](#javascript-changes) + - [MATLAB Changes](#matlab-changes) + - [Python Changes](#python-changes) + - [Ruby Changes](#ruby-changes) + - [Swift Changes](#swift-changes) - [Changes in Ice 3.7.3](#changes-in-ice-373) - - [General Changes](#general-changes-1) - - [C++ Changes](#c-changes-2) - - [C# Changes](#c-changes-3) - - [Java Changes](#java-changes) - - [JavaScript Changes](#javascript-changes-1) - - [MATLAB Changes](#matlab-changes-1) - - [Python Changes](#python-changes-1) + - [General Changes](#general-changes-2) + - [C++ Changes](#c-changes-2) + - [C# Changes](#c-changes-3) + - [Java Changes](#java-changes) + - [JavaScript Changes](#javascript-changes-1) + - [MATLAB Changes](#matlab-changes-1) + - [Python Changes](#python-changes-1) - [Changes in Ice 3.7.2](#changes-in-ice-372) - - [General Changes](#general-changes-2) - - [C++ Changes](#c-changes-4) - - [C# Changes](#c-changes-5) - - [Java Changes](#java-changes-1) - - [JavaScript Changes](#javascript-changes-2) - - [MATLAB Changes](#matlab-changes-2) - - [Objective-C Changes](#objective-c-changes) - - [PHP Changes](#php-changes) - - [Python Changes](#python-changes-2) + - [General Changes](#general-changes-3) + - [C++ Changes](#c-changes-4) + - [C# Changes](#c-changes-5) + - [Java Changes](#java-changes-1) + - [JavaScript Changes](#javascript-changes-2) + - [MATLAB Changes](#matlab-changes-2) + - [Objective-C Changes](#objective-c-changes) + - [PHP Changes](#php-changes) + - [Python Changes](#python-changes-2) - [Changes in Ice 3.7.1](#changes-in-ice-371) - - [General Changes](#general-changes-3) - - [C++ Changes](#c-changes-6) - - [C# Changes](#c-changes-7) - - [Java Changes](#java-changes-2) - - [JavaScript Changes](#javascript-changes-3) - - [MATLAB Changes](#matlab-changes-3) - - [Objective-C Changes](#objective-c-changes-1) - - [PHP Changes](#php-changes-1) - - [Python Changes](#python-changes-3) - - [Ruby Changes](#ruby-changes-1) + - [General Changes](#general-changes-4) + - [C++ Changes](#c-changes-6) + - [C# Changes](#c-changes-7) + - [Java Changes](#java-changes-2) + - [JavaScript Changes](#javascript-changes-3) + - [MATLAB Changes](#matlab-changes-3) + - [Objective-C Changes](#objective-c-changes-1) + - [PHP Changes](#php-changes-1) + - [Python Changes](#python-changes-3) + - [Ruby Changes](#ruby-changes-1) - [Changes in Ice 3.7.0](#changes-in-ice-370) - - [General Changes](#general-changes-4) - - [C++ Changes](#c-changes-8) - - [C# Changes](#c-changes-9) - - [Java Changes](#java-changes-3) - - [JavaScript Changes](#javascript-changes-4) - - [Objective-C Changes](#objective-c-changes-2) - - [PHP Changes](#php-changes-2) - - [Python Changes](#python-changes-4) - - [Ruby Changes](#ruby-changes-2) + - [General Changes](#general-changes-5) + - [C++ Changes](#c-changes-8) + - [C# Changes](#c-changes-9) + - [Java Changes](#java-changes-3) + - [JavaScript Changes](#javascript-changes-4) + - [Objective-C Changes](#objective-c-changes-2) + - [PHP Changes](#php-changes-2) + - [Python Changes](#python-changes-4) + - [Ruby Changes](#ruby-changes-2) + +# Changes in Ice 3.7.5 + +These are the changes since Ice 3.7.4. + +## General Changes + +- Add support for detection of class cycles during unmarshaling in + languages which do not have garbage collection: C++, Swift, and Objective-C. + A `MarshalException` is thrown when a cycle is detected. + + The property `Ice.AcceptClassCycles` can be set to a value greater than `0` + to change this behavior. # Changes in Ice 3.7.4 diff --git a/config/PropertyNames.xml b/config/PropertyNames.xml index f98f28f834e..d494f75490c 100644 --- a/config/PropertyNames.xml +++ b/config/PropertyNames.xml @@ -300,6 +300,7 @@ generated from the section label. </class> <section name="Ice"> + <property name="AcceptClassCycles" /> <property name="ACM.Client" deprecated="true"/> <property name="ACM.Server" deprecated="true"/> <property name="ACM" class="acm"/> diff --git a/cpp/src/Ice/InputStream.cpp b/cpp/src/Ice/InputStream.cpp index d0c4c91d9fc..3c4686e979d 100644 --- a/cpp/src/Ice/InputStream.cpp +++ b/cpp/src/Ice/InputStream.cpp @@ -1819,12 +1819,17 @@ Ice::InputStream::EncapsDecoder::addPatchEntry(Int index, PatchFunc patchFunc, v assert(index > 0); // - // Check if we already unmarshaled the object. If that's the case, - // just patch the object smart pointer and we're done. + // Check if we already unmarshaled the object. If that's the case, just patch the object smart pointer + // and we're done. A null value indicates we've encountered a cycle and Ice.AllowClassCycles is false. // IndexToPtrMap::iterator p = _unmarshaledMap.find(index); if(p != _unmarshaledMap.end()) { + if (p->second == ICE_NULLPTR) + { + assert(!_stream->_instance->acceptClassCycles()); + throw MarshalException(__FILE__, __LINE__, "cycle detected during Value unmarshaling"); + } (*patchFunc)(patchAddr, p->second); return; } @@ -1862,7 +1867,10 @@ Ice::InputStream::EncapsDecoder::unmarshal(Int index, const Ice::ValuePtr& v) // Add the object to the map of unmarshaled instances, this must // be done before reading the instances (for circular references). // - _unmarshaledMap.insert(make_pair(index, v)); + // If circular references are not allowed we insert null (for cycle detection) and add + // the object to the map once it has been fully unmarshaled. + // + _unmarshaledMap.insert(make_pair(index, _stream->_instance->acceptClassCycles() ? v : Ice::ValuePtr())); // // Read the object. @@ -1915,6 +1923,13 @@ Ice::InputStream::EncapsDecoder::unmarshal(Int index, const Ice::ValuePtr& v) _valueList.clear(); } } + + if(!_stream->_instance->acceptClassCycles()) + { + // This class has been fully unmarshaled without creating any cycles + // It can be added to the map now. + _unmarshaledMap[index] = v; + } } void diff --git a/cpp/src/Ice/Instance.cpp b/cpp/src/Ice/Instance.cpp index e0c06b52a9b..7a1b5952157 100644 --- a/cpp/src/Ice/Instance.cpp +++ b/cpp/src/Ice/Instance.cpp @@ -950,6 +950,7 @@ IceInternal::Instance::Instance(const CommunicatorPtr& communicator, const Initi _classGraphDepthMax(0), _collectObjects(false), _toStringMode(ICE_ENUM(ToStringMode, Unicode)), + _acceptClassCycles(false), _implicitContext(0), _stringConverter(Ice::getProcessStringConverter()), _wstringConverter(Ice::getProcessWstringConverter()), @@ -1227,6 +1228,8 @@ IceInternal::Instance::Instance(const CommunicatorPtr& communicator, const Initi throw InitializationException(__FILE__, __LINE__, "The value for Ice.ToStringMode must be Unicode, ASCII or Compat"); } + const_cast<bool&>(_acceptClassCycles) = _initData.properties->getPropertyAsInt("Ice.AcceptClassCycles") > 0; + const_cast<ImplicitContextIPtr&>(_implicitContext) = ImplicitContextI::create(_initData.properties->getProperty("Ice.ImplicitContext")); diff --git a/cpp/src/Ice/Instance.h b/cpp/src/Ice/Instance.h index 70e9c249e42..150167190d8 100644 --- a/cpp/src/Ice/Instance.h +++ b/cpp/src/Ice/Instance.h @@ -106,6 +106,7 @@ public: size_t classGraphDepthMax() const { return _classGraphDepthMax; } bool collectObjects() const { return _collectObjects; } Ice::ToStringMode toStringMode() const { return _toStringMode; } + bool acceptClassCycles() const { return _acceptClassCycles; } const ACMConfig& clientACM() const; const ACMConfig& serverACM() const; @@ -175,6 +176,7 @@ private: const size_t _classGraphDepthMax; // Immutable, not reset by destroy(). const bool _collectObjects; // Immutable, not reset by destroy(). const Ice::ToStringMode _toStringMode; // Immutable, not reset by destroy() + const bool _acceptClassCycles; // Immutable, not reset by destroy() ACMConfig _clientACM; ACMConfig _serverACM; RouterManagerPtr _routerManager; diff --git a/cpp/src/Ice/PropertyNames.cpp b/cpp/src/Ice/PropertyNames.cpp index 9d332d8618a..da4d0af3534 100644 --- a/cpp/src/Ice/PropertyNames.cpp +++ b/cpp/src/Ice/PropertyNames.cpp @@ -1,7 +1,7 @@ // // Copyright (c) ZeroC, Inc. All rights reserved. // -// Generated by makeprops.py from file ./config/PropertyNames.xml, Fri Sep 6 18:11:04 2019 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Thu Jul 2 14:55:02 2020 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -9,6 +9,7 @@ const IceInternal::Property IcePropsData[] = { + IceInternal::Property("Ice.AcceptClassCycles", false, 0), IceInternal::Property("Ice.ACM.Client", true, 0), IceInternal::Property("Ice.ACM.Server", true, 0), IceInternal::Property("Ice.ACM.Timeout", false, 0), diff --git a/cpp/src/Ice/PropertyNames.h b/cpp/src/Ice/PropertyNames.h index 9c057af4afe..8f65258d805 100644 --- a/cpp/src/Ice/PropertyNames.h +++ b/cpp/src/Ice/PropertyNames.h @@ -1,7 +1,7 @@ // // Copyright (c) ZeroC, Inc. All rights reserved. // -// Generated by makeprops.py from file ./config/PropertyNames.xml, Fri Sep 6 18:11:04 2019 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Thu Jul 2 14:55:02 2020 // IMPORTANT: Do not edit this file -- any edits made here will be lost! diff --git a/cpp/test/Ice/objects/AllTests.cpp b/cpp/test/Ice/objects/AllTests.cpp index 65134a2c49e..c84b73d51a9 100644 --- a/cpp/test/Ice/objects/AllTests.cpp +++ b/cpp/test/Ice/objects/AllTests.cpp @@ -604,5 +604,25 @@ allTests(Test::TestHelper* helper) } cout << "ok" << endl; + cout << "testing sending class cycle... " << flush; + { + RecursivePtr rec = ICE_MAKE_SHARED(Recursive); + rec->v = rec; + bool acceptsCycles = initial->acceptsClassCycles(); + try + { + initial->setCycle(rec); + test(acceptsCycles); + } + catch(const Ice::UnknownLocalException&) + { + // expected when the remote server does not accept cycles + // and throws a MarshalException + test(!acceptsCycles); + } + rec->v = ICE_NULLPTR; + } + cout << "ok" << endl; + return initial; } diff --git a/cpp/test/Ice/objects/Client.cpp b/cpp/test/Ice/objects/Client.cpp index 4d5dc868b83..a27c51e17f6 100644 --- a/cpp/test/Ice/objects/Client.cpp +++ b/cpp/test/Ice/objects/Client.cpp @@ -118,7 +118,7 @@ void Client::run(int argc, char** argv) { Ice::PropertiesPtr properties = createTestProperties(argc, argv); - + properties->setProperty("Ice.AcceptClassCycles", "1"); #ifndef ICE_CPP11_MAPPING properties->setProperty("Ice.CollectObjects", "1"); #endif diff --git a/cpp/test/Ice/objects/Collocated.cpp b/cpp/test/Ice/objects/Collocated.cpp index eb4434fbe39..0633cfa5d13 100644 --- a/cpp/test/Ice/objects/Collocated.cpp +++ b/cpp/test/Ice/objects/Collocated.cpp @@ -110,6 +110,7 @@ void Collocated::run(int argc, char** argv) { Ice::PropertiesPtr properties = createTestProperties(argc, argv); + properties->setProperty("Ice.AcceptClassCycles", "1"); #ifndef ICE_CPP11_MAPPING properties->setProperty("Ice.CollectObjects", "1"); #endif diff --git a/cpp/test/Ice/objects/Test.ice b/cpp/test/Ice/objects/Test.ice index 38214e39951..a89b9beb0e8 100644 --- a/cpp/test/Ice/objects/Test.ice +++ b/cpp/test/Ice/objects/Test.ice @@ -222,6 +222,9 @@ interface Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/cpp/test/Ice/objects/TestI.cpp b/cpp/test/Ice/objects/TestI.cpp index 9cfd4c395eb..abf4154e59e 100644 --- a/cpp/test/Ice/objects/TestI.cpp +++ b/cpp/test/Ice/objects/TestI.cpp @@ -187,6 +187,20 @@ InitialI::supportsClassGraphDepthMax(const Ice::Current&) return true; } +void +InitialI::setCycle(ICE_IN(RecursivePtr) r, const Ice::Current&) +{ + // break the cycle + assert(r); + r->v = ICE_NULLPTR; +} + +bool +InitialI::acceptsClassCycles(const Ice::Current& c) +{ + return c.adapter->getCommunicator()->getProperties()->getPropertyAsInt("Ice.AcceptClassCycles") > 0; +} + #ifdef ICE_CPP11_MAPPING InitialI::GetMBMarshaledResult InitialI::getMB(const Ice::Current& current) diff --git a/cpp/test/Ice/objects/TestI.h b/cpp/test/Ice/objects/TestI.h index 3b265a19664..78874dbb91b 100644 --- a/cpp/test/Ice/objects/TestI.h +++ b/cpp/test/Ice/objects/TestI.h @@ -93,6 +93,9 @@ public: virtual void setRecursive(ICE_IN(Test::RecursivePtr), const Ice::Current&); virtual bool supportsClassGraphDepthMax(const Ice::Current&); + virtual void setCycle(ICE_IN(Test::RecursivePtr), const Ice::Current&); + virtual bool acceptsClassCycles(const Ice::Current&); + #ifdef ICE_CPP11_MAPPING virtual GetMBMarshaledResult getMB(const Ice::Current&); virtual void getAMDMBAsync(std::function<void(const GetAMDMBMarshaledResult&)>, diff --git a/cpp/test/Ice/optional/AllTests.cpp b/cpp/test/Ice/optional/AllTests.cpp index 7b00e0a421c..369d2228cc6 100644 --- a/cpp/test/Ice/optional/AllTests.cpp +++ b/cpp/test/Ice/optional/AllTests.cpp @@ -587,6 +587,7 @@ allTests(Test::TestHelper* helper, bool) mo1->k = mo1; MultiOptionalPtr mo5 = ICE_DYNAMIC_CAST(MultiOptional, initial->pingPong(mo1)); + test(mo5->a == mo1->a); test(mo5->b == mo1->b); test(mo5->c == mo1->c); diff --git a/cpp/test/Ice/optional/Client.cpp b/cpp/test/Ice/optional/Client.cpp index 1f2bb2edb45..4806e2f02cf 100644 --- a/cpp/test/Ice/optional/Client.cpp +++ b/cpp/test/Ice/optional/Client.cpp @@ -20,6 +20,7 @@ void Client::run(int argc, char** argv) { Ice::PropertiesPtr properties = createTestProperties(argc, argv); + properties->setProperty("Ice.AcceptClassCycles", "1"); #ifndef ICE_CPP11_MAPPING properties->setProperty("Ice.CollectObjects", "1"); #endif diff --git a/cpp/test/Ice/optional/Server.cpp b/cpp/test/Ice/optional/Server.cpp index f526d0355d9..a84d6694b7e 100644 --- a/cpp/test/Ice/optional/Server.cpp +++ b/cpp/test/Ice/optional/Server.cpp @@ -20,6 +20,7 @@ void Server::run(int argc, char** argv) { Ice::PropertiesPtr properties = createTestProperties(argc, argv); + properties->setProperty("Ice.AcceptClassCycles", "1"); #ifndef ICE_CPP11_MAPPING properties->setProperty("Ice.CollectObjects", "1"); #endif diff --git a/cpp/test/Ice/optional/ServerAMD.cpp b/cpp/test/Ice/optional/ServerAMD.cpp index bdc012694ce..f9d16fed20f 100644 --- a/cpp/test/Ice/optional/ServerAMD.cpp +++ b/cpp/test/Ice/optional/ServerAMD.cpp @@ -19,6 +19,7 @@ void ServerAMD::run(int argc, char** argv) { Ice::PropertiesPtr properties = createTestProperties(argc, argv); + properties->setProperty("Ice.AcceptClassCycles", "1"); #ifndef ICE_CPP11_MAPPING properties->setProperty("Ice.CollectObjects", "1"); #endif diff --git a/cpp/test/Ice/slicing/objects/Client.cpp b/cpp/test/Ice/slicing/objects/Client.cpp index 537d7e9d216..b9f217e495d 100644 --- a/cpp/test/Ice/slicing/objects/Client.cpp +++ b/cpp/test/Ice/slicing/objects/Client.cpp @@ -23,6 +23,7 @@ Client::run(int argc, char** argv) // // For this test, we enable object collection. // + properties->setProperty("Ice.AcceptClassCycles", "1"); properties->setProperty("Ice.CollectObjects", "1"); Ice::CommunicatorHolder communicator = initialize(argc, argv, properties); diff --git a/cpp/test/Ice/slicing/objects/Server.cpp b/cpp/test/Ice/slicing/objects/Server.cpp index f1cd93e6ae3..b7ca13be31b 100644 --- a/cpp/test/Ice/slicing/objects/Server.cpp +++ b/cpp/test/Ice/slicing/objects/Server.cpp @@ -19,6 +19,7 @@ void Server::run(int argc, char** argv) { Ice::PropertiesPtr properties = createTestProperties(argc, argv); + properties->setProperty("Ice.AcceptClassCycles", "1"); #ifndef ICE_CPP11_MAPPING properties->setProperty("Ice.CollectObjects", "1"); #endif diff --git a/cpp/test/Ice/slicing/objects/ServerAMD.cpp b/cpp/test/Ice/slicing/objects/ServerAMD.cpp index ab4df8a0f5e..e7e60f6998f 100644 --- a/cpp/test/Ice/slicing/objects/ServerAMD.cpp +++ b/cpp/test/Ice/slicing/objects/ServerAMD.cpp @@ -19,6 +19,7 @@ void ServerAMD::run(int argc, char** argv) { Ice::PropertiesPtr properties = createTestProperties(argc, argv); + properties->setProperty("Ice.AcceptClassCycles", "1"); #ifndef ICE_CPP11_MAPPING properties->setProperty("Ice.CollectObjects", "1"); #endif diff --git a/cpp/test/Ice/stream/Client.cpp b/cpp/test/Ice/stream/Client.cpp index 23a2be692df..879b93ffb61 100644 --- a/cpp/test/Ice/stream/Client.cpp +++ b/cpp/test/Ice/stream/Client.cpp @@ -1365,7 +1365,9 @@ public: void Client::run(int argc, char** argv) { - Ice::CommunicatorHolder communicator = initialize(argc, argv); + Ice::PropertiesPtr properties = createTestProperties(argc, argv); + properties->setProperty("Ice.AcceptClassCycles", "1"); + Ice::CommunicatorHolder communicator = initialize(argc, argv, properties); void allTests(Test::TestHelper*); allTests(this); } diff --git a/csharp/src/Ice/PropertyNames.cs b/csharp/src/Ice/PropertyNames.cs index a4b0440ead6..d8926852333 100644 --- a/csharp/src/Ice/PropertyNames.cs +++ b/csharp/src/Ice/PropertyNames.cs @@ -1,7 +1,7 @@ // // Copyright (c) ZeroC, Inc. All rights reserved. // -// Generated by makeprops.py from file ./config/PropertyNames.xml, Fri Sep 6 18:11:04 2019 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Thu Jul 2 14:55:02 2020 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -11,6 +11,7 @@ namespace IceInternal { public static Property[] IceProps = { + new Property(@"^Ice\.AcceptClassCycles$", false, null), new Property(@"^Ice\.ACM\.Client$", true, null), new Property(@"^Ice\.ACM\.Server$", true, null), new Property(@"^Ice\.ACM\.Timeout$", false, null), diff --git a/csharp/test/Ice/objects/AllTests.cs b/csharp/test/Ice/objects/AllTests.cs index d0cfb08c6ba..abfa3785dc0 100644 --- a/csharp/test/Ice/objects/AllTests.cs +++ b/csharp/test/Ice/objects/AllTests.cs @@ -538,6 +538,25 @@ namespace Ice } output.WriteLine("ok"); + output.Write("testing sending class cycle... "); + output.Flush(); + { + var rec = new Test.Recursive(); + rec.v = rec; + var acceptsCycles = initial.acceptsClassCycles(); + try + { + initial.setCycle(rec); + test(acceptsCycles); + } + catch(Ice.UnknownLocalException) + { + test(!acceptsCycles); + } + + } + output.WriteLine("ok"); + return initial; } } diff --git a/csharp/test/Ice/objects/InitialI.cs b/csharp/test/Ice/objects/InitialI.cs index 19e34a228d8..0aa1a0451f6 100644 --- a/csharp/test/Ice/objects/InitialI.cs +++ b/csharp/test/Ice/objects/InitialI.cs @@ -123,6 +123,15 @@ namespace Ice return true; } + public override void setCycle(Test.Recursive r, Ice.Current current) + { + } + + public override bool acceptsClassCycles(Ice.Current current) + { + return true; + } + public override Test.D1 getD1(Test.D1 d1, Ice.Current current) { return d1; diff --git a/csharp/test/Ice/objects/Test.ice b/csharp/test/Ice/objects/Test.ice index 9fda575741c..9bcea5b2eaa 100644 --- a/csharp/test/Ice/objects/Test.ice +++ b/csharp/test/Ice/objects/Test.ice @@ -216,6 +216,9 @@ interface Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/java-compat/src/Ice/src/main/java/IceInternal/PropertyNames.java b/java-compat/src/Ice/src/main/java/IceInternal/PropertyNames.java index 36daa5282b8..b974493637b 100644 --- a/java-compat/src/Ice/src/main/java/IceInternal/PropertyNames.java +++ b/java-compat/src/Ice/src/main/java/IceInternal/PropertyNames.java @@ -1,7 +1,7 @@ // // Copyright (c) ZeroC, Inc. All rights reserved. // -// Generated by makeprops.py from file ./config/PropertyNames.xml, Fri Sep 6 18:11:04 2019 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Thu Jul 2 14:55:02 2020 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -11,6 +11,7 @@ public final class PropertyNames { public static final Property IceProps[] = { + new Property("Ice\\.AcceptClassCycles", false, null), new Property("Ice\\.ACM\\.Client", true, null), new Property("Ice\\.ACM\\.Server", true, null), new Property("Ice\\.ACM\\.Timeout", false, null), diff --git a/java-compat/test/src/main/java/test/Ice/objects/AllTests.java b/java-compat/test/src/main/java/test/Ice/objects/AllTests.java index 20e6c0972ee..82087aeccf4 100644 --- a/java-compat/test/src/main/java/test/Ice/objects/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/objects/AllTests.java @@ -445,6 +445,24 @@ public class AllTests } out.println("ok"); + out.print("testing sending class cycle... "); + out.flush(); + { + Recursive rec = new Recursive(); + rec.v = rec; + boolean acceptsCycles = initial.acceptsClassCycles(); + try + { + initial.setCycle(rec); + test(acceptsCycles); + } + catch(Ice.UnknownLocalException ex) + { + test(!acceptsCycles); + } + } + out.println("ok"); + return initial; } } diff --git a/java-compat/test/src/main/java/test/Ice/objects/InitialI.java b/java-compat/test/src/main/java/test/Ice/objects/InitialI.java index 1a48cf0f7bf..1b2a76804c3 100644 --- a/java-compat/test/src/main/java/test/Ice/objects/InitialI.java +++ b/java-compat/test/src/main/java/test/Ice/objects/InitialI.java @@ -148,6 +148,17 @@ public final class InitialI extends Initial } @Override + public void setCycle(Recursive r, Ice.Current current) + { + } + + @Override + public boolean acceptsClassCycles(Ice.Current current) + { + return true; + } + + @Override public B getMB(Ice.Current current) { diff --git a/java-compat/test/src/main/java/test/Ice/objects/Test.ice b/java-compat/test/src/main/java/test/Ice/objects/Test.ice index 9af55ef027c..9a572e054ef 100644 --- a/java-compat/test/src/main/java/test/Ice/objects/Test.ice +++ b/java-compat/test/src/main/java/test/Ice/objects/Test.ice @@ -219,6 +219,9 @@ class Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/java/src/Ice/src/main/java/com/zeroc/IceInternal/PropertyNames.java b/java/src/Ice/src/main/java/com/zeroc/IceInternal/PropertyNames.java index 60078543e6f..3ba05d0bde6 100644 --- a/java/src/Ice/src/main/java/com/zeroc/IceInternal/PropertyNames.java +++ b/java/src/Ice/src/main/java/com/zeroc/IceInternal/PropertyNames.java @@ -1,7 +1,7 @@ // // Copyright (c) ZeroC, Inc. All rights reserved. // -// Generated by makeprops.py from file ./config/PropertyNames.xml, Fri Sep 6 18:11:04 2019 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Thu Jul 2 14:55:02 2020 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -11,6 +11,7 @@ public final class PropertyNames { public static final Property IceProps[] = { + new Property("Ice\\.AcceptClassCycles", false, null), new Property("Ice\\.ACM\\.Client", true, null), new Property("Ice\\.ACM\\.Server", true, null), new Property("Ice\\.ACM\\.Timeout", false, null), diff --git a/java/test/src/main/java/test/Ice/objects/AllTests.java b/java/test/src/main/java/test/Ice/objects/AllTests.java index aa11250a6fa..d161292e807 100644 --- a/java/test/src/main/java/test/Ice/objects/AllTests.java +++ b/java/test/src/main/java/test/Ice/objects/AllTests.java @@ -435,6 +435,24 @@ public class AllTests } out.println("ok"); + out.print("testing sending class cycle... "); + out.flush(); + { + Recursive rec = new Recursive(); + rec.v = rec; + boolean acceptsCycles = initial.acceptsClassCycles(); + try + { + initial.setCycle(rec); + test(acceptsCycles); + } + catch(com.zeroc.Ice.UnknownLocalException ex) + { + test(!acceptsCycles); + } + } + out.println("ok"); + return initial; } } diff --git a/java/test/src/main/java/test/Ice/objects/InitialI.java b/java/test/src/main/java/test/Ice/objects/InitialI.java index 7760480f1e9..125427eb06a 100644 --- a/java/test/src/main/java/test/Ice/objects/InitialI.java +++ b/java/test/src/main/java/test/Ice/objects/InitialI.java @@ -112,6 +112,17 @@ public final class InitialI implements Initial } @Override + public void setCycle(Recursive r, com.zeroc.Ice.Current current) + { + } + + @Override + public boolean acceptsClassCycles(com.zeroc.Ice.Current current) + { + return true; + } + + @Override public com.zeroc.Ice.Value getI(com.zeroc.Ice.Current current) { return new II(); diff --git a/java/test/src/main/java/test/Ice/objects/Test.ice b/java/test/src/main/java/test/Ice/objects/Test.ice index 50e6248fd4a..a0862f9f4f4 100644 --- a/java/test/src/main/java/test/Ice/objects/Test.ice +++ b/java/test/src/main/java/test/Ice/objects/Test.ice @@ -216,6 +216,9 @@ interface Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/js/src/Ice/PropertyNames.js b/js/src/Ice/PropertyNames.js index 4f198ec4f30..79e02bcad47 100644 --- a/js/src/Ice/PropertyNames.js +++ b/js/src/Ice/PropertyNames.js @@ -1,7 +1,7 @@ // // Copyright (c) ZeroC, Inc. All rights reserved. // -// Generated by makeprops.py from file ./config/PropertyNames.xml, Fri Sep 6 18:11:04 2019 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Thu Jul 2 14:55:02 2020 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -14,6 +14,7 @@ const PropertyNames = {}; const Property = Ice.Property; PropertyNames.IceProps = [ + new Property("/^Ice\.AcceptClassCycles/", false, null), new Property("/^Ice\.ACM\.Client/", true, null), new Property("/^Ice\.ACM\.Server/", true, null), new Property("/^Ice\.ACM\.Timeout/", false, null), diff --git a/js/test/Ice/objects/Client.js b/js/test/Ice/objects/Client.js index 0f5e5cb730b..76697dc3d17 100644 --- a/js/test/Ice/objects/Client.js +++ b/js/test/Ice/objects/Client.js @@ -491,6 +491,23 @@ } out.writeLine("ok"); + out.write("testing sending class cycle... "); + { + const rec = new Test.Recursive(); + rec.v = rec; + const acceptsCycles = await initial.acceptsClassCycles(); + try + { + await initial.setCycle(rec); + test(acceptsCycles); + } + catch (error) + { + test(!acceptsCycles); + } + } + out.writeLine("ok"); + await initial.shutdown(); } diff --git a/js/test/Ice/objects/InitialI.js b/js/test/Ice/objects/InitialI.js index e6140642963..c5c3d623b40 100644 --- a/js/test/Ice/objects/InitialI.js +++ b/js/test/Ice/objects/InitialI.js @@ -225,6 +225,15 @@ return false; } + setCycle(r, current) + { + } + + acceptsClassCycles(current) + { + return true; + } + getMB(current) { return this._b1; diff --git a/js/test/Ice/objects/Test.ice b/js/test/Ice/objects/Test.ice index 617deda65de..3f60df69a41 100644 --- a/js/test/Ice/objects/Test.ice +++ b/js/test/Ice/objects/Test.ice @@ -220,6 +220,9 @@ interface Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/matlab/src/Init.cpp b/matlab/src/Init.cpp index fb90429390a..6147c9de1bd 100644 --- a/matlab/src/Init.cpp +++ b/matlab/src/Init.cpp @@ -53,6 +53,14 @@ Ice_initialize(mxArray* args, void* propsImpl, void** r) id.properties = deref<Ice::Properties>(propsImpl); } + if(!id.properties) + { + id.properties = Ice::createProperties(); + } + + // Always accept cycles in MATLAB + id.properties->setProperty("Ice.AcceptClassCycles", "1"); + *r = new shared_ptr<Ice::Communicator>(Ice::initialize(a, id)); return createResultValue(createStringList(a)); } diff --git a/matlab/test/Ice/objects/AllTests.m b/matlab/test/Ice/objects/AllTests.m index 7ad96bcb5c3..a12538eb07e 100644 --- a/matlab/test/Ice/objects/AllTests.m +++ b/matlab/test/Ice/objects/AllTests.m @@ -309,6 +309,18 @@ classdef AllTests end fprintf('ok\n'); + fprintf('testing sending class cycle... '); + rec = Recursive(); + rec.v = rec; + acceptsCycles = initial.acceptsClassCycles(); + try + initial.setCycle(rec); + assert(acceptsCycles); + catch ex + assert(!acceptsCycles); + end + fprintf('ok\n'); + r = initial; end end diff --git a/matlab/test/Ice/objects/Test.ice b/matlab/test/Ice/objects/Test.ice index d05e6feff56..aac48083c68 100644 --- a/matlab/test/Ice/objects/Test.ice +++ b/matlab/test/Ice/objects/Test.ice @@ -216,6 +216,9 @@ interface Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/objective-c/test/Ice/objects/AllTests.m b/objective-c/test/Ice/objects/AllTests.m index f88a3adc7c8..de23a3d1b63 100644 --- a/objective-c/test/Ice/objects/AllTests.m +++ b/objective-c/test/Ice/objects/AllTests.m @@ -706,5 +706,23 @@ objectsAllTests(id<ICECommunicator> communicator, BOOL __unused collocated) tprintf("ok\n"); } + { + tprintf("testing sending class cycle... "); + TestObjectsRecursive* rec = [TestObjectsRecursive recursive]; + rec.v = rec; + bool acceptsCycles = [initial acceptsClassCycles]; + @try + { + [initial setCycle:rec]; + test(acceptsCycles); + } + @catch(ICEUnknownLocalException*) + { + test(!acceptsCycles); + } + rec.v = nil; + tprintf("ok\n"); + } + return initial; } diff --git a/objective-c/test/Ice/objects/Client.m b/objective-c/test/Ice/objects/Client.m index fc3354caec1..243625c942b 100644 --- a/objective-c/test/Ice/objects/Client.m +++ b/objective-c/test/Ice/objects/Client.m @@ -121,6 +121,7 @@ main(int argc, char* argv[]) { ICEInitializationData* initData = [ICEInitializationData initializationData]; initData.properties = defaultClientProperties(&argc, argv); + [initData.properties setProperty:@"Ice.AcceptClassCycles" value:@"1"]; #if TARGET_OS_IPHONE initData.prefixTable_ = [NSDictionary dictionaryWithObjectsAndKeys: @"TestObjects", @"::Test", diff --git a/objective-c/test/Ice/objects/Collocated.m b/objective-c/test/Ice/objects/Collocated.m index 26af67be66d..cf2e2068f93 100644 --- a/objective-c/test/Ice/objects/Collocated.m +++ b/objective-c/test/Ice/objects/Collocated.m @@ -135,6 +135,7 @@ main(int argc, char* argv[]) ICEInitializationData* initData = [ICEInitializationData initializationData]; initData.properties = defaultServerProperties(&argc, argv); [initData.properties setProperty:@"Ice.Warn.Dispatch" value:@"0"]; + [initData.properties setProperty:@"Ice.AcceptClassCycles" value:@"1"]; #if TARGET_OS_IPHONE initData.prefixTable_ = [NSDictionary dictionaryWithObjectsAndKeys: @"TestObjects", @"::Test", diff --git a/objective-c/test/Ice/objects/ObjectsTest.ice b/objective-c/test/Ice/objects/ObjectsTest.ice index b7919f45a9e..019db4cd751 100644 --- a/objective-c/test/Ice/objects/ObjectsTest.ice +++ b/objective-c/test/Ice/objects/ObjectsTest.ice @@ -225,6 +225,9 @@ class Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/objective-c/test/Ice/objects/TestI.m b/objective-c/test/Ice/objects/TestI.m index f217e09daf4..1c0dae64e65 100644 --- a/objective-c/test/Ice/objects/TestI.m +++ b/objective-c/test/Ice/objects/TestI.m @@ -198,6 +198,17 @@ return YES; } +-(void) setCycle:(TestObjectsRecursive*)r current:(ICECurrent*)__unused current +{ + // break the cycle + r.v = nil; +} + +-(BOOL) acceptsClassCycles:(ICECurrent*) current +{ + return [[[[current adapter] getCommunicator] getProperties] getPropertyAsInt:@"Ice.AcceptClassCycles"] > 0; +} + -(TestObjectsB*) getMB:(ICECurrent*)__unused current { return _b1; diff --git a/objective-c/test/Ice/slicing/objects/Client.m b/objective-c/test/Ice/slicing/objects/Client.m index 689c70963d1..857a18b5a9f 100644 --- a/objective-c/test/Ice/slicing/objects/Client.m +++ b/objective-c/test/Ice/slicing/objects/Client.m @@ -38,6 +38,7 @@ main(int argc, char* argv[]) { ICEInitializationData* initData = [ICEInitializationData initializationData]; initData.properties = defaultClientProperties(&argc, argv); + [initData.properties setProperty:@"Ice.AcceptClassCycles" value:@"1"]; #if TARGET_OS_IPHONE initData.prefixTable_ = [NSDictionary dictionaryWithObjectsAndKeys: @"TestSlicingObjectsClient", @"::Test", diff --git a/objective-c/test/Ice/slicing/objects/Server.m b/objective-c/test/Ice/slicing/objects/Server.m index a75e72ea65e..f7dbd8b8365 100644 --- a/objective-c/test/Ice/slicing/objects/Server.m +++ b/objective-c/test/Ice/slicing/objects/Server.m @@ -47,6 +47,7 @@ main(int argc, char* argv[]) { ICEInitializationData* initData = [ICEInitializationData initializationData]; initData.properties = defaultServerProperties(&argc, argv); + [initData.properties setProperty:@"Ice.AcceptClassCycles" value:@"1"]; #if TARGET_OS_IPHONE initData.prefixTable_ = [NSDictionary dictionaryWithObjectsAndKeys: @"TestSlicingObjectsServer", @"::Test", diff --git a/objective-c/test/Ice/stream/Client.m b/objective-c/test/Ice/stream/Client.m index b51a3b43f29..8f22ca293a3 100644 --- a/objective-c/test/Ice/stream/Client.m +++ b/objective-c/test/Ice/stream/Client.m @@ -932,6 +932,7 @@ main(int argc, char* argv[]) ICEInitializationData* initData = [ICEInitializationData initializationData]; initData.properties = defaultClientProperties(&argc, argv); + [initData.properties setProperty:@"Ice.AcceptClassCycles" value:@"1"]; #if TARGET_OS_IPHONE initData.prefixTable_ = [NSDictionary dictionaryWithObjectsAndKeys: @"TestStream", @"::Test", diff --git a/php/src/php5/Communicator.cpp b/php/src/php5/Communicator.cpp index a7d185e9ccc..41b91656238 100644 --- a/php/src/php5/Communicator.cpp +++ b/php/src/php5/Communicator.cpp @@ -1234,6 +1234,14 @@ ZEND_FUNCTION(Ice_initialize) initData.compactIdResolver = new IdResolver(TSRMLS_C); initData.valueFactoryManager = new ValueFactoryManager; + if(!initData.properties) + { + initData.properties = Ice::createProperties(); + } + + // Always accept cycles in PHP + initData.properties->setProperty("Ice.AcceptClassCycles", "1"); + CommunicatorInfoIPtr info = initializeCommunicator(return_value, seq, zvargs != 0, initData TSRMLS_CC); if(!info) { diff --git a/php/src/php7/Communicator.cpp b/php/src/php7/Communicator.cpp index 3a0f53ca70b..4143ec65a4c 100644 --- a/php/src/php7/Communicator.cpp +++ b/php/src/php7/Communicator.cpp @@ -1245,6 +1245,14 @@ ZEND_FUNCTION(Ice_initialize) initData.compactIdResolver = new IdResolver(); initData.valueFactoryManager = new ValueFactoryManager; + if(!initData.properties) + { + initData.properties = Ice::createProperties(); + } + + // Always accept cycles in PHP + initData.properties->setProperty("Ice.AcceptClassCycles", "1"); + CommunicatorInfoIPtr info = initializeCommunicator(return_value, seq, zvargs != 0, initData); if(!info) { diff --git a/php/test/Ice/objects/Client.php b/php/test/Ice/objects/Client.php index cc6e62bb838..9355713801b 100644 --- a/php/test/Ice/objects/Client.php +++ b/php/test/Ice/objects/Client.php @@ -595,6 +595,23 @@ function allTests($helper) } echo "ok\n"; + echo "testing sending class cycle..."; + $rec = new Test_Recursive(); + $rec->v = $rec; + $acceptsCycles = $initial->acceptsClassCycles(); + try + { + $initial->setCycle($rec); + test($acceptsCycles); + } + catch(Exception $ex) + { + $ule = $NS ? "Ice\\UnknownLocalException" : "Ice_UnknownLocalException"; + test($ex instanceof $ule); + test(!$acceptsCycles); + } + echo "ok\n"; + return $initial; } diff --git a/php/test/Ice/objects/Test.ice b/php/test/Ice/objects/Test.ice index 81fc72314b2..cf407b7cf90 100644 --- a/php/test/Ice/objects/Test.ice +++ b/php/test/Ice/objects/Test.ice @@ -173,6 +173,9 @@ interface Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/python/modules/IcePy/Communicator.cpp b/python/modules/IcePy/Communicator.cpp index 7d7931e5f09..6ad3b3c2cf1 100644 --- a/python/modules/IcePy/Communicator.cpp +++ b/python/modules/IcePy/Communicator.cpp @@ -286,6 +286,9 @@ communicatorInit(CommunicatorObject* self, PyObject* args, PyObject* /*kwds*/) data.compactIdResolver = new IdResolver; + // Always accept cycles in Python + data.properties->setProperty("Ice.AcceptClassCycles", "1"); + Ice::CommunicatorPtr communicator; try { diff --git a/python/test/Ice/admin/AllTests.py b/python/test/Ice/admin/AllTests.py index 1e8e1cb72d9..a6ac1fcb2f8 100644 --- a/python/test/Ice/admin/AllTests.py +++ b/python/test/Ice/admin/AllTests.py @@ -183,12 +183,14 @@ def allTests(helper, communicator): # Test: PropertiesAdmin::getProperties() # pd = pa.getPropertiesForPrefix("") - test(len(pd) == 5) + test(len(pd) == 6) test(pd["Ice.Admin.Endpoints"] == "tcp -h 127.0.0.1") test(pd["Ice.Admin.InstanceName"] == "Test") test(pd["Prop1"] == "1") test(pd["Prop2"] == "2") test(pd["Prop3"] == "3") + # Ice for Python always sets Ice.AcceptClassCycles + test(pd["Ice.AcceptClassCycles"] == "1") changes = {} diff --git a/python/test/Ice/objects/AllTests.py b/python/test/Ice/objects/AllTests.py index 13dcef18c27..0cdcbd7d48d 100644 --- a/python/test/Ice/objects/AllTests.py +++ b/python/test/Ice/objects/AllTests.py @@ -357,4 +357,16 @@ def allTests(helper, communicator): test(f32.f2.ice_getIdentity().name == "F22") print("ok") + sys.stdout.write("testing sending class cycle... ") + sys.stdout.flush() + rec = Test.Recursive() + rec.v = rec + acceptsCycles = initial.acceptsClassCycles() + try: + initial.setCycle(rec) + test(acceptsCycles) + except Ice.UnknownLocalException: + test(not acceptsCycles) + print("ok") + return initial diff --git a/python/test/Ice/objects/Test.ice b/python/test/Ice/objects/Test.ice index e6e4828ffb9..0acc2fd52a5 100644 --- a/python/test/Ice/objects/Test.ice +++ b/python/test/Ice/objects/Test.ice @@ -216,6 +216,9 @@ interface Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/python/test/Ice/objects/TestI.py b/python/test/Ice/objects/TestI.py index 7bebf279f22..82d868f0143 100644 --- a/python/test/Ice/objects/TestI.py +++ b/python/test/Ice/objects/TestI.py @@ -126,6 +126,12 @@ class InitialI(Test.Initial): def supportsClassGraphDepthMax(self, current): return True + def setCycle(self, r, current): + pass + + def acceptsClassCycles(self, current): + return True + def getMB(self, current): return Test.Initial.GetMBMarshaledResult(self._b1, current) diff --git a/ruby/src/IceRuby/Communicator.cpp b/ruby/src/IceRuby/Communicator.cpp index 6050352e5d1..29f4f9e387e 100644 --- a/ruby/src/IceRuby/Communicator.cpp +++ b/ruby/src/IceRuby/Communicator.cpp @@ -207,6 +207,9 @@ IceRuby_initialize(int argc, VALUE* argv, VALUE /*self*/) data.properties = Ice::createProperties(seq, data.properties); } + // Always accept cycles in Ruby + data.properties->setProperty("Ice.AcceptClassCycles", "1"); + // // Remaining command line options are passed to the communicator // as an argument vector in case they contain plugin properties. diff --git a/ruby/test/Ice/objects/AllTests.rb b/ruby/test/Ice/objects/AllTests.rb index 65d1faa0fcf..1a01c9b5864 100644 --- a/ruby/test/Ice/objects/AllTests.rb +++ b/ruby/test/Ice/objects/AllTests.rb @@ -404,5 +404,19 @@ def allTests(helper, communicator) test(f32.f2.ice_getIdentity().name == "F22") end puts "ok" + + print "testing sending class cycle... " + STDOUT.flush + rec = Test::Recursive.new + rec.v = rec + acceptsCycles = initial.acceptsClassCycles() + begin + initial.setCycle(rec) + test(acceptsCycles) + rescue Ice::UnknownLocalException => ex + test(!acceptsCycles) + end + puts "ok" + return initial end diff --git a/ruby/test/Ice/objects/Test.ice b/ruby/test/Ice/objects/Test.ice index b4e0f3fd690..af4ab4786c9 100644 --- a/ruby/test/Ice/objects/Test.ice +++ b/ruby/test/Ice/objects/Test.ice @@ -186,6 +186,9 @@ class Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/swift/src/Ice/CommunicatorI.swift b/swift/src/Ice/CommunicatorI.swift index 13a68d4cce8..0f930e325e3 100644 --- a/swift/src/Ice/CommunicatorI.swift +++ b/swift/src/Ice/CommunicatorI.swift @@ -11,6 +11,7 @@ class CommunicatorI: LocalObject<ICECommunicator>, Communicator { let initData: InitializationData let classGraphDepthMax: Int32 let traceSlicing: Bool + let acceptClassCycles: Bool init(handle: ICECommunicator, initData: InitializationData) { defaultsAndOverrides = DefaultsAndOverrides(handle: handle) @@ -22,6 +23,7 @@ class CommunicatorI: LocalObject<ICECommunicator>, Communicator { classGraphDepthMax = num } traceSlicing = initData.properties!.getPropertyAsIntWithDefault(key: "Ice.Trace.Slicing", value: 0) > 0 + acceptClassCycles = initData.properties!.getPropertyAsIntWithDefault(key: "Ice.AcceptClassCycles", value: 0) > 0 super.init(handle: handle) } diff --git a/swift/src/Ice/InputStream.swift b/swift/src/Ice/InputStream.swift index c92fe9efbf3..57d354869a6 100644 --- a/swift/src/Ice/InputStream.swift +++ b/swift/src/Ice/InputStream.swift @@ -14,6 +14,7 @@ public class InputStream { private(set) var communicator: Communicator private let encoding: EncodingVersion private let traceSlicing: Bool + fileprivate let acceptClassCycles: Bool private var encaps: Encaps! @@ -43,6 +44,7 @@ public class InputStream { self.encoding = encoding classGraphDepthMax = (communicator as! CommunicatorI).classGraphDepthMax traceSlicing = (communicator as! CommunicatorI).traceSlicing + acceptClassCycles = (communicator as! CommunicatorI).acceptClassCycles } /// Reads an encapsulation from the stream. @@ -887,7 +889,7 @@ private protocol EncapsDecoder: AnyObject { // Encapsulation attributes for value unmarshaling. // var patchMap: [Int32: [PatchEntry]] { get set } - var unmarshaledMap: [Int32: Value] { get set } + var unmarshaledMap: [Int32: Value?] { get set } var typeIdMap: [Int32: String] { get set } var typeIdIndex: Int32 { get set } var valueList: [Value] { get set } @@ -985,10 +987,14 @@ extension EncapsDecoder { precondition(index > 0, "invalid index") // - // Check if we have already unmarshalled the instance. If that's the case, - // just invoke the callback and we're done. + // Check if we already unmarshaled the object. If that's the case, just patch the object smart pointer + // and we're done. A null value indicates we've encountered a cycle and Ice.AllowClassCycles is false. // - if let obj: Value = unmarshaledMap[index] { + if let optObj = unmarshaledMap[index] { + guard let obj = optObj else { + assert(!stream.acceptClassCycles) + throw MarshalException(reason: "cycle detected during Value unmarshaling") + } try cb(obj) return } @@ -1008,7 +1014,11 @@ extension EncapsDecoder { // Add the instance to the map of unmarshaled instances, this must // be done before reading the instances (for circular references). // - unmarshaledMap[index] = v + // If circular references are not allowed we insert null (for cycle detection) and add + // the object to the map once it has been fully unmarshaled. + // + unmarshaledMap[index] = stream.acceptClassCycles ? v : (nil as Value?) + assert(unmarshaledMap[index] != nil) // // Read the instance. @@ -1053,6 +1063,12 @@ extension EncapsDecoder { valueList.removeAll() } } + + if !stream.acceptClassCycles { + // This class has been fully unmarshaled without creating any cycles + // It can be added to the map now. + unmarshaledMap[index] = v + } } } @@ -1061,7 +1077,7 @@ private class EncapsDecoder10: EncapsDecoder { unowned let stream: InputStream let valueFactoryManager: ValueFactoryManager lazy var patchMap = [Int32: [PatchEntry]]() - lazy var unmarshaledMap = [Int32: Value]() + lazy var unmarshaledMap = [Int32: Value?]() lazy var typeIdMap = [Int32: String]() var typeIdIndex: Int32 = 0 lazy var valueList = [Value]() @@ -1325,7 +1341,7 @@ private class EncapsDecoder11: EncapsDecoder { unowned let stream: InputStream let valueFactoryManager: ValueFactoryManager lazy var patchMap = [Int32: [PatchEntry]]() - lazy var unmarshaledMap = [Int32: Value]() + lazy var unmarshaledMap = [Int32: Value?]() lazy var typeIdMap = [Int32: String]() var typeIdIndex: Int32 = 0 lazy var valueList = [Value]() diff --git a/swift/test/Ice/objects/AllTests.swift b/swift/test/Ice/objects/AllTests.swift index 393b9bf6031..9efa1f8fa1e 100644 --- a/swift/test/Ice/objects/AllTests.swift +++ b/swift/test/Ice/objects/AllTests.swift @@ -313,5 +313,20 @@ func allTests(_ helper: TestHelper) throws -> InitialPrx { } output.writeLine("ok") + output.write("testing sending class cycle... ") + do { + let rec = Recursive(v: nil) + rec.v = rec + let acceptsCycles = try initial.acceptsClassCycles() + do { + try initial.setCycle(rec) + try test(acceptsCycles) + } catch is Ice.UnknownLocalException { + try test(!acceptsCycles) + } + rec.v = nil + } + output.writeLine("ok") + return initial } diff --git a/swift/test/Ice/objects/Client.swift b/swift/test/Ice/objects/Client.swift index 7fd5decd901..5f242a0b70e 100644 --- a/swift/test/Ice/objects/Client.swift +++ b/swift/test/Ice/objects/Client.swift @@ -9,6 +9,7 @@ import TestCommon public class Client: TestHelperI { public override func run(args: [String]) throws { let properties = try createTestProperties(args) + properties.setProperty(key: "Ice.AcceptClassCycles", value: "1") var initData = InitializationData() initData.properties = properties initData.classResolverPrefix = ["IceObjects"] diff --git a/swift/test/Ice/objects/Collocated.swift b/swift/test/Ice/objects/Collocated.swift index 611bc9745da..1a8f85e70b5 100644 --- a/swift/test/Ice/objects/Collocated.swift +++ b/swift/test/Ice/objects/Collocated.swift @@ -8,6 +8,7 @@ import TestCommon class Collocated: TestHelperI { public override func run(args: [String]) throws { let properties = try createTestProperties(args) + properties.setProperty(key: "Ice.AcceptClassCycles", value: "1") properties.setProperty(key: "Ice.Warn.Dispatch", value: "0") var initData = Ice.InitializationData() initData.properties = properties diff --git a/swift/test/Ice/objects/Test.ice b/swift/test/Ice/objects/Test.ice index f7e681f216b..db85cb340c0 100644 --- a/swift/test/Ice/objects/Test.ice +++ b/swift/test/Ice/objects/Test.ice @@ -222,6 +222,9 @@ interface Initial void setRecursive(Recursive p); bool supportsClassGraphDepthMax(); + void setCycle(Recursive r); + bool acceptsClassCycles(); + ["marshaled-result"] B getMB(); ["amd", "marshaled-result"] B getAMDMB(); diff --git a/swift/test/Ice/objects/TestI.swift b/swift/test/Ice/objects/TestI.swift index 31af1d0c95a..f7e5cb73bae 100644 --- a/swift/test/Ice/objects/TestI.swift +++ b/swift/test/Ice/objects/TestI.swift @@ -174,6 +174,18 @@ class InitialI: Initial { return true } + func setCycle(r: Recursive?, current _: Ice.Current) { + precondition(r != nil) + precondition(r!.v === r) + // break the cycle + r!.v = nil + } + + func acceptsClassCycles(current: Ice.Current) throws -> Bool { + let properties = current.adapter!.getCommunicator().getProperties() + return properties.getPropertyAsIntWithDefault(key: "Ice.AcceptClassCycles", value: 0) > 0 + } + func getD1(d1: D1?, current _: Ice.Current) throws -> D1? { return d1 } diff --git a/swift/test/Ice/slicing/objects/Client.swift b/swift/test/Ice/slicing/objects/Client.swift index 580a37a371e..203d07afabb 100644 --- a/swift/test/Ice/slicing/objects/Client.swift +++ b/swift/test/Ice/slicing/objects/Client.swift @@ -34,8 +34,10 @@ class PNodeI: PNode { public class Client: TestHelperI { public override func run(args: [String]) throws { + let properties = try createTestProperties(args) + properties.setProperty(key: "Ice.AcceptClassCycles", value: "1") var initData = InitializationData() - initData.properties = try createTestProperties(args) + initData.properties = properties initData.classResolverPrefix = ["IceSlicingObjects", "IceSlicingObjectsClient"] let communicator = try initialize(initData) defer { diff --git a/swift/test/Ice/slicing/objects/Server.swift b/swift/test/Ice/slicing/objects/Server.swift index faf8d5c6037..8d89d0bc01f 100644 --- a/swift/test/Ice/slicing/objects/Server.swift +++ b/swift/test/Ice/slicing/objects/Server.swift @@ -8,6 +8,7 @@ import TestCommon class Server: TestHelperI { public override func run(args: [String]) throws { let properties = try createTestProperties(args) + properties.setProperty(key: "Ice.AcceptClassCycles", value: "1") properties.setProperty(key: "Ice.Warn.Dispatch", value: "0") var initData = InitializationData() initData.properties = properties diff --git a/swift/test/Ice/slicing/objects/ServerAMD.swift b/swift/test/Ice/slicing/objects/ServerAMD.swift index dd2fcdae22b..d9d87fad95e 100644 --- a/swift/test/Ice/slicing/objects/ServerAMD.swift +++ b/swift/test/Ice/slicing/objects/ServerAMD.swift @@ -8,6 +8,7 @@ import TestCommon class ServerAMD: TestHelperI { public override func run(args: [String]) throws { let properties = try createTestProperties(args) + properties.setProperty(key: "Ice.AcceptClassCycles", value: "1") properties.setProperty(key: "Ice.Warn.Dispatch", value: "0") var initData = InitializationData() initData.properties = properties diff --git a/swift/test/Ice/stream/Client.swift b/swift/test/Ice/stream/Client.swift index 6a5bce8776e..a84378d8204 100644 --- a/swift/test/Ice/stream/Client.swift +++ b/swift/test/Ice/stream/Client.swift @@ -11,8 +11,10 @@ public class Client: TestHelperI { let writer = getWriter() writer.write("testing primitive types... ") + let properties = try createTestProperties(args) + properties.setProperty(key: "Ice.AcceptClassCycles", value: "1") var initData = Ice.InitializationData() - initData.properties = try createTestProperties(args) + initData.properties = properties initData.classResolverPrefix = ["IceStrem"] let communicator = try initialize(initData) defer { |