Burtsev-Alexey/net-object-deep-copy

StackOverflow in InternalCopy

Opened this issue · 10 comments

In InternalCopy, there is a line:

CopyFields(originalObject, visited, cloneObject, typeToReflect);

...but then in CopyFields, you have the line:

var clonedFieldValue = InternalCopy(originalFieldValue, visited);

This results in a StackOverflow exception.

It's recursion, it should work. Can you provide simple code describing class that result in your error?

I deal with the same issue, leading to the StackOverflow exception when creating a copy of a large object, when I debugged I found the following details regarding the issue:

Method - private static Object InternalCopy(Object originalObject, IDictionary<Object, Object> visited)
Line of Issue - "var cloneObject = CloneMethod.Invoke(originalObject, null);"
count value for the - IDictionary<Object, Object> visited is 2421, which essentially show I am trying to copy a large object

Other details that you might be keen about are:

  • Class I am trying to copy is singleton
  • It further contains multiple collection objects and reference to several Singleton classes. In fact few collections are also Singleton.

I am not sure if I have a simple way to provide you the details of the issue, as it is part of our project. Let me known in case you want me to try few options to provide debug details

Also does this need implementation of the extension methods for the child objects too, I am assuming No

I have some new ideas about what might be the cause, I'll check them.

Thanks Alex. Please let me know when you have an update, I can test the changes.
Stack overflow means recursive code is leading to infinite recursion, is it because for a given type in a class, there's no break condition

Hi there!

My very first attempt to copy a complex business object resulted in a stack overflow exception, too. I've nailed it down to a regular expression somewhere deep down in my object hierarchy. It's simple enough to reproduce, but unfortunately I have no idea how to fix it.

Regex re = new Regex(@"\w", RegexOptions.Compiled);
Regex re2 = re.Copy<Regex>();  // <--- StackOverflow exception

HTH and best regards,
Thomas

Thank you, will check it out.

Checked it, yes I can reproduce this. I know why it happen, and will fix it soon. But

  1. I do not recommend copying Regex objects, or any system objects, because they contain references for almost whole loaded .NET runtime. For example your Regex object due to Complied flag, contains reference to .NET dynamic compiler (Reflection.Code.Emit) which is getting copied too. Copying some .NET system objects can destabilize CLR, like copying Garbage Collector. This code was intended for copying user created classes.

2.I will fix this problem. The problem is in System.Reflection.Pointer object that holds Void* pointer, when I'm getting its value, it returns new Object (different object Reference) but it points to same memory address. So we have endless cycle because, the field value always return new Object instance, note that its not recursion (I store visited nodes), its endless stream of objects.

I was able to solve the problem with unmanaged pointers, but your object with Regex still will not work, the problem is in System.Reflection.Emit.SignatureHelper class that is used inside Regex, it's GetHashCode() is poorly implemented and throw NullReferenceException

public override int GetHashCode()
{
  int num = this.m_module.GetHashCode() + this.m_currSig + this.m_sizeLoc;
  if (this.m_sigDone)
    ++num;
  for (int index = 0; index < this.m_currSig; ++index)
    num += this.m_signature[index].GetHashCode();
  return num;
}

notice the usage of m_module, it's null in your example.

I'm not sure I want to commit the unmanaged pointers fix. Though it will fix some problems, but this is far beyond this code usage. If I will start dealing with unmanaged memory, its not gonna end well.
You can disable unmanaged pointers cloning by returning same object or null, like this:

    private static Object InternalCopy(Object originalObject, IDictionary<Object, Object> visited)
    {
        if (originalObject == null) return null;
        var typeToReflect = originalObject.GetType();
        if (IsPrimitive(typeToReflect)) return originalObject;
        if (visited.ContainsKey(originalObject)) return visited[originalObject];
        if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
        if (typeof (Pointer) == typeToReflect) return null;
        var cloneObject = CloneMethod.Invoke(originalObject, null);
        if (typeToReflect.IsArray)
        {
            var arrayType = typeToReflect.GetElementType();
            if (IsPrimitive(arrayType) == false)
            {
                Array clonedArray = (Array)cloneObject;
                clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
            }

        }
        visited.Add(originalObject, cloneObject);
        CopyFields(originalObject, visited, cloneObject, typeToReflect);
        RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect);
        return cloneObject;
    }

Hi Burtsev,
I am using the same ObjectExtensions.cs, And I am getting the same StackOverflow exception for certain object copy. It is coming in
var cloneObject = CloneMethod.Invoke(originalObject, null);
Do you happen to have any updated version of the ObjectExtensions.cs? It would really help and thanks in advance.

Thanks and Regards,
Adarsh S K