OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
Compiler.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.CodeDom.Compiler;
30 using System.Collections.Generic;
31 using System.Globalization;
32 using System.Reflection;
33 using System.IO;
34 using System.Text;
35 using Microsoft.CSharp;
36 //using Microsoft.JScript;
37 using Microsoft.VisualBasic;
38 using log4net;
39 
40 using OpenSim.Region.Framework.Interfaces;
41 using OpenSim.Region.ScriptEngine.Interfaces;
42 using OpenMetaverse;
43 
44 namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
45 {
46  public class Compiler : ICompiler
47  {
48  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49 
50  // * Uses "LSL2Converter" to convert LSL to C# if necessary.
51  // * Compiles C#-code into an assembly
52  // * Returns assembly name ready for AppDomain load.
53  //
54  // Assembly is compiled using LSL_BaseClass as base. Look at debug C# code file created when LSL script is compiled for full details.
55  //
56 
57  internal enum enumCompileType
58  {
59  lsl = 0,
60  cs = 1,
61  vb = 2
62  }
63 
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;
74 
75  private string FilePrefix;
76  private string ScriptEnginesPath = null;
77  // mapping between LSL and C# line/column numbers
78  private ICodeConverter LSL_Converter;
79 
80  private List<string> m_warnings = new List<string>();
81 
82  // private object m_syncy = new object();
83 
84  private static CSharpCodeProvider CScodeProvider = new CSharpCodeProvider();
85  private static VBCodeProvider VBcodeProvider = new VBCodeProvider();
86 
87  // private static int instanceID = new Random().Next(0, int.MaxValue); // Unique number to use on our compiled files
88  private static UInt64 scriptCompileCounter = 0; // And a counter
89 
91  private Dictionary<string, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>> m_lineMaps =
92  new Dictionary<string, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>>();
93 
94  public bool in_startup = true;
95 
96  public Compiler(IScriptEngine scriptEngine)
97  {
98  m_scriptEngine = scriptEngine;
99  ScriptEnginesPath = scriptEngine.ScriptEnginePath;
100  ReadConfig();
101  }
102 
103  public void ReadConfig()
104  {
105  // Get some config
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";
110 
111  // Get file prefix from scriptengine name and make it file system safe:
112  FilePrefix = "CommonCompiler";
113  foreach (char c in Path.GetInvalidFileNameChars())
114  {
115  FilePrefix = FilePrefix.Replace(c, '_');
116  }
117 
118  if (in_startup)
119  {
120  in_startup = false;
121  CheckOrCreateScriptsDirectory();
122 
123  // First time we start? Delete old files
124  if (DeleteScriptsOnStartup)
125  DeleteOldFiles();
126  }
127 
128  // Map name and enum type of our supported languages
129  LanguageMapping.Add(enumCompileType.cs.ToString(), enumCompileType.cs);
130  LanguageMapping.Add(enumCompileType.vb.ToString(), enumCompileType.vb);
131  LanguageMapping.Add(enumCompileType.lsl.ToString(), enumCompileType.lsl);
132 
133  // Allowed compilers
134  string allowComp = m_scriptEngine.Config.GetString("AllowedCompilers", "lsl");
135  AllowedCompilers.Clear();
136 
137 #if DEBUG
138  m_log.Debug("[Compiler]: Allowed languages: " + allowComp);
139 #endif
140 
141 
142  foreach (string strl in allowComp.Split(','))
143  {
144  string strlan = strl.Trim(" \t".ToCharArray()).ToLower();
145  if (!LanguageMapping.ContainsKey(strlan))
146  {
147  m_log.Error("[Compiler]: Config error. Compiler is unable to recognize language type \"" + strlan + "\" specified in \"AllowedCompilers\".");
148  }
149  else
150  {
151 #if DEBUG
152  //m_log.Debug("[Compiler]: Config OK. Compiler recognized language type \"" + strlan + "\" specified in \"AllowedCompilers\".");
153 #endif
154  }
155  AllowedCompilers.Add(strlan, true);
156  }
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!");
159 
160  // Default language
161  string defaultCompileLanguage = m_scriptEngine.Config.GetString("DefaultCompileLanguage", "lsl").ToLower();
162 
163  // Is this language recognized at all?
164  if (!LanguageMapping.ContainsKey(defaultCompileLanguage))
165  {
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";
169  }
170 
171  // Is this language in allow-list?
172  if (!AllowedCompilers.ContainsKey(defaultCompileLanguage))
173  {
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!");
176  }
177  else
178  {
179 #if DEBUG
180  // m_log.Debug("[Compiler]: " +
181  // "Config OK. Default language \"" + defaultCompileLanguage + "\" specified in \"DefaultCompileLanguage\" is recognized as a valid language.");
182 #endif
183  // LANGUAGE IS IN ALLOW-LIST
184  DefaultCompileLanguage = LanguageMapping[defaultCompileLanguage];
185  }
186 
187  // We now have an allow-list, a mapping list, and a default language
188  }
189 
193  private void CheckOrCreateScriptsDirectory()
194  {
195  if (!Directory.Exists(ScriptEnginesPath))
196  {
197  try
198  {
199  Directory.CreateDirectory(ScriptEnginesPath);
200  }
201  catch (Exception ex)
202  {
203  m_log.Error("[Compiler]: Exception trying to create ScriptEngine directory \"" + ScriptEnginesPath + "\": " + ex.ToString());
204  }
205  }
206 
207  if (!Directory.Exists(Path.Combine(ScriptEnginesPath,
208  m_scriptEngine.World.RegionInfo.RegionID.ToString())))
209  {
210  try
211  {
212  Directory.CreateDirectory(Path.Combine(ScriptEnginesPath,
213  m_scriptEngine.World.RegionInfo.RegionID.ToString()));
214  }
215  catch (Exception ex)
216  {
217  m_log.Error("[Compiler]: Exception trying to create ScriptEngine directory \"" + Path.Combine(ScriptEnginesPath,
218  m_scriptEngine.World.RegionInfo.RegionID.ToString()) + "\": " + ex.ToString());
219  }
220  }
221  }
222 
226  private void DeleteOldFiles()
227  {
228  foreach (string file in Directory.GetFiles(Path.Combine(ScriptEnginesPath,
229  m_scriptEngine.World.RegionInfo.RegionID.ToString()), FilePrefix + "_compiled*"))
230  {
231  try
232  {
233  File.Delete(file);
234  }
235  catch (Exception ex)
236  {
237  m_log.Error("[Compiler]: Exception trying delete old script file \"" + file + "\": " + ex.ToString());
238  }
239  }
240  foreach (string file in Directory.GetFiles(Path.Combine(ScriptEnginesPath,
241  m_scriptEngine.World.RegionInfo.RegionID.ToString()), FilePrefix + "_source*"))
242  {
243  try
244  {
245  File.Delete(file);
246  }
247  catch (Exception ex)
248  {
249  m_log.Error("[Compiler]: Exception trying delete old script file \"" + file + "\": " + ex.ToString());
250  }
251  }
252  }
253 
255  //public string CompileFromFile(string LSOFileName)
256  //{
257  // switch (Path.GetExtension(LSOFileName).ToLower())
258  // {
259  // case ".txt":
260  // case ".lsl":
261  // Common.ScriptEngineBase.Shared.SendToDebug("Source code is LSL, converting to CS");
262  // return CompileFromLSLText(File.ReadAllText(LSOFileName));
263  // case ".cs":
264  // Common.ScriptEngineBase.Shared.SendToDebug("Source code is CS");
265  // return CompileFromCSText(File.ReadAllText(LSOFileName));
266  // default:
267  // throw new Exception("Unknown script type.");
268  // }
269  //}
270 
271  public string GetCompilerOutput(string assetID)
272  {
273  return Path.Combine(ScriptEnginesPath, Path.Combine(
274  m_scriptEngine.World.RegionInfo.RegionID.ToString(),
275  FilePrefix + "_compiled_" + assetID + ".dll"));
276  }
277 
278  public string GetCompilerOutput(UUID assetID)
279  {
280  return GetCompilerOutput(assetID.ToString());
281  }
282 
283  public void PerformScriptCompile(
284  string source, string asset, UUID ownerUUID,
285  out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
286  {
287  PerformScriptCompile(source, asset, ownerUUID, false, out assembly, out linemap);
288  }
289 
290  public void PerformScriptCompile(
291  string source, string asset, UUID ownerUUID, bool alwaysRecompile,
292  out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
293  {
294 // m_log.DebugFormat("[Compiler]: Checking script for asset {0} in {1}\n{2}", asset, m_scriptEngine.World.Name, source);
295 
296  IScriptModuleComms comms = m_scriptEngine.World.RequestModuleInterface<IScriptModuleComms>();
297 
298  linemap = null;
299  m_warnings.Clear();
300 
301  assembly = GetCompilerOutput(asset);
302 
303 // m_log.DebugFormat("[Compiler]: Retrieved assembly {0} for asset {1} in {2}", assembly, asset, m_scriptEngine.World.Name);
304 
305  CheckOrCreateScriptsDirectory();
306 
307  // Don't recompile if we're not forced to and we already have it
308  // Performing 3 file exists tests for every script can still be slow
309  if (!alwaysRecompile && File.Exists(assembly) && File.Exists(assembly + ".text") && File.Exists(assembly + ".map"))
310  {
311 // m_log.DebugFormat("[Compiler]: Found existing assembly {0} for asset {1} in {2}", assembly, asset, m_scriptEngine.World.Name);
312 
313  // If we have already read this linemap file, then it will be in our dictionary.
314  // Don't build another copy of the dictionary (saves memory) and certainly
315  // don't keep reading the same file from disk multiple times.
316  if (!m_lineMaps.ContainsKey(assembly))
317  m_lineMaps[assembly] = ReadMapFile(assembly + ".map");
318  linemap = m_lineMaps[assembly];
319  return;
320  }
321 
322 // m_log.DebugFormat("[Compiler]: Compiling assembly {0} for asset {1} in {2}", assembly, asset, m_scriptEngine.World.Name);
323 
324  if (source == String.Empty)
325  throw new Exception("Cannot find script assembly and no script text present");
326 
327  enumCompileType language = DefaultCompileLanguage;
328 
329  if (source.StartsWith("//c#", true, CultureInfo.InvariantCulture))
330  language = enumCompileType.cs;
331  if (source.StartsWith("//vb", true, CultureInfo.InvariantCulture))
332  {
333  language = enumCompileType.vb;
334  // We need to remove //vb, it won't compile with that
335 
336  source = source.Substring(4, source.Length - 4);
337  }
338  if (source.StartsWith("//lsl", true, CultureInfo.InvariantCulture))
339  language = enumCompileType.lsl;
340 
341 // m_log.DebugFormat("[Compiler]: Compile language is {0}", language);
342 
343  if (!AllowedCompilers.ContainsKey(language.ToString()))
344  {
345  // Not allowed to compile to this language!
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);
349  }
350 
351  if (m_scriptEngine.World.Permissions.CanCompileScript(ownerUUID, (int)language) == false)
352  {
353  // Not allowed to compile to this language!
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);
357  }
358 
359  string compileScript = source;
360 
361  if (language == enumCompileType.lsl)
362  {
363  // Its LSL, convert it to C#
364  LSL_Converter = (ICodeConverter)new CSCodeGenerator(comms, m_insertCoopTerminationCalls);
365  compileScript = LSL_Converter.Convert(source);
366 
367  // copy converter warnings into our warnings.
368  foreach (string warning in LSL_Converter.GetWarnings())
369  {
370  AddWarning(warning);
371  }
372 
373  linemap = ((CSCodeGenerator)LSL_Converter).PositionMap;
374  // Write the linemap to a file and save it in our dictionary for next time.
375  m_lineMaps[assembly] = linemap;
376  WriteMapFile(assembly + ".map", linemap);
377  }
378 
379  switch (language)
380  {
381  case enumCompileType.cs:
382  case enumCompileType.lsl:
383  compileScript = CreateCSCompilerScript(
384  compileScript,
385  m_scriptEngine.ScriptClassName,
386  m_scriptEngine.ScriptBaseClassName,
387  m_scriptEngine.ScriptBaseClassParameters);
388  break;
389  case enumCompileType.vb:
390  compileScript = CreateVBCompilerScript(
391  compileScript, m_scriptEngine.ScriptClassName, m_scriptEngine.ScriptBaseClassName);
392  break;
393  }
394 
395  assembly = CompileFromDotNetText(compileScript, language, asset, assembly);
396  }
397 
398  public string[] GetWarnings()
399  {
400  return m_warnings.ToArray();
401  }
402 
403  private void AddWarning(string warning)
404  {
405  if (!m_warnings.Contains(warning))
406  {
407  m_warnings.Add(warning);
408  }
409  }
410 
411 // private static string CreateJSCompilerScript(string compileScript)
412 // {
413 // compileScript = String.Empty +
414 // "import OpenSim.Region.ScriptEngine.Shared; import System.Collections.Generic;\r\n" +
415 // "package SecondLife {\r\n" +
416 // "class Script extends OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass { \r\n" +
417 // compileScript +
418 // "} }\r\n";
419 // return compileScript;
420 // }
421 
422  public static string CreateCSCompilerScript(
423  string compileScript, string className, string baseClassName, ParameterInfo[] constructorParameters)
424  {
425  compileScript = string.Format(
426 @"using OpenSim.Region.ScriptEngine.Shared;
427 using System.Collections.Generic;
428 
429 namespace SecondLife
430 {{
431  public class {0} : {1}
432  {{
433  public {0}({2}) : base({3}) {{}}
434 {4}
435  }}
436 }}",
437  className,
438  baseClassName,
439  constructorParameters != null
440  ? string.Join(", ", Array.ConvertAll<ParameterInfo, string>(constructorParameters, pi => pi.ToString()))
441  : "",
442  constructorParameters != null
443  ? string.Join(", ", Array.ConvertAll<ParameterInfo, string>(constructorParameters, pi => pi.Name))
444  : "",
445  compileScript);
446 
447  return compileScript;
448  }
449 
450  public static string CreateVBCompilerScript(string compileScript, string className, string baseClassName)
451  {
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: " +
457  compileScript +
458  ":End Class :End Namespace\r\n";
459 
460  return compileScript;
461  }
462 
468  internal string CompileFromDotNetText(string Script, enumCompileType lang, string asset, string assembly)
469  {
470 // m_log.DebugFormat("[Compiler]: Compiling to assembly\n{0}", Script);
471 
472  string ext = "." + lang.ToString();
473 
474  // Output assembly name
475  scriptCompileCounter++;
476  try
477  {
478  if (File.Exists(assembly))
479  {
480  File.SetAttributes(assembly, FileAttributes.Normal);
481  File.Delete(assembly);
482  }
483  }
484  catch (Exception e) // NOTLEGIT - Should be just FileIOException
485  {
486  throw new Exception("Unable to delete old existing " +
487  "script-file before writing new. Compile aborted: " +
488  e.ToString());
489  }
490 
491  // DEBUG - write source to disk
492  if (WriteScriptSourceToDebugFile)
493  {
494  string srcFileName = FilePrefix + "_source_" +
495  Path.GetFileNameWithoutExtension(assembly) + ext;
496  try
497  {
498  File.WriteAllText(Path.Combine(Path.Combine(
499  ScriptEnginesPath,
500  m_scriptEngine.World.RegionInfo.RegionID.ToString()),
501  srcFileName), Script);
502  }
503  catch (Exception ex) //NOTLEGIT - Should be just FileIOException
504  {
505  m_log.Error("[Compiler]: Exception while " +
506  "trying to write script source to file \"" +
507  srcFileName + "\": " + ex.ToString());
508  }
509  }
510 
511  // Do actual compile
512  CompilerParameters parameters = new CompilerParameters();
513 
514  parameters.IncludeDebugInformation = true;
515 
516  string rootPath = AppDomain.CurrentDomain.BaseDirectory;
517 
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"));
524 
525  if (m_scriptEngine.ScriptReferencedAssemblies != null)
526  Array.ForEach<string>(
527  m_scriptEngine.ScriptReferencedAssemblies,
528  a => parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, a)));
529 
530  parameters.GenerateExecutable = false;
531  parameters.OutputAssembly = assembly;
532  parameters.IncludeDebugInformation = CompileWithDebugInformation;
533  //parameters.WarningLevel = 1; // Should be 4?
534  parameters.TreatWarningsAsErrors = false;
535 
536  CompilerResults results;
537  switch (lang)
538  {
539  case enumCompileType.vb:
540  results = VBcodeProvider.CompileAssemblyFromSource(
541  parameters, Script);
542  break;
543  case enumCompileType.cs:
544  case enumCompileType.lsl:
545  bool complete = false;
546  bool retried = false;
547  do
548  {
549  lock (CScodeProvider)
550  {
551  results = CScodeProvider.CompileAssemblyFromSource(
552  parameters, Script);
553  }
554 
555  // Deal with an occasional segv in the compiler.
556  // Rarely, if ever, occurs twice in succession.
557  // Line # == 0 and no file name are indications that
558  // this is a native stack trace rather than a normal
559  // error log.
560  if (results.Errors.Count > 0)
561  {
562  if (!retried && string.IsNullOrEmpty(results.Errors[0].FileName) &&
563  results.Errors[0].Line == 0)
564  {
565  // System.Console.WriteLine("retrying failed compilation");
566  retried = true;
567  }
568  else
569  {
570  complete = true;
571  }
572  }
573  else
574  {
575  complete = true;
576  }
577  } while (!complete);
578  break;
579  default:
580  throw new Exception("Compiler is not able to recongnize " +
581  "language type \"" + lang.ToString() + "\"");
582  }
583 
584 // foreach (Type type in results.CompiledAssembly.GetTypes())
585 // {
586 // foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
587 // {
588 // m_log.DebugFormat("[COMPILER]: {0}.{1}", type.FullName, method.Name);
589 // }
590 // }
591 
592  //
593  // WARNINGS AND ERRORS
594  //
595  bool hadErrors = false;
596  string errtext = String.Empty;
597  if (results.Errors.Count > 0)
598  {
599  foreach (CompilerError CompErr in results.Errors)
600  {
601  string severity = CompErr.IsWarning ? "Warning" : "Error";
602 
603  KeyValuePair<int, int> errorPos;
604 
605  // Show 5 errors max, but check entire list for errors
606 
607  if (severity == "Error")
608  {
609  // C# scripts will not have a linemap since theres no line translation involved.
610  if (!m_lineMaps.ContainsKey(assembly))
611  errorPos = new KeyValuePair<int, int>(CompErr.Line, CompErr.Column);
612  else
613  errorPos = FindErrorPosition(CompErr.Line, CompErr.Column, m_lineMaps[assembly]);
614 
615  string text = CompErr.ErrorText;
616 
617  // Use LSL type names
618  if (lang == enumCompileType.lsl)
619  text = ReplaceTypes(CompErr.ErrorText);
620 
621  // The Second Life viewer's script editor begins
622  // countingn lines and columns at 0, so we subtract 1.
623  errtext += String.Format("({0},{1}): {4} {2}: {3}\n",
624  errorPos.Key - 1, errorPos.Value - 1,
625  CompErr.ErrorNumber, text, severity);
626  hadErrors = true;
627  }
628  }
629  }
630 
631  if (hadErrors)
632  {
633  throw new Exception(errtext);
634  }
635 
636  // On today's highly asynchronous systems, the result of
637  // the compile may not be immediately apparent. Wait a
638  // reasonable amount of time before giving up on it.
639 
640  if (!File.Exists(assembly))
641  {
642  for (int i = 0; i < 20 && !File.Exists(assembly); i++)
643  {
644  System.Threading.Thread.Sleep(250);
645  }
646  // One final chance...
647  if (!File.Exists(assembly))
648  {
649  errtext = String.Empty;
650  errtext += "No compile error. But not able to locate compiled file.";
651  throw new Exception(errtext);
652  }
653  }
654 
655  // m_log.DebugFormat("[Compiler] Compiled new assembly "+
656  // "for {0}", asset);
657 
658  // Because windows likes to perform exclusive locks, we simply
659  // write out a textual representation of the file here
660  //
661  // Read the binary file into a buffer
662  //
663  FileInfo fi = new FileInfo(assembly);
664 
665  if (fi == null)
666  {
667  errtext = String.Empty;
668  errtext += "No compile error. But not able to stat file.";
669  throw new Exception(errtext);
670  }
671 
672  Byte[] data = new Byte[fi.Length];
673 
674  try
675  {
676  using (FileStream fs = File.Open(assembly, FileMode.Open, FileAccess.Read))
677  fs.Read(data, 0, data.Length);
678  }
679  catch (Exception)
680  {
681  errtext = String.Empty;
682  errtext += "No compile error. But not able to open file.";
683  throw new Exception(errtext);
684  }
685 
686  // Convert to base64
687  //
688  string filetext = System.Convert.ToBase64String(data);
689 
690  Byte[] buf = Encoding.ASCII.GetBytes(filetext);
691 
692  using (FileStream sfs = File.Create(assembly + ".text"))
693  sfs.Write(buf, 0, buf.Length);
694 
695  return assembly;
696  }
697 
698  private class kvpSorter : IComparer<KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>>>
699  {
700  public int Compare(KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> a,
701  KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> b)
702  {
703  int kc = a.Key.Key.CompareTo(b.Key.Key);
704  return (kc != 0) ? kc : a.Key.Value.CompareTo(b.Key.Value);
705  }
706  }
707 
708  public static KeyValuePair<int, int> FindErrorPosition(int line,
709  int col, Dictionary<KeyValuePair<int, int>,
710  KeyValuePair<int, int>> positionMap)
711  {
712  if (positionMap == null || positionMap.Count == 0)
713  return new KeyValuePair<int, int>(line, col);
714 
715  KeyValuePair<int, int> ret = new KeyValuePair<int, int>();
716 
717  if (positionMap.TryGetValue(new KeyValuePair<int, int>(line, col),
718  out ret))
719  return ret;
720 
721  var sorted = new List<KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>>>(positionMap);
722 
723  sorted.Sort(new kvpSorter());
724 
725  int l = 1;
726  int c = 1;
727  int pl = 1;
728 
729  foreach (KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> posmap in sorted)
730  {
731  //m_log.DebugFormat("[Compiler]: Scanning line map {0},{1} --> {2},{3}", posmap.Key.Key, posmap.Key.Value, posmap.Value.Key, posmap.Value.Value);
732  int nl = posmap.Value.Key + line - posmap.Key.Key; // New, translated LSL line and column.
733  int nc = posmap.Value.Value + col - posmap.Key.Value;
734  // Keep going until we find the first point passed line,col.
735  if (posmap.Key.Key > line)
736  {
737  //m_log.DebugFormat("[Compiler]: Line is larger than requested {0},{1}, returning {2},{3}", line, col, l, c);
738  if (pl < line)
739  {
740  //m_log.DebugFormat("[Compiler]: Previous line ({0}) is less than requested line ({1}), setting column to 1.", pl, line);
741  c = 1;
742  }
743  break;
744  }
745  if (posmap.Key.Key == line && posmap.Key.Value > col)
746  {
747  // Never move l,c backwards.
748  if (nl > l || (nl == l && nc > c))
749  {
750  //m_log.DebugFormat("[Compiler]: Using offset relative to this: {0} + {1} - {2}, {3} + {4} - {5} = {6}, {7}",
751  // posmap.Value.Key, line, posmap.Key.Key, posmap.Value.Value, col, posmap.Key.Value, nl, nc);
752  l = nl;
753  c = nc;
754  }
755  //m_log.DebugFormat("[Compiler]: Column is larger than requested {0},{1}, returning {2},{3}", line, col, l, c);
756  break;
757  }
758  pl = posmap.Key.Key;
759  l = posmap.Value.Key;
760  c = posmap.Value.Value;
761  }
762  return new KeyValuePair<int, int>(l, c);
763  }
764 
765  string ReplaceTypes(string message)
766  {
767  message = message.Replace(
768  "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString",
769  "string");
770 
771  message = message.Replace(
772  "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger",
773  "integer");
774 
775  message = message.Replace(
776  "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat",
777  "float");
778 
779  message = message.Replace(
780  "OpenSim.Region.ScriptEngine.Shared.LSL_Types.list",
781  "list");
782 
783  return message;
784  }
785 
786  private static void WriteMapFile(string filename, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
787  {
788  string mapstring = String.Empty;
789  foreach (KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> kvp in linemap)
790  {
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);
794  }
795 
796  Byte[] mapbytes = Encoding.ASCII.GetBytes(mapstring);
797 
798  using (FileStream mfs = File.Create(filename))
799  mfs.Write(mapbytes, 0, mapbytes.Length);
800  }
801 
802  private static Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> ReadMapFile(string filename)
803  {
804  Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap;
805  try
806  {
807  using (StreamReader r = File.OpenText(filename))
808  {
809  linemap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
810 
811  string line;
812  while ((line = r.ReadLine()) != null)
813  {
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]);
819 
820  KeyValuePair<int, int> k = new KeyValuePair<int, int>(kk, kv);
821  KeyValuePair<int, int> v = new KeyValuePair<int, int>(vk, vv);
822 
823  linemap[k] = v;
824  }
825  }
826  }
827  catch
828  {
829  linemap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
830  }
831 
832  return linemap;
833  }
834  }
835 }
static string CreateCSCompilerScript(string compileScript, string className, string baseClassName, ParameterInfo[] constructorParameters)
Definition: Compiler.cs:422
void PerformScriptCompile(string source, string asset, UUID ownerUUID, out string assembly, out Dictionary< KeyValuePair< int, int >, KeyValuePair< int, int >> linemap)
Performs the script compile.
Definition: Compiler.cs:283
void PerformScriptCompile(string source, string asset, UUID ownerUUID, bool alwaysRecompile, out string assembly, out Dictionary< KeyValuePair< int, int >, KeyValuePair< int, int >> linemap)
Performs the script compile.
Definition: Compiler.cs:290
An interface for a script API module to communicate with the engine it's running under ...
static string CreateVBCompilerScript(string compileScript, string className, string baseClassName)
Definition: Compiler.cs:450
Interface for communication between OpenSim modules and in-world scripts
Interactive OpenSim region server
Definition: OpenSim.cs:55
static KeyValuePair< int, int > FindErrorPosition(int line, int col, Dictionary< KeyValuePair< int, int >, KeyValuePair< int, int >> positionMap)
Definition: Compiler.cs:708