1+ extern alias References ;
12using Oxide . Core ;
23using Oxide . Core . Logging ;
34using Oxide . Logging ;
1011using System . Threading ;
1112using Oxide . CSharp . Common ;
1213using Oxide . CSharp . CompilerStream ;
14+ using Oxide . Pooling ;
15+ using References ::Mono . Cecil ;
1316
1417namespace Oxide . Plugins
1518{
1619 internal class Compilation
1720 {
18- public static Compilation Current ;
21+ public static Compilation ? Current ;
1922
2023 internal int id ;
2124 internal string name ;
@@ -29,7 +32,6 @@ internal class Compilation
2932 internal CompiledAssembly compiledAssembly ;
3033 internal float duration => endedAt - startedAt ;
3134
32- private string includePath ;
3335 private string [ ] extensionNames ;
3436
3537 internal Compilation ( int id , Action < Compilation > callback , List < CompilablePlugin > plugins )
@@ -38,18 +40,16 @@ internal Compilation(int id, Action<Compilation> callback, List<CompilablePlugin
3840 this . callback = callback ;
3941 queuedPlugins = new ConcurrentHashSet < CompilablePlugin > ( plugins ) ;
4042
41- if ( Current == null )
42- {
43- Current = this ;
44- }
43+ Current ??= this ;
4544
46- foreach ( CompilablePlugin plugin in plugins )
45+ int pluginCount = plugins . Count ;
46+ for ( int i = 0 ; i < pluginCount ; i ++ )
4747 {
48+ CompilablePlugin plugin = plugins [ i ] ;
4849 plugin . CompilerErrors . Clear ( ) ;
4950 plugin . OnCompilationStarted ( ) ;
5051 }
5152
52- includePath = Path . Combine ( Interface . Oxide . PluginDirectory , "include" ) ;
5353 extensionNames = Interface . Oxide . GetAllExtensions ( ) . Select ( ext => ext . Name ) . ToArray ( ) ;
5454 }
5555
@@ -58,7 +58,7 @@ internal void Started()
5858 name = ( plugins . Count < 2 ? plugins . First ( ) . Name : "plugins_" ) + Math . Round ( Interface . Oxide . Now * 10000000f ) + ".dll" ;
5959 }
6060
61- internal void Completed ( byte [ ] rawAssembly = null , byte [ ] symbols = null )
61+ internal void Completed ( byte [ ] ? rawAssembly = null , byte [ ] ? symbols = null )
6262 {
6363 endedAt = Interface . Oxide . Now ;
6464 if ( plugins . Count > 0 && rawAssembly != null )
@@ -144,56 +144,66 @@ internal void Prepare(Action callback)
144144
145145 Interface . Oxide . RootLogger . WriteDebug ( LogType . Info , LogEvent . Compile , "CSharp" , $ "Preparing compilation") ;
146146
147- List < CompilablePlugin > pluginsToAdd = new List < CompilablePlugin > ( ) ;
148-
149- while ( queuedPlugins . TryDequeue ( out CompilablePlugin plugin ) )
147+ List < CompilablePlugin > pluginsToAdd = PoolFactory < List < CompilablePlugin > > . Shared . Take ( ) ;
148+ try
150149 {
151- if ( Current == null )
152- {
153- Current = this ;
154- }
155150
156- if ( ! CacheScriptLines ( plugin ) || plugin . ScriptLines . Length < 1 )
151+ while ( queuedPlugins . TryDequeue ( out CompilablePlugin plugin ) )
157152 {
158- plugin . References . Clear ( ) ;
159- plugin . IncludePaths . Clear ( ) ;
160- plugin . Requires . Clear ( ) ;
161- Interface . Oxide . RootLogger . WriteDebug ( LogType . Error , LogEvent . Compile , "CSharp" , $ "Script file is empty: { plugin . Name } ") ;
162- RemovePlugin ( plugin ) ;
163- }
153+ Current ??= this ;
164154
165- if ( ! pluginsToAdd . Contains ( plugin ) )
166- {
167- pluginsToAdd . Add ( plugin ) ;
155+ if ( ! CacheScriptLines ( plugin ) || plugin . ScriptLines . Length < 1 )
156+ {
157+ plugin . References . Clear ( ) ;
158+ plugin . IncludePaths . Clear ( ) ;
159+ plugin . Requires . Clear ( ) ;
160+ Interface . Oxide . RootLogger . WriteDebug ( LogType . Error , LogEvent . Compile , "CSharp" , $ "Script file is empty: { plugin . Name } ") ;
161+ RemovePlugin ( plugin ) ;
162+ }
168163
169- PreparseScript ( plugin ) ;
170- ResolveReferences ( plugin ) ;
171- }
172- else
173- {
174- Interface . Oxide . RootLogger . WriteDebug ( LogType . Error , LogEvent . Compile , "CSharp" , $ "Plugin is already part of the compilation: { plugin . Name } ") ;
175- }
164+ if ( ! pluginsToAdd . Contains ( plugin ) )
165+ {
166+ pluginsToAdd . Add ( plugin ) ;
176167
177- CacheModifiedScripts ( ) ;
168+ PreparseScript ( plugin ) ;
178169
179- // We don't want the main thread to be able to add more plugins which could be missed
180- if ( queuedPlugins . Count == 0 && Current == this )
181- {
182- Current = null ;
170+ ResolveReferences ( plugin ) ;
171+ }
172+ else
173+ {
174+ Interface . Oxide . RootLogger . WriteDebug ( LogType . Error , LogEvent . Compile , "CSharp" , $ "Plugin is already part of the compilation: { plugin . Name } ") ;
175+ }
176+
177+ CacheModifiedScripts ( ) ;
178+
179+ // We don't want the main thread to be able to add more plugins which could be missed
180+ if ( queuedPlugins . Count == 0 && Current == this )
181+ {
182+ Current = null ;
183+ }
183184 }
184- }
185185
186- pluginsToAdd . Sort ( ( x , y ) => string . Compare ( x . Name , y . Name , StringComparison . Ordinal ) ) ;
186+ pluginsToAdd . Sort ( ( x , y ) => string . Compare ( x . Name , y . Name , StringComparison . Ordinal ) ) ;
187187
188- foreach ( CompilablePlugin plugin in pluginsToAdd )
189- {
190- if ( ! plugins . Add ( plugin ) )
188+ int pluginCount = pluginsToAdd . Count ;
189+ for ( int i = 0 ; i < pluginCount ; i ++ )
191190 {
192- Interface . Oxide . RootLogger . WriteDebug ( LogType . Error , LogEvent . Compile , "CSharp" , $ "Failed to add plugin to compilation: { plugin . Name } ") ;
193- continue ;
194- }
191+ CompilablePlugin plugin = pluginsToAdd [ i ] ;
192+ if ( ! plugins . Add ( plugin ) )
193+ {
194+ Interface . Oxide . RootLogger . WriteDebug ( LogType . Error , LogEvent . Compile , "CSharp" ,
195+ $ "Failed to add plugin to compilation: { plugin . Name } ") ;
196+ continue ;
197+ }
195198
196- Interface . Oxide . RootLogger . WriteDebug ( LogType . Info , LogEvent . Compile , "CSharp" , $ "Added plugin to compilation: { plugin . Name } ") ;
199+ Interface . Oxide . RootLogger . WriteDebug ( LogType . Info , LogEvent . Compile , "CSharp" ,
200+ $ "Added plugin to compilation: { plugin . Name } ") ;
201+ }
202+ }
203+ finally
204+ {
205+ pluginsToAdd . Clear ( ) ;
206+ PoolFactory < List < CompilablePlugin > > . Shared . Return ( pluginsToAdd ) ;
197207 }
198208
199209 Interface . Oxide . RootLogger . WriteDebug ( LogType . Info , LogEvent . Compile , "CSharp" , $ "Done preparing compilation: { plugins . Select ( p => p . Name ) . ToSentence ( ) } ") ;
@@ -214,11 +224,11 @@ private void PreparseScript(CompilablePlugin plugin)
214224 plugin . Requires . Clear ( ) ;
215225
216226 bool parsingNamespace = false ;
217- for ( int i = 0 ; i < plugin . ScriptLines . Length ; i ++ )
227+ int scriptLineCount = plugin . ScriptLines . Length ;
228+ for ( int i = 0 ; i < scriptLineCount ; i ++ )
218229 {
219230 string line = plugin . ScriptLines [ i ] . Trim ( ) ;
220-
221- if ( line . IndexOf ( "namespace uMod.Plugins" , StringComparison . InvariantCultureIgnoreCase ) >= 0 )
231+ if ( line . IndexOf ( Constants . UmodNamespace , StringComparison . InvariantCultureIgnoreCase ) >= 0 )
222232 {
223233 Interface . Oxide . LogError ( $ "Plugin { plugin . ScriptName } .cs is a uMod plugin, not an Oxide plugin. Please downgrade to the Oxide version if available.") ;
224234 plugin . CompilerErrors . Add ( $ "Plugin { plugin . ScriptName } .cs is a uMod plugin, not an Oxide plugin. Please downgrade to the Oxide version if available.") ;
@@ -234,16 +244,8 @@ private void PreparseScript(CompilablePlugin plugin)
234244 Match match ;
235245 if ( parsingNamespace )
236246 {
237- // Skip blank lines and opening brace at the top of the namespace block
238- match = Constants . BlankLineRegex . Match ( line ) ;
239- if ( match . Success )
240- {
241- continue ;
242- }
243-
244- // Skip class custom attributes
245- match = Constants . CustomAttributeRegex . Match ( line ) ;
246- if ( match . Success )
247+ // Skip opening brace at the top of the namespace block & class custom attributes
248+ if ( line . StartsWith ( "[" ) || line . StartsWith ( "{" ) )
247249 {
248250 continue ;
249251 }
@@ -272,7 +274,7 @@ private void PreparseScript(CompilablePlugin plugin)
272274 {
273275 string dependencyName = match . Groups [ 1 ] . Value ;
274276 plugin . Requires . Add ( dependencyName ) ;
275- if ( ! File . Exists ( Path . Combine ( plugin . Directory , dependencyName + " .cs") ) )
277+ if ( ! File . Exists ( Path . Combine ( plugin . Directory , $ " { dependencyName } .cs") ) )
276278 {
277279 Interface . Oxide . LogError ( $ "{ plugin . Name } plugin requires missing dependency: { dependencyName } ") ;
278280 plugin . CompilerErrors . Add ( $ "Missing dependency: { dependencyName } ") ;
@@ -323,8 +325,7 @@ private void PreparseScript(CompilablePlugin plugin)
323325 }
324326
325327 // Start parsing the Oxide.Plugins namespace contents
326- match = Constants . NamespaceRegex . Match ( line ) ;
327- if ( match . Success )
328+ if ( line . IndexOf ( Constants . OxideNamespace , StringComparison . InvariantCultureIgnoreCase ) >= 0 )
328329 {
329330 parsingNamespace = true ;
330331 }
@@ -348,9 +349,9 @@ private void ResolveReferences(CompilablePlugin plugin)
348349 continue ;
349350 }
350351
351- if ( Directory . Exists ( includePath ) )
352+ if ( Directory . Exists ( Constants . IncludePath ) )
352353 {
353- string includeFilePath = Path . Combine ( includePath , $ "Ext.{ name } .cs") ;
354+ string includeFilePath = Path . Combine ( Constants . IncludePath , $ "Ext.{ name } .cs") ;
354355 if ( File . Exists ( includeFilePath ) )
355356 {
356357 plugin . IncludePaths . Add ( includeFilePath ) ;
@@ -406,25 +407,24 @@ private void AddReference(CompilablePlugin plugin, string assemblyNameString)
406407 return ;
407408 }
408409
409- Assembly assembly ;
410- try
411- {
412- assembly = Assembly . Load ( assemblyNameString ) ;
413- }
414- catch ( FileNotFoundException )
410+ using AssemblyDefinition ? assemblyDefinition = AssemblyDefinition . ReadAssembly ( path ) ;
411+ if ( assemblyDefinition == null )
415412 {
416413 Interface . Oxide . LogError ( $ "Assembly referenced by { plugin . Name } plugin is invalid: { assemblyNameString } .dll") ;
417414 plugin . CompilerErrors . Add ( $ "Referenced assembly is invalid: { assemblyNameString } ") ;
418415 RemovePlugin ( plugin ) ;
419416 return ;
420417 }
421418
422- AssemblyName assemblyName = assembly . GetName ( ) ;
423- AddReference ( plugin , assemblyName , $ "{ assemblyName . Name } .dll") ;
419+ string assemblyName = assemblyDefinition . Name . Name ;
420+
421+ AddReference ( plugin , assemblyName , $ "{ assemblyName } .dll") ;
424422
425423 // Include references made by the referenced assembly
426- foreach ( AssemblyName reference in assembly . GetReferencedAssemblies ( ) )
424+ int referenceCount = assemblyDefinition . MainModule . AssemblyReferences . Count ;
425+ for ( int i = 0 ; i < referenceCount ; i ++ )
427426 {
427+ AssemblyNameReference reference = assemblyDefinition . MainModule . AssemblyReferences [ i ] ;
428428 // TODO: Fix Oxide.References to avoid these and other dependency conflicts
429429 if ( reference . Name . StartsWith ( "Newtonsoft.Json" ) || reference . Name . StartsWith ( "Rust.Workshop" ) )
430430 {
@@ -435,25 +435,25 @@ private void AddReference(CompilablePlugin plugin, string assemblyNameString)
435435 string referencePath = Path . Combine ( Interface . Oxide . ExtensionDirectory , referenceString ) ;
436436 if ( ! File . Exists ( referencePath ) )
437437 {
438- Interface . Oxide . LogWarning ( $ "Reference { reference . Name } .dll from { assembly . GetName ( ) . Name } .dll not found") ;
438+ Interface . Oxide . LogWarning ( $ "Reference { reference . Name } .dll from { assemblyName } .dll not found") ;
439439 continue ;
440440 }
441441
442- AddReference ( plugin , reference , referenceString ) ;
442+ AddReference ( plugin , reference . Name , referenceString ) ;
443443 }
444444 }
445445
446- private void AddReference ( CompilablePlugin plugin , AssemblyName reference , string referenceString )
446+ private void AddReference ( CompilablePlugin plugin , string reference , string referenceString )
447447 {
448448 if ( ! references . ContainsKey ( referenceString ) )
449449 {
450450 Interface . Oxide . RootLogger . WriteDebug ( LogType . Info , LogEvent . Compile , "CSharp" ,
451- $ "{ reference . Name } has been added as a reference") ;
451+ $ "{ reference } has been added as a reference") ;
452452
453453 references [ referenceString ] = CompilerFile . CachedReadFile ( Interface . Oxide . ExtensionDirectory , referenceString ) ;
454454 }
455455
456- plugin . References . Add ( reference . Name ) ;
456+ plugin . References . Add ( reference ) ;
457457 }
458458
459459 private bool CacheScriptLines ( CompilablePlugin plugin )
@@ -472,26 +472,34 @@ private bool CacheScriptLines(CompilablePlugin plugin)
472472 }
473473
474474 plugin . CheckLastModificationTime ( ) ;
475- if ( plugin . LastCachedScriptAt ! = plugin . LastModifiedAt )
475+ if ( plugin . LastCachedScriptAt = = plugin . LastModifiedAt )
476476 {
477- using ( StreamReader reader = File . OpenText ( plugin . ScriptPath ) )
478- {
479- List < string > lines = new List < string > ( ) ;
480- while ( ! reader . EndOfStream )
481- {
482- lines . Add ( reader . ReadLine ( ) ) ;
483- }
477+ return true ;
478+ }
484479
485- plugin . ScriptLines = lines . ToArray ( ) ;
486- plugin . ScriptEncoding = reader . CurrentEncoding ;
480+ using StreamReader streamReader = File . OpenText ( plugin . ScriptPath ) ;
481+ List < string > lines = PoolFactory < List < string > > . Shared . Take ( ) ;
482+ try
483+ {
484+ while ( ! streamReader . EndOfStream )
485+ {
486+ lines . Add ( streamReader . ReadLine ( ) ) ;
487487 }
488488
489+ plugin . ScriptLines = lines . ToArray ( ) ;
490+ plugin . ScriptEncoding = streamReader . CurrentEncoding ;
491+
489492 plugin . LastCachedScriptAt = plugin . LastModifiedAt ;
490493 if ( plugins . Remove ( plugin ) )
491494 {
492495 queuedPlugins . Add ( plugin ) ;
493496 }
494497 }
498+ finally
499+ {
500+ lines . Clear ( ) ;
501+ PoolFactory < List < string > > . Shared . Return ( lines ) ;
502+ }
495503
496504 return true ;
497505 }
@@ -519,8 +527,10 @@ private void CacheModifiedScripts()
519527 return ;
520528 }
521529
522- foreach ( CompilablePlugin plugin in modifiedPlugins )
530+ int modifiedPluginCount = modifiedPlugins . Length ;
531+ for ( int i = 0 ; i < modifiedPluginCount ; i ++ )
523532 {
533+ CompilablePlugin plugin = modifiedPlugins [ i ] ;
524534 CacheScriptLines ( plugin ) ;
525535 }
526536
@@ -540,12 +550,19 @@ private void RemovePlugin(CompilablePlugin plugin)
540550 plugin . OnCompilationFailed ( ) ;
541551
542552 // Remove plugins which are required by this plugin if they are only being compiled for this requirement
543- foreach ( CompilablePlugin requiredPlugin in plugins . Where ( pl => ! pl . IsCompilationNeeded && plugin . Requires . Contains ( pl . Name ) ) . ToArray ( ) )
553+ CompilablePlugin [ ] requiredPlugins = plugins . Where ( pl =>
554+ ! pl . IsCompilationNeeded && plugin . Requires . Contains ( pl . Name ) ) . ToArray ( ) ;
555+
556+ int requiredPluginCount = requiredPlugins . Length ;
557+ for ( int i = 0 ; i < requiredPluginCount ; i ++ )
544558 {
545- if ( ! plugins . Any ( pl => pl . Requires . Contains ( requiredPlugin . Name ) ) )
559+ CompilablePlugin requiredPlugin = requiredPlugins [ i ] ;
560+ if ( plugins . Any ( pl => pl . Requires . Contains ( requiredPlugin . Name ) ) )
546561 {
547- RemovePlugin ( requiredPlugin ) ;
562+ continue ;
548563 }
564+
565+ RemovePlugin ( requiredPlugin ) ;
549566 }
550567 }
551568 }
0 commit comments