summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe George <joe@zeroc.com>2020-07-07 16:57:51 -0400
committerGitHub <noreply@github.com>2020-07-07 16:57:51 -0400
commit6c0e7e6fcabde691e7c38a814b6171f9f4e77d09 (patch)
treeaed41fdff6561e134c73da214e580be0910e6f6a
parentCopy python dependencies to the extension directory - Close #926 (#927) (diff)
downloadice-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.
-rw-r--r--CHANGELOG-3.7.md101
-rw-r--r--config/PropertyNames.xml1
-rw-r--r--cpp/src/Ice/InputStream.cpp21
-rw-r--r--cpp/src/Ice/Instance.cpp3
-rw-r--r--cpp/src/Ice/Instance.h2
-rw-r--r--cpp/src/Ice/PropertyNames.cpp3
-rw-r--r--cpp/src/Ice/PropertyNames.h2
-rw-r--r--cpp/test/Ice/objects/AllTests.cpp20
-rw-r--r--cpp/test/Ice/objects/Client.cpp2
-rw-r--r--cpp/test/Ice/objects/Collocated.cpp1
-rw-r--r--cpp/test/Ice/objects/Test.ice3
-rw-r--r--cpp/test/Ice/objects/TestI.cpp14
-rw-r--r--cpp/test/Ice/objects/TestI.h3
-rw-r--r--cpp/test/Ice/optional/AllTests.cpp1
-rw-r--r--cpp/test/Ice/optional/Client.cpp1
-rw-r--r--cpp/test/Ice/optional/Server.cpp1
-rw-r--r--cpp/test/Ice/optional/ServerAMD.cpp1
-rw-r--r--cpp/test/Ice/slicing/objects/Client.cpp1
-rw-r--r--cpp/test/Ice/slicing/objects/Server.cpp1
-rw-r--r--cpp/test/Ice/slicing/objects/ServerAMD.cpp1
-rw-r--r--cpp/test/Ice/stream/Client.cpp4
-rw-r--r--csharp/src/Ice/PropertyNames.cs3
-rw-r--r--csharp/test/Ice/objects/AllTests.cs19
-rw-r--r--csharp/test/Ice/objects/InitialI.cs9
-rw-r--r--csharp/test/Ice/objects/Test.ice3
-rw-r--r--java-compat/src/Ice/src/main/java/IceInternal/PropertyNames.java3
-rw-r--r--java-compat/test/src/main/java/test/Ice/objects/AllTests.java18
-rw-r--r--java-compat/test/src/main/java/test/Ice/objects/InitialI.java11
-rw-r--r--java-compat/test/src/main/java/test/Ice/objects/Test.ice3
-rw-r--r--java/src/Ice/src/main/java/com/zeroc/IceInternal/PropertyNames.java3
-rw-r--r--java/test/src/main/java/test/Ice/objects/AllTests.java18
-rw-r--r--java/test/src/main/java/test/Ice/objects/InitialI.java11
-rw-r--r--java/test/src/main/java/test/Ice/objects/Test.ice3
-rw-r--r--js/src/Ice/PropertyNames.js3
-rw-r--r--js/test/Ice/objects/Client.js17
-rw-r--r--js/test/Ice/objects/InitialI.js9
-rw-r--r--js/test/Ice/objects/Test.ice3
-rw-r--r--matlab/src/Init.cpp8
-rw-r--r--matlab/test/Ice/objects/AllTests.m12
-rw-r--r--matlab/test/Ice/objects/Test.ice3
-rw-r--r--objective-c/test/Ice/objects/AllTests.m18
-rw-r--r--objective-c/test/Ice/objects/Client.m1
-rw-r--r--objective-c/test/Ice/objects/Collocated.m1
-rw-r--r--objective-c/test/Ice/objects/ObjectsTest.ice3
-rw-r--r--objective-c/test/Ice/objects/TestI.m11
-rw-r--r--objective-c/test/Ice/slicing/objects/Client.m1
-rw-r--r--objective-c/test/Ice/slicing/objects/Server.m1
-rw-r--r--objective-c/test/Ice/stream/Client.m1
-rw-r--r--php/src/php5/Communicator.cpp8
-rw-r--r--php/src/php7/Communicator.cpp8
-rw-r--r--php/test/Ice/objects/Client.php17
-rw-r--r--php/test/Ice/objects/Test.ice3
-rw-r--r--python/modules/IcePy/Communicator.cpp3
-rw-r--r--python/test/Ice/admin/AllTests.py4
-rw-r--r--python/test/Ice/objects/AllTests.py12
-rw-r--r--python/test/Ice/objects/Test.ice3
-rw-r--r--python/test/Ice/objects/TestI.py6
-rw-r--r--ruby/src/IceRuby/Communicator.cpp3
-rw-r--r--ruby/test/Ice/objects/AllTests.rb14
-rw-r--r--ruby/test/Ice/objects/Test.ice3
-rw-r--r--swift/src/Ice/CommunicatorI.swift2
-rw-r--r--swift/src/Ice/InputStream.swift30
-rw-r--r--swift/test/Ice/objects/AllTests.swift15
-rw-r--r--swift/test/Ice/objects/Client.swift1
-rw-r--r--swift/test/Ice/objects/Collocated.swift1
-rw-r--r--swift/test/Ice/objects/Test.ice3
-rw-r--r--swift/test/Ice/objects/TestI.swift12
-rw-r--r--swift/test/Ice/slicing/objects/Client.swift4
-rw-r--r--swift/test/Ice/slicing/objects/Server.swift1
-rw-r--r--swift/test/Ice/slicing/objects/ServerAMD.swift1
-rw-r--r--swift/test/Ice/stream/Client.swift4
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 {