IL2CPP scripts getting stripped

I’m currently using 5.1.1p1. In previous builds these scripts (which get added at runtime) were not stripped, but in 5.1.1p1 they are. It was mentioned in the patch release notes that Unity added a “PreserveAttribute” to prevent classes, methods, fields and properties from being stripped in IL2CPP. I’m not finding this attribute anywhere.
I suppose I can just stick to using the link.xml file for now?

Edit:
A bit of context, this is what we’re doing:
MapObject mapObjectScript = toAddTo.AddComponent(System.Type.GetType(script.Trim())) as MapObject;
This produces the runtime error: “AddComponent asking for invalid type”
Note: This is only happening in an iOS Build. Works fine in the editor (which is why I’m assuming it’s a stripping issue)

As I mentioned this was working before and this code hasn’t changed since I’ve updated Unity.

Solved the problem!
The scripts weren’t getting stripped. I needed to add which assembly the script belongs to. So instead of this:

Object mapObjectScript = gameObject.AddComponent(System.Type.GetType("ScriptName"));

You’ll want:

Object mapObjectScript = gameObject.AddComponent(System.Type.GetType("ScriptName,Assembly-CSharp"));

Not sure why this has changed, but I assume due to some underlying change in the IL2CPP stuff. I can’t say that for sure though, as I have not tried this under Mono scripting backend.

@CPXTom

For the Preserve attribute we do not have a specifically defined attribute that you need to use. You can create your own attribute in C# code, and use it. As long as the name of the attribute is Preserve, the code stripper will find it.

As for the actual problem (with Type.GetType), it looks like that might be related to this bug:

http://issuetracker.unity3d.com/issues/il2cpp-type-dot-gettype-fails-on-armv7

I’ve corrected this bug internally, and the fix should be landing in upcoming patch releases. When that fix does land, you can probably remove the assembly name from the argument to GetType.

Just to add a note here though, I am starting to see all kinds of crazy stuff that really is getting stripped from IL2CPP in recent releases. A bunch of reflection stuff like TypeConverter getting the constructor stripped… using classes that implement interfaces instead of base classes often get their default constructors stripped as well. I need to get a bug report filed for it, but there are stripping issues that are cropping up (affecting both iOS and WebGL).

Edit: I should also note that the Preserve attribute isn’t always feasible, though it’s a nice addition. Sometimes you have types in a precompiled assembly that you don’t have access to modify.

1 Like

@Dustin-Horne

Yes, please submit a bug report if you have a case where stripping is incorrect and overly aggressive, thanks.

Note that if you don’t have source code access, you can use a link.xml file to prevent stripping instead of the Preserve attribute. Or is there a case with link.xml won’t work?

Working on it. I have a few bugs that I need to report so I’m getting repro projects together. As to link.xml yes that approach should work just fine. I’m assuming link.xml will be used for all platforms as well when IL2CPP is rolled out?

@Dustin-Horne

Yes, we will continue to use link.xml for stripping with IL2CPP on all platforms.

1 Like

I have also a problem with a constructor which has been stripped away:

My code:

public interface IAssetProvider<T>
  where T : UnityEngine.Object
{
    // ...
}

[Serializable]
public class BundleAssetProvider<T> : IAssetProvider<T>
  where T : UnityEngine.Object
{
    // ...
}

This sounds very much like one the problems you described…

I defined an PreserveAttribute (because the UnityEngine.Scripting.PreserveAttribute doesn’t exist) and put it above my class… It did not solve the problem :frowning:

So I would like to know: am I on the right track?
Can I fix this problem myself somehow?

To answer my own question: It seems to be a Unity bug which is fixed in Unity 5.1.2p2.

It may have been Unity 5.1.2 where the Preserve attribute support was actually added which would explain why it didn’t work. I’m not sure there actually is a Preserve attribute at all in Unity (unless they added one), it’s just something we have to create ourselves which isn’t necessarily a bad thing since you may be working with classes in a separate assembly that you don’t necessarily want to introduce a UnityEngine dependency on.

Yes, the Preserve attribute is a bit of a hack, at least in that it is not provided in Unity. The stripping code looks for an attribute named “Preserve”, so that is really all that is necessary.

The attribute only has to start with Preserve correct? So it could be:

public class PreserveData : Attribute
{

}

Or does it just have to be Preserve and nothing else.

@Dustin-Horne

After looking at the code for the stripper, I think that the attribute has to be named “Preserve” exactly. Are you seeing a different behavior from this?

I have not tried it, I had just seen a post by one of your guys when you first implemented it that said it needed to start with Preserve I thought, but I could have been mistaken.

I think you should allow “PreserveAttribute” as well. Because it is common for .Net programmers to put “Attribute” at the end (which can be ignored when using it).

@sz-Bit-Barons

Yes, that is how it works. The name of the attribute class should be PreserveAttribute like this:

class PreserveAttribute : Attribute
{
}
1 Like

I’m seeing that code stripping inside DLLs is more aggressive than code stripping in code compiled by Unity.

The same code inside and outside a DLL behaves differently. The default constructor on a class that is never called is stripped from a DLL whether the default constructor is explicitly declared or not unless some code actually calls the default constructor. (Dummy methods do not work because they are also stripped.) In code compiled by Unity, even without declaring a default constructor in the class and never calling the constructor anywhere in code, the constructor is preserved. This aggressive stripping in DLLs causes Activator.CreateInstance to fail when called in the DLL but not in the code compiled by Unity.

@guavaman

This is the expected behavior. Managed code stripping is only applied to pre-built assemblies in the project and any assemblies that Unity ships with (e.g. UnityEngine.dll, mscorlib.dll, etc.). It is not applied to the assemblies produced by any scripting code in the project (e.g. Assembly-CSharp.dll, Assembly-UnityScript.dll, etc.).

Thanks for the reply! I see. That’s not clear from the documentation: https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html

Since I have to support Unity 5.0 before the Preserve attribute was added (appears to have been added in 5.1), the only workaround I could find was to instantiate the object using the default constructor in the static constructor to prevent IL2CPP from stripping it out.

It might help to let you know how I do it. I can’t recall exactly when the Preserve attribute was added but, you can create your own. In your asset create the following:

public class PreserveAttribute : Attribute
{

}

Then use it on your classes. The stripping engine actually looks for an attribute called Preserve (and it originally did this in 4.x before the built in Preserve attribute was added). So for all of my internal stuff in JSON .NET, I created and utilized my own Preserve attribute, that way it still works in 4.x as well. For versions prior to like… 4.4 or whenever Preserve was honored, you can use link.xml.

Another thing to keep in mind is that constructors aren’t the only thing stripped, so your approach won’t work with 100% certainty. For example, since I do deserialization, there are times when my customers have classes with properties where they only ever use the getters for those properties in code, and JSON .NET uses reflection to execute the setters. The stripping engine will actually strip setters off of those classes.

Lastly… I’m not 100% sure that Josh’s answer was accurate because I’m pretty sure things do get stripped from the Unity generated assemblies for your game, just not nearly as aggressively.

Edit: One more note… I’ve noticed that stripping is also inconsistent per platform even when using IL2CPP. Sometimes things will be stripped for IOS but not other platforms for example. Android actually is usually the biggest offender for my customers.

1 Like