JakubNei/mcs-ICodeCompiler

Multiple files

z3t0 opened this issue ยท 25 comments

z3t0 commented

Hi,

Can you please provide an example for how I can load multiple files? When adding a second file the GUI shows two buttons, loading one script works perfectly, but I get an error whenever I try to load a second script.

Any help is greatly appreciated

NotSupportedException: The invoked member is not supported in a dynamic module.
System.Reflection.Emit.AssemblyBuilder.get_Location () (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection.Emit/AssemblyBuilder.cs:241)
CSharpCompiler.ScriptBundleLoader+ScriptBundle.<ScriptBundle>m__6 (System.Reflection.Assembly a) (at Assets/CSharpCompiler/ScriptBundleLoader.cs:57)
System.Linq.Enumerable+<CreateSelectIterator>c__Iterator10`2[System.Reflection.Assembly,System.String].MoveNext ()
System.Collections.Generic.List`1[System.String].AddEnumerable (IEnumerable`1 enumerable) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:128)
System.Collections.Generic.List`1[System.String]..ctor (IEnumerable`1 collection) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:65)
System.Linq.Enumerable.ToArray[String] (IEnumerable`1 source)
CSharpCompiler.ScriptBundleLoader+ScriptBundle..ctor (CSharpCompiler.ScriptBundleLoader manager, IEnumerable`1 filePaths) (at Assets/CSharpCompiler/ScriptBundleLoader.cs:57)
CSharpCompiler.ScriptBundleLoader.LoadAndWatchScriptsBundle (IEnumerable`1 fileSources) (at Assets/CSharpCompiler/ScriptBundleLoader.cs:33)
CSharpCompiler.DemoLoadScripts.OnGUI () (at Assets/CSharpCompiler/Demo/DemoLoadScripts.cs:61)

What was the problem and how did you fix it ? Some other people might encounter similar problem.

z3t0 commented

The problem was that trying to load a second file always failed with the abov error. I didn't really fix it but instead chose to bundle all files into one so its only compiled once. This seemed to be working but then I started to get crashes so now I'm not so sure.

I'll see if I can find a way to fix it since my work around may be buggy itself

z3t0 commented

Still working on this but, for now it seems that trying to load a second file will always fail, unless they are loaded together as a bundle.

Lypyl commented

I ran into this problem myself, I solved it by implementing the AppDomain.AssemblyResolve Event

https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx

I basically just cache all the dynamic assemblies by name, and use a try/catch so when the error comes up it gets looked up in the local dictionary instead. I haven't encountered any problems with this so far.

XeonG commented

Can you give an example of this Lypyl?

z3t0 commented

@Lypyl excellent, I was working towards a similar solution but ended up moving onto other projects instead!

@Lypyl @z3t0 Can You provide some examples to solve this problem? I am also facing the same problem but I don't know how to solve it. Thank you so much

z3t0 commented

It's been a long while since I tried this but the issue was that loading a second file using the method in this project would always fail for some reason.

It seems that @Lypyl solved it by checking for an error. I can't exactly remember how I solved it but I did something similar.

@z3t0 @Lypyl I cant really get the meaning of "looked up in the local dictionary". Can you explain the procedure of doing this? Since I am trying to implement this on unity3d. I want to attach some script at run time, but when I tried to load scripts second time, the problem come up like u mentioned.

z3t0 commented

I can't exactly remember the procedure, maybe if you share some code on a gist I could make suggestions?

@z3t0 I finally solved it with the AppDomain.AssemblyResolve. Thank you so much!

XeonG commented

Don't support you could share the code for the solution you got for this?

@ioioio8888
Been at this problem for hours, i'm trying to compile many cs files and the 2nd etc have this same error.
If you know how to fix it, simdiff at gmail com PLEASE! :) I'll be here trying stuff to fix it but if you can help I have other work to do.

XeonG commented

he's probably not going to bother like the others who also didnt bother submitting in a patch either.. but hey if you do find the solution milo83 maybe do otherwise.

personally i'm not going to even bother using this myself, as this one issue kinda makes the thing pointless.

z3t0 commented

hi, if you share your code i can mess around and try to reimplement the solution I had. I would just provide a patch but i lost the files to my old project

z3t0 commented

okay never mind i found my code but its not commented at all so im guessing a bit at this point. Though I think these lines may be relevant

 public void createInstance(GameObject gameObject, Type t)
     {
         if (typeof(UnityEngine.Component).IsAssignableFrom(t))
         {
             gameObject.AddComponent(t);
             return;
         } 
         
         System.Activator.CreateInstance(t);
    }

@ioioio8888 Hello. I am also tried to solve this problem for a long time, but with no success. Could you please send me a solution too? : ) It would be very helpful.

I'm also totally lost on this. Can either of you share how you did this with AppDomain.AssemblyResolve @ioioio8888 @z3t0 ?

Just solved it in a way that is acceptable for me so for anyone else getting stuck on this @Artaani :
The problem is in that once you add a dynamic assembly (the first time you add a script), this assembly doesn't support .Location and throws a NotSupportedException | "The current assembly is a dynamic assembly, represented by an AssemblyBuilder object."
https://msdn.microsoft.com/en-us/library/system.reflection.assembly.location(v=vs.110).aspx

So this line in ScriptBundleLoader.cs errors out:
domain.GetAssemblies().Select(a => a.Location).ToArray();

I instead replaced it with:

var prevAssemblies = domain.GetAssemblies();
var tempAssemblyRefs = new List<string>();
for (int i = 0; i < prevAssemblies.Length; i++)
{
    try
    {
        tempAssemblyRefs.Add(prevAssemblies[i].Location);
    }
    catch (System.NotSupportedException e)
    {
        Debug.Log(e);
    }
}
this.assemblyReferences = tempAssemblyRefs.ToArray();

Though it can be done neater probably.

@Veguard , thanks for your reply!
I tried to use your code, but there is no "prevAssemblies" variable anywhere. Seems like it is your code.
Can you please, show, where exactly you added this variable and what you do with it?

Or maybe you can upload full Unity project?
It will be extremely helpful.

@Artaani Hey, sorry there was a line that went missing in the formatting.
I've updated the comment above with the full code. It replaces this line in ScriptBundleLoader.cs:
domain.GetAssemblies().Select(a => a.Location).ToArray();

@Artaani
As I suspected, there's a way of doing it without try catch, which is a bit neater.

var prevAssemblies = domain.GetAssemblies();
List<string> tempAssemblyRefs = new List<string>();
for (int i = 0; i < prevAssemblies.Length; i++)
{
    // In .Net 4 this would would be: if (prevAssemblies[i].IsDynamic)
    if (prevAssemblies[i].ManifestModule is System.Reflection.Emit.ModuleBuilder) 
        continue;
    tempAssemblyRefs.Add(prevAssemblies[i].Location);
}
this.assemblyReferences = tempAssemblyRefs.ToArray();

@Veguard Wow! Perfect!
You basically made C# runtime compiler for Unity! Without this fix it was unusable.
Great job! Thanks you very much!

@Veguard thank you