1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . IO ;
4+ using System . Reflection ;
5+ using System . Reflection . Emit ;
6+ using System . Text ;
7+ using Mono . CSharp ;
8+
9+ namespace ScriptLoader
10+ {
11+ public interface ICSharpSource
12+ {
13+ byte [ ] Bytes { get ; }
14+ string Location { get ; }
15+ string Name { get ; }
16+ }
17+
18+ public class CSharpFile : ICSharpSource
19+ {
20+ public CSharpFile ( string path )
21+ {
22+ if ( ! File . Exists ( path ) )
23+ throw new FileNotFoundException ( "The given source file does not exist!" , path ) ;
24+ if ( Path . GetExtension ( path ) ? . ToLowerInvariant ( ) != ".cs" )
25+ throw new ArgumentException ( "The given path is not a .cs file!" , nameof ( path ) ) ;
26+ Location = path ;
27+ }
28+
29+ public byte [ ] Bytes => File . ReadAllBytes ( Location ) ;
30+ public string Location { get ; }
31+
32+ public string Name => Path . GetFileName ( Location ) ;
33+ }
34+
35+ public class CSharpCode : ICSharpSource
36+ {
37+ public CSharpCode ( )
38+ {
39+ SourceCode = new StringBuilder ( ) ;
40+ }
41+
42+ public StringBuilder SourceCode { get ; }
43+
44+ public byte [ ] Bytes => Encoding . UTF8 . GetBytes ( SourceCode . ToString ( ) ) ;
45+ public string Location => "<eval>" ;
46+ public string Name => "<eval>" ;
47+ }
48+
49+ public static class MonoCompiler
50+ {
51+ private static readonly HashSet < string > StdLib =
52+ new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) { "mscorlib" , "System.Core" , "System" , "System.Xml" } ;
53+
54+ //public MonoCompiler(TextWriter logger) : this(null, logger) { }
55+ //public MonoCompiler() : this(null, null) { }
56+
57+ //public MonoCompiler(CompilerContext ctx, TextWriter logger) : base(ctx ?? CreateContext(reportPrinter))
58+ //{
59+ // Reporter = logger != null ? CreatePrinter(logger) : new ConsoleReportPrinter();
60+ // Logger = logger;
61+ // ImportAppdomainAssemblies(ReferenceAssembly);
62+ // ActiveDomain.AssemblyLoad += OnAssemblyLoad;
63+ //}
64+
65+ //private AppDomain ActiveDomain => AppDomain.CurrentDomain;
66+ //private TextWriter Logger { get; }
67+ //private ReportPrinter Reporter { get; }
68+
69+ //public void Dispose()
70+ //{
71+ // ActiveDomain.AssemblyLoad -= OnAssemblyLoad;
72+ //}
73+
74+ // Mimicked from https://github.com/kkdevs/Patchwork/blob/master/Patchwork/MonoScript.cs#L124
75+ public static Assembly Compile < T > ( IList < T > sources , IEnumerable < Assembly > imports = null , TextWriter logger = null ) where T : ICSharpSource
76+ {
77+ ReportPrinter reporter = logger == null ? new ConsoleReportPrinter ( ) : new StreamReportPrinter ( logger ) ;
78+ Location . Reset ( ) ;
79+
80+ string dllName = $ "compiled_{ DateTime . Now . Ticks } ";
81+
82+ CompilerContext ctx = CreateContext ( reporter ) ;
83+ ctx . Settings . SourceFiles . Clear ( ) ;
84+
85+ int i = 0 ;
86+
87+ SeekableStreamReader GetFile ( SourceFile file )
88+ {
89+ return new SeekableStreamReader ( new MemoryStream ( sources [ file . Index ] . Bytes ) , Encoding . UTF8 ) ;
90+ }
91+
92+ foreach ( ICSharpSource source in sources )
93+ {
94+ ctx . Settings . SourceFiles . Add ( new SourceFile ( source . Name , source . Location , i , GetFile ) ) ;
95+ i ++ ;
96+ }
97+
98+ var container = new ModuleContainer ( ctx ) ;
99+
100+ RootContext . ToplevelTypes = container ;
101+ Location . Initialize ( ctx . Settings . SourceFiles ) ;
102+
103+ var session = new ParserSession { UseJayGlobalArrays = true , LocatedTokens = new LocatedToken [ 15000 ] } ;
104+ container . EnableRedefinition ( ) ;
105+
106+ foreach ( SourceFile sourceFile in ctx . Settings . SourceFiles )
107+ {
108+ SeekableStreamReader stream = sourceFile . GetInputStream ( sourceFile ) ;
109+ var source = new CompilationSourceFile ( container , sourceFile ) ;
110+ source . EnableRedefinition ( ) ;
111+ container . AddTypeContainer ( source ) ;
112+ var parser = new CSharpParser ( stream , source , session ) ;
113+ parser . parse ( ) ;
114+ }
115+
116+ var ass = new AssemblyDefinitionDynamic ( container , dllName , $ "{ dllName } .dll") ;
117+ container . SetDeclaringAssembly ( ass ) ;
118+
119+ var importer = new ReflectionImporter ( container , ctx . BuiltinTypes ) ;
120+ ass . Importer = importer ;
121+
122+ var loader = new DynamicLoader ( importer , ctx ) ;
123+ ImportAppdomainAssemblies ( a => importer . ImportAssembly ( a , container . GlobalRootNamespace ) ) ;
124+
125+ if ( imports != null )
126+ foreach ( Assembly assembly in imports )
127+ importer . ImportAssembly ( assembly , container . GlobalRootNamespace ) ;
128+
129+ loader . LoadReferences ( container ) ;
130+ ass . Create ( AppDomain . CurrentDomain , AssemblyBuilderAccess . RunAndSave ) ;
131+ container . CreateContainer ( ) ;
132+ loader . LoadModules ( ass , container . GlobalRootNamespace ) ;
133+ container . InitializePredefinedTypes ( ) ;
134+ container . Define ( ) ;
135+
136+ if ( ctx . Report . Errors > 0 )
137+ {
138+ logger ? . WriteLine ( "Found errors! Aborting compilation..." ) ;
139+ return null ;
140+ }
141+
142+ try
143+ {
144+ ass . Resolve ( ) ;
145+ ass . Emit ( ) ;
146+ container . CloseContainer ( ) ;
147+ ass . EmbedResources ( ) ;
148+ }
149+ catch ( Exception e )
150+ {
151+ logger ? . WriteLine ( $ "Failed to compile because { e } ") ;
152+ return null ;
153+ }
154+
155+ return ass . Builder ;
156+ }
157+
158+ //private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
159+ //{
160+ // ReferenceAssembly(args.LoadedAssembly);
161+ //}
162+
163+ private static void ImportAppdomainAssemblies ( Action < Assembly > import )
164+ {
165+ foreach ( Assembly assembly in AppDomain . CurrentDomain . GetAssemblies ( ) )
166+ {
167+ string name = assembly . GetName ( ) . Name ;
168+ if ( StdLib . Contains ( name ) )
169+ continue ;
170+ import ( assembly ) ;
171+ }
172+ }
173+
174+ private static CompilerContext CreateContext ( ReportPrinter reportPrinter )
175+ {
176+ var settings = new CompilerSettings
177+ {
178+ Version = LanguageVersion . Experimental ,
179+ GenerateDebugInfo = false ,
180+ StdLib = true ,
181+ Target = Target . Library
182+ } ;
183+
184+ return new CompilerContext ( settings , reportPrinter ) ;
185+ }
186+
187+ //private static ReportPrinter CreatePrinter(TextWriter tw)
188+ //{
189+ // return new StreamReportPrinter(tw);
190+ //}
191+ }
192+ }
0 commit comments