29 using System.CodeDom.Compiler;
30 using System.Collections.Generic;
31 using System.Globalization;
32 using System.Reflection;
35 using Microsoft.CSharp;
37 using Microsoft.VisualBasic;
40 using OpenSim.Region.Framework.Interfaces;
41 using OpenSim.Region.ScriptEngine.Interfaces;
44 namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
57 internal enum enumCompileType
67 public int LinesToRemoveOnError = 3;
68 private enumCompileType DefaultCompileLanguage;
69 private bool WriteScriptSourceToDebugFile;
70 private bool CompileWithDebugInformation;
71 private Dictionary<string, bool> AllowedCompilers =
new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);
72 private Dictionary<string, enumCompileType> LanguageMapping =
new Dictionary<string, enumCompileType>(StringComparer.CurrentCultureIgnoreCase);
73 private bool m_insertCoopTerminationCalls;
75 private string FilePrefix;
76 private string ScriptEnginesPath = null;
80 private List<string> m_warnings =
new List<string>();
84 private static CSharpCodeProvider CScodeProvider =
new CSharpCodeProvider();
85 private static VBCodeProvider VBcodeProvider =
new VBCodeProvider();
88 private static UInt64 scriptCompileCounter = 0;
91 private Dictionary<string, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>> m_lineMaps =
92 new Dictionary<string, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>>();
94 public bool in_startup =
true;
98 m_scriptEngine = scriptEngine;
99 ScriptEnginesPath = scriptEngine.ScriptEnginePath;
106 WriteScriptSourceToDebugFile = m_scriptEngine.Config.GetBoolean(
"WriteScriptSourceToDebugFile",
false);
107 CompileWithDebugInformation = m_scriptEngine.Config.GetBoolean(
"CompileWithDebugInformation",
true);
108 bool DeleteScriptsOnStartup = m_scriptEngine.Config.GetBoolean(
"DeleteScriptsOnStartup",
true);
109 m_insertCoopTerminationCalls = m_scriptEngine.Config.GetString(
"ScriptStopStrategy",
"abort") ==
"co-op";
112 FilePrefix =
"CommonCompiler";
113 foreach (
char c
in Path.GetInvalidFileNameChars())
115 FilePrefix = FilePrefix.Replace(c,
'_');
121 CheckOrCreateScriptsDirectory();
124 if (DeleteScriptsOnStartup)
129 LanguageMapping.Add(enumCompileType.cs.ToString(), enumCompileType.cs);
130 LanguageMapping.Add(enumCompileType.vb.ToString(), enumCompileType.vb);
131 LanguageMapping.Add(enumCompileType.lsl.ToString(), enumCompileType.lsl);
134 string allowComp = m_scriptEngine.Config.GetString(
"AllowedCompilers",
"lsl");
135 AllowedCompilers.Clear();
138 m_log.Debug(
"[Compiler]: Allowed languages: " + allowComp);
142 foreach (
string strl
in allowComp.Split(
','))
144 string strlan = strl.Trim(
" \t".ToCharArray()).ToLower();
145 if (!LanguageMapping.ContainsKey(strlan))
147 m_log.Error(
"[Compiler]: Config error. Compiler is unable to recognize language type \"" + strlan +
"\" specified in \"AllowedCompilers\".");
155 AllowedCompilers.Add(strlan,
true);
157 if (AllowedCompilers.Count == 0)
158 m_log.Error(
"[Compiler]: Config error. Compiler could not recognize any language in \"AllowedCompilers\". Scripts will not be executed!");
161 string defaultCompileLanguage = m_scriptEngine.Config.GetString(
"DefaultCompileLanguage",
"lsl").ToLower();
164 if (!LanguageMapping.ContainsKey(defaultCompileLanguage))
166 m_log.Error(
"[Compiler]: " +
167 "Config error. Default language \"" + defaultCompileLanguage +
"\" specified in \"DefaultCompileLanguage\" is not recognized as a valid language. Changing default to: \"lsl\".");
168 defaultCompileLanguage =
"lsl";
172 if (!AllowedCompilers.ContainsKey(defaultCompileLanguage))
174 m_log.Error(
"[Compiler]: " +
175 "Config error. Default language \"" + defaultCompileLanguage +
"\"specified in \"DefaultCompileLanguage\" is not in list of \"AllowedCompilers\". Scripts may not be executed!");
184 DefaultCompileLanguage = LanguageMapping[defaultCompileLanguage];
193 private void CheckOrCreateScriptsDirectory()
195 if (!Directory.Exists(ScriptEnginesPath))
199 Directory.CreateDirectory(ScriptEnginesPath);
203 m_log.Error(
"[Compiler]: Exception trying to create ScriptEngine directory \"" + ScriptEnginesPath +
"\": " + ex.ToString());
207 if (!Directory.Exists(Path.Combine(ScriptEnginesPath,
208 m_scriptEngine.World.RegionInfo.RegionID.ToString())))
212 Directory.CreateDirectory(Path.Combine(ScriptEnginesPath,
213 m_scriptEngine.World.RegionInfo.RegionID.ToString()));
217 m_log.Error(
"[Compiler]: Exception trying to create ScriptEngine directory \"" + Path.Combine(ScriptEnginesPath,
218 m_scriptEngine.World.RegionInfo.RegionID.ToString()) +
"\": " + ex.ToString());
226 private void DeleteOldFiles()
228 foreach (
string file
in Directory.GetFiles(Path.Combine(ScriptEnginesPath,
229 m_scriptEngine.World.RegionInfo.RegionID.ToString()), FilePrefix +
"_compiled*"))
237 m_log.Error(
"[Compiler]: Exception trying delete old script file \"" + file +
"\": " + ex.ToString());
240 foreach (
string file
in Directory.GetFiles(Path.Combine(ScriptEnginesPath,
241 m_scriptEngine.World.RegionInfo.RegionID.ToString()), FilePrefix +
"_source*"))
249 m_log.Error(
"[Compiler]: Exception trying delete old script file \"" + file +
"\": " + ex.ToString());
273 return Path.Combine(ScriptEnginesPath, Path.Combine(
274 m_scriptEngine.World.RegionInfo.RegionID.ToString(),
275 FilePrefix +
"_compiled_" + assetID +
".dll"));
280 return GetCompilerOutput(assetID.ToString());
284 string source,
string asset, UUID ownerUUID,
285 out
string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
287 PerformScriptCompile(source, asset, ownerUUID,
false, out assembly, out linemap);
291 string source,
string asset, UUID ownerUUID,
bool alwaysRecompile,
292 out
string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
301 assembly = GetCompilerOutput(asset);
305 CheckOrCreateScriptsDirectory();
309 if (!alwaysRecompile && File.Exists(assembly) && File.Exists(assembly +
".text") && File.Exists(assembly +
".map"))
316 if (!m_lineMaps.ContainsKey(assembly))
317 m_lineMaps[assembly] = ReadMapFile(assembly +
".map");
318 linemap = m_lineMaps[assembly];
324 if (source ==
String.Empty)
325 throw new Exception(
"Cannot find script assembly and no script text present");
327 enumCompileType language = DefaultCompileLanguage;
329 if (source.StartsWith(
"//c#",
true, CultureInfo.InvariantCulture))
330 language = enumCompileType.cs;
331 if (source.StartsWith(
"//vb",
true, CultureInfo.InvariantCulture))
333 language = enumCompileType.vb;
336 source = source.Substring(4, source.Length - 4);
338 if (source.StartsWith(
"//lsl",
true, CultureInfo.InvariantCulture))
339 language = enumCompileType.lsl;
343 if (!AllowedCompilers.ContainsKey(language.ToString()))
346 string errtext = String.Empty;
347 errtext +=
"The compiler for language \"" + language.ToString() +
"\" is not in list of allowed compilers. Script will not be executed!";
348 throw new Exception(errtext);
351 if (m_scriptEngine.World.Permissions.CanCompileScript(ownerUUID, (
int)language) ==
false)
354 string errtext = String.Empty;
355 errtext += ownerUUID +
" is not in list of allowed users for this scripting language. Script will not be executed!";
356 throw new Exception(errtext);
359 string compileScript = source;
361 if (language == enumCompileType.lsl)
365 compileScript = LSL_Converter.Convert(source);
368 foreach (
string warning
in LSL_Converter.GetWarnings())
375 m_lineMaps[assembly] = linemap;
376 WriteMapFile(assembly +
".map", linemap);
381 case enumCompileType.cs:
382 case enumCompileType.lsl:
383 compileScript = CreateCSCompilerScript(
385 m_scriptEngine.ScriptClassName,
386 m_scriptEngine.ScriptBaseClassName,
387 m_scriptEngine.ScriptBaseClassParameters);
389 case enumCompileType.vb:
390 compileScript = CreateVBCompilerScript(
391 compileScript, m_scriptEngine.ScriptClassName, m_scriptEngine.ScriptBaseClassName);
395 assembly = CompileFromDotNetText(compileScript, language, asset, assembly);
400 return m_warnings.ToArray();
403 private void AddWarning(
string warning)
405 if (!m_warnings.Contains(warning))
407 m_warnings.Add(warning);
423 string compileScript,
string className,
string baseClassName, ParameterInfo[] constructorParameters)
425 compileScript = string.Format(
426 @"using OpenSim.Region.ScriptEngine.Shared;
427 using System.Collections.Generic;
431 public class {0} : {1}
433 public {0}({2}) : base({3}) {{}}
439 constructorParameters != null
440 ? string.Join(
", ", Array.ConvertAll<ParameterInfo,
string>(constructorParameters, pi => pi.ToString()))
442 constructorParameters != null
443 ?
string.Join(
", ",
Array.ConvertAll<ParameterInfo,
string>(constructorParameters, pi => pi.Name))
447 return compileScript;
452 compileScript = String.Empty +
453 "Imports OpenSim.Region.ScriptEngine.Shared: Imports System.Collections.Generic: " +
454 String.Empty +
"NameSpace SecondLife:" +
455 String.Empty +
"Public Class " + className +
": Inherits " + baseClassName +
456 "\r\nPublic Sub New()\r\nEnd Sub: " +
458 ":End Class :End Namespace\r\n";
460 return compileScript;
468 internal string CompileFromDotNetText(
string Script, enumCompileType lang,
string asset,
string assembly)
472 string ext =
"." + lang.ToString();
475 scriptCompileCounter++;
478 if (File.Exists(assembly))
480 File.SetAttributes(assembly, FileAttributes.Normal);
481 File.Delete(assembly);
486 throw new Exception(
"Unable to delete old existing " +
487 "script-file before writing new. Compile aborted: " +
492 if (WriteScriptSourceToDebugFile)
494 string srcFileName = FilePrefix +
"_source_" +
495 Path.GetFileNameWithoutExtension(assembly) + ext;
498 File.WriteAllText(Path.Combine(Path.Combine(
500 m_scriptEngine.World.RegionInfo.RegionID.ToString()),
501 srcFileName), Script);
505 m_log.Error(
"[Compiler]: Exception while " +
506 "trying to write script source to file \"" +
507 srcFileName +
"\": " + ex.ToString());
512 CompilerParameters parameters =
new CompilerParameters();
514 parameters.IncludeDebugInformation =
true;
516 string rootPath = AppDomain.CurrentDomain.BaseDirectory;
518 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
519 "OpenSim.Region.ScriptEngine.Shared.dll"));
520 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
521 "OpenSim.Region.ScriptEngine.Shared.Api.Runtime.dll"));
522 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
523 "OpenMetaverseTypes.dll"));
525 if (m_scriptEngine.ScriptReferencedAssemblies != null)
526 Array.ForEach<
string>(
527 m_scriptEngine.ScriptReferencedAssemblies,
528 a => parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, a)));
530 parameters.GenerateExecutable =
false;
531 parameters.OutputAssembly = assembly;
532 parameters.IncludeDebugInformation = CompileWithDebugInformation;
534 parameters.TreatWarningsAsErrors =
false;
536 CompilerResults results;
539 case enumCompileType.vb:
540 results = VBcodeProvider.CompileAssemblyFromSource(
543 case enumCompileType.cs:
544 case enumCompileType.lsl:
545 bool complete =
false;
546 bool retried =
false;
549 lock (CScodeProvider)
551 results = CScodeProvider.CompileAssemblyFromSource(
560 if (results.Errors.Count > 0)
562 if (!retried &&
string.IsNullOrEmpty(results.Errors[0].FileName) &&
563 results.Errors[0].Line == 0)
580 throw new Exception(
"Compiler is not able to recongnize " +
581 "language type \"" + lang.ToString() +
"\"");
595 bool hadErrors =
false;
596 string errtext = String.Empty;
597 if (results.Errors.Count > 0)
599 foreach (CompilerError CompErr
in results.Errors)
601 string severity = CompErr.IsWarning ?
"Warning" :
"Error";
603 KeyValuePair<int, int> errorPos;
607 if (severity ==
"Error")
610 if (!m_lineMaps.ContainsKey(assembly))
611 errorPos =
new KeyValuePair<int, int>(CompErr.Line, CompErr.Column);
613 errorPos = FindErrorPosition(CompErr.Line, CompErr.Column, m_lineMaps[assembly]);
615 string text = CompErr.ErrorText;
618 if (lang == enumCompileType.lsl)
619 text = ReplaceTypes(CompErr.ErrorText);
623 errtext += String.Format(
"({0},{1}): {4} {2}: {3}\n",
624 errorPos.Key - 1, errorPos.Value - 1,
625 CompErr.ErrorNumber, text, severity);
633 throw new Exception(errtext);
640 if (!
File.Exists(assembly))
642 for (
int i = 0; i < 20 && !File.Exists(assembly); i++)
644 System.Threading.Thread.Sleep(250);
647 if (!
File.Exists(assembly))
649 errtext = String.Empty;
650 errtext +=
"No compile error. But not able to locate compiled file.";
651 throw new Exception(errtext);
663 FileInfo fi =
new FileInfo(assembly);
667 errtext = String.Empty;
668 errtext +=
"No compile error. But not able to stat file.";
669 throw new Exception(errtext);
672 Byte[] data =
new Byte[fi.Length];
676 using (FileStream fs = File.Open(assembly, FileMode.Open, FileAccess.Read))
677 fs.Read(data, 0, data.Length);
681 errtext = String.Empty;
682 errtext +=
"No compile error. But not able to open file.";
683 throw new Exception(errtext);
688 string filetext = System.Convert.ToBase64String(data);
690 Byte[] buf = Encoding.ASCII.GetBytes(filetext);
692 using (FileStream sfs = File.Create(assembly +
".text"))
693 sfs.Write(buf, 0, buf.Length);
698 private class kvpSorter : IComparer<KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>>>
700 public int Compare(KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> a,
701 KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> b)
703 int kc = a.Key.Key.CompareTo(b.Key.Key);
704 return (kc != 0) ? kc : a.Key.Value.CompareTo(b.Key.Value);
709 int col, Dictionary<KeyValuePair<int, int>,
710 KeyValuePair<int, int>> positionMap)
712 if (positionMap == null || positionMap.Count == 0)
713 return new KeyValuePair<int, int>(line, col);
715 KeyValuePair<int, int> ret =
new KeyValuePair<int, int>();
717 if (positionMap.TryGetValue(
new KeyValuePair<int, int>(line, col),
721 var sorted =
new List<KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>>>(positionMap);
723 sorted.Sort(
new kvpSorter());
729 foreach (KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> posmap
in sorted)
732 int nl = posmap.Value.Key + line - posmap.Key.Key;
733 int nc = posmap.Value.Value + col - posmap.Key.Value;
735 if (posmap.Key.Key > line)
745 if (posmap.Key.Key == line && posmap.Key.Value > col)
748 if (nl > l || (nl == l && nc > c))
759 l = posmap.Value.Key;
760 c = posmap.Value.Value;
762 return new KeyValuePair<int, int>(l, c);
765 string ReplaceTypes(
string message)
767 message = message.Replace(
768 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString",
771 message = message.Replace(
772 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger",
775 message = message.Replace(
776 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat",
779 message = message.Replace(
780 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.list",
786 private static void WriteMapFile(
string filename, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
788 string mapstring = String.Empty;
789 foreach (KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> kvp
in linemap)
791 KeyValuePair<int, int> k = kvp.Key;
792 KeyValuePair<int, int> v = kvp.Value;
793 mapstring += String.Format(
"{0},{1},{2},{3}\n", k.Key, k.Value, v.Key, v.Value);
796 Byte[] mapbytes = Encoding.ASCII.GetBytes(mapstring);
798 using (FileStream mfs = File.Create(filename))
799 mfs.Write(mapbytes, 0, mapbytes.Length);
802 private static Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> ReadMapFile(
string filename)
804 Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap;
807 using (StreamReader r = File.OpenText(filename))
809 linemap =
new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
812 while ((line = r.ReadLine()) != null)
814 String[] parts = line.Split(
new Char[] {
',' });
815 int kk = System.Convert.ToInt32(parts[0]);
816 int kv = System.Convert.ToInt32(parts[1]);
817 int vk = System.Convert.ToInt32(parts[2]);
818 int vv = System.Convert.ToInt32(parts[3]);
820 KeyValuePair<int, int> k =
new KeyValuePair<int, int>(kk, kv);
821 KeyValuePair<int, int> v =
new KeyValuePair<int, int>(vk, vv);
829 linemap =
new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
An interface for a script API module to communicate with the engine it's running under ...
Interface for communication between OpenSim modules and in-world scripts
Interactive OpenSim region server