clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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  
64 /// <summary>
65 /// This contains number of lines WE use for header when compiling script. User will get error in line x-LinesToRemoveOnError when error occurs.
66 /// </summary>
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  
90 public IScriptEngine m_scriptEngine;
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  
190 /// <summary>
191 /// Create the directory where compiled scripts are stored if it does not already exist.
192 /// </summary>
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  
223 /// <summary>
224 /// Delete old script files
225 /// </summary>
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  
254 ////private ICodeCompiler icc = codeProvider.CreateCompiler();
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]: Compiling script\n{0}", Script);
295  
296 IScriptModuleComms comms = m_scriptEngine.World.RequestModuleInterface<IScriptModuleComms>();
297  
298 linemap = null;
299 m_warnings.Clear();
300  
301 assembly = GetCompilerOutput(asset);
302  
303 CheckOrCreateScriptsDirectory();
304  
305 // Don't recompile if we're not forced to and we already have it
306 // Performing 3 file exists tests for every script can still be slow
307 if (!alwaysRecompile && File.Exists(assembly) && File.Exists(assembly + ".text") && File.Exists(assembly + ".map"))
308 {
309 // If we have already read this linemap file, then it will be in our dictionary.
310 // Don't build another copy of the dictionary (saves memory) and certainly
311 // don't keep reading the same file from disk multiple times.
312 if (!m_lineMaps.ContainsKey(assembly))
313 m_lineMaps[assembly] = ReadMapFile(assembly + ".map");
314 linemap = m_lineMaps[assembly];
315 return;
316 }
317  
318 if (source == String.Empty)
319 throw new Exception("Cannot find script assembly and no script text present");
320  
321 enumCompileType language = DefaultCompileLanguage;
322  
323 if (source.StartsWith("//c#", true, CultureInfo.InvariantCulture))
324 language = enumCompileType.cs;
325 if (source.StartsWith("//vb", true, CultureInfo.InvariantCulture))
326 {
327 language = enumCompileType.vb;
328 // We need to remove //vb, it won't compile with that
329  
330 source = source.Substring(4, source.Length - 4);
331 }
332 if (source.StartsWith("//lsl", true, CultureInfo.InvariantCulture))
333 language = enumCompileType.lsl;
334  
335 // m_log.DebugFormat("[Compiler]: Compile language is {0}", language);
336  
337 if (!AllowedCompilers.ContainsKey(language.ToString()))
338 {
339 // Not allowed to compile to this language!
340 string errtext = String.Empty;
341 errtext += "The compiler for language \"" + language.ToString() + "\" is not in list of allowed compilers. Script will not be executed!";
342 throw new Exception(errtext);
343 }
344  
345 if (m_scriptEngine.World.Permissions.CanCompileScript(ownerUUID, (int)language) == false)
346 {
347 // Not allowed to compile to this language!
348 string errtext = String.Empty;
349 errtext += ownerUUID + " is not in list of allowed users for this scripting language. Script will not be executed!";
350 throw new Exception(errtext);
351 }
352  
353 string compileScript = source;
354  
355 if (language == enumCompileType.lsl)
356 {
357 // Its LSL, convert it to C#
358 LSL_Converter = (ICodeConverter)new CSCodeGenerator(comms, m_insertCoopTerminationCalls);
359 compileScript = LSL_Converter.Convert(source);
360  
361 // copy converter warnings into our warnings.
362 foreach (string warning in LSL_Converter.GetWarnings())
363 {
364 AddWarning(warning);
365 }
366  
367 linemap = ((CSCodeGenerator)LSL_Converter).PositionMap;
368 // Write the linemap to a file and save it in our dictionary for next time.
369 m_lineMaps[assembly] = linemap;
370 WriteMapFile(assembly + ".map", linemap);
371 }
372  
373 switch (language)
374 {
375 case enumCompileType.cs:
376 case enumCompileType.lsl:
377 compileScript = CreateCSCompilerScript(
378 compileScript,
379 m_scriptEngine.ScriptClassName,
380 m_scriptEngine.ScriptBaseClassName,
381 m_scriptEngine.ScriptBaseClassParameters);
382 break;
383 case enumCompileType.vb:
384 compileScript = CreateVBCompilerScript(
385 compileScript, m_scriptEngine.ScriptClassName, m_scriptEngine.ScriptBaseClassName);
386 break;
387 }
388  
389 assembly = CompileFromDotNetText(compileScript, language, asset, assembly);
390 }
391  
392 public string[] GetWarnings()
393 {
394 return m_warnings.ToArray();
395 }
396  
397 private void AddWarning(string warning)
398 {
399 if (!m_warnings.Contains(warning))
400 {
401 m_warnings.Add(warning);
402 }
403 }
404  
405 // private static string CreateJSCompilerScript(string compileScript)
406 // {
407 // compileScript = String.Empty +
408 // "import OpenSim.Region.ScriptEngine.Shared; import System.Collections.Generic;\r\n" +
409 // "package SecondLife {\r\n" +
410 // "class Script extends OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass { \r\n" +
411 // compileScript +
412 // "} }\r\n";
413 // return compileScript;
414 // }
415  
416 public static string CreateCSCompilerScript(
417 string compileScript, string className, string baseClassName, ParameterInfo[] constructorParameters)
418 {
419 compileScript = string.Format(
420 @"using OpenSim.Region.ScriptEngine.Shared;
421 using System.Collections.Generic;
422  
423 namespace SecondLife
424 {{
425 public class {0} : {1}
426 {{
427 public {0}({2}) : base({3}) {{}}
428 {4}
429 }}
430 }}",
431 className,
432 baseClassName,
433 constructorParameters != null
434 ? string.Join(", ", Array.ConvertAll<ParameterInfo, string>(constructorParameters, pi => pi.ToString()))
435 : "",
436 constructorParameters != null
437 ? string.Join(", ", Array.ConvertAll<ParameterInfo, string>(constructorParameters, pi => pi.Name))
438 : "",
439 compileScript);
440  
441 return compileScript;
442 }
443  
444 public static string CreateVBCompilerScript(string compileScript, string className, string baseClassName)
445 {
446 compileScript = String.Empty +
447 "Imports OpenSim.Region.ScriptEngine.Shared: Imports System.Collections.Generic: " +
448 String.Empty + "NameSpace SecondLife:" +
449 String.Empty + "Public Class " + className + ": Inherits " + baseClassName +
450 "\r\nPublic Sub New()\r\nEnd Sub: " +
451 compileScript +
452 ":End Class :End Namespace\r\n";
453  
454 return compileScript;
455 }
456  
457 /// <summary>
458 /// Compile .NET script to .Net assembly (.dll)
459 /// </summary>
460 /// <param name="Script">CS script</param>
461 /// <returns>Filename to .dll assembly</returns>
462 internal string CompileFromDotNetText(string Script, enumCompileType lang, string asset, string assembly)
463 {
464 // m_log.DebugFormat("[Compiler]: Compiling to assembly\n{0}", Script);
465  
466 string ext = "." + lang.ToString();
467  
468 // Output assembly name
469 scriptCompileCounter++;
470 try
471 {
472 File.Delete(assembly);
473 }
474 catch (Exception e) // NOTLEGIT - Should be just FileIOException
475 {
476 throw new Exception("Unable to delete old existing " +
477 "script-file before writing new. Compile aborted: " +
478 e.ToString());
479 }
480  
481 // DEBUG - write source to disk
482 if (WriteScriptSourceToDebugFile)
483 {
484 string srcFileName = FilePrefix + "_source_" +
485 Path.GetFileNameWithoutExtension(assembly) + ext;
486 try
487 {
488 File.WriteAllText(Path.Combine(Path.Combine(
489 ScriptEnginesPath,
490 m_scriptEngine.World.RegionInfo.RegionID.ToString()),
491 srcFileName), Script);
492 }
493 catch (Exception ex) //NOTLEGIT - Should be just FileIOException
494 {
495 m_log.Error("[Compiler]: Exception while " +
496 "trying to write script source to file \"" +
497 srcFileName + "\": " + ex.ToString());
498 }
499 }
500  
501 // Do actual compile
502 CompilerParameters parameters = new CompilerParameters();
503  
504 parameters.IncludeDebugInformation = true;
505  
506 string rootPath = AppDomain.CurrentDomain.BaseDirectory;
507  
508 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
509 "OpenSim.Region.ScriptEngine.Shared.dll"));
510 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
511 "OpenSim.Region.ScriptEngine.Shared.Api.Runtime.dll"));
512 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
513 "OpenMetaverseTypes.dll"));
514  
515 if (m_scriptEngine.ScriptReferencedAssemblies != null)
516 Array.ForEach<string>(
517 m_scriptEngine.ScriptReferencedAssemblies,
518 a => parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, a)));
519  
520 parameters.GenerateExecutable = false;
521 parameters.OutputAssembly = assembly;
522 parameters.IncludeDebugInformation = CompileWithDebugInformation;
523 //parameters.WarningLevel = 1; // Should be 4?
524 parameters.TreatWarningsAsErrors = false;
525  
526 CompilerResults results;
527 switch (lang)
528 {
529 case enumCompileType.vb:
530 results = VBcodeProvider.CompileAssemblyFromSource(
531 parameters, Script);
532 break;
533 case enumCompileType.cs:
534 case enumCompileType.lsl:
535 bool complete = false;
536 bool retried = false;
537 do
538 {
539 lock (CScodeProvider)
540 {
541 results = CScodeProvider.CompileAssemblyFromSource(
542 parameters, Script);
543 }
544  
545 // Deal with an occasional segv in the compiler.
546 // Rarely, if ever, occurs twice in succession.
547 // Line # == 0 and no file name are indications that
548 // this is a native stack trace rather than a normal
549 // error log.
550 if (results.Errors.Count > 0)
551 {
552 if (!retried && string.IsNullOrEmpty(results.Errors[0].FileName) &&
553 results.Errors[0].Line == 0)
554 {
555 // System.Console.WriteLine("retrying failed compilation");
556 retried = true;
557 }
558 else
559 {
560 complete = true;
561 }
562 }
563 else
564 {
565 complete = true;
566 }
567 } while (!complete);
568 break;
569 default:
570 throw new Exception("Compiler is not able to recongnize " +
571 "language type \"" + lang.ToString() + "\"");
572 }
573  
574 // foreach (Type type in results.CompiledAssembly.GetTypes())
575 // {
576 // foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
577 // {
578 // m_log.DebugFormat("[COMPILER]: {0}.{1}", type.FullName, method.Name);
579 // }
580 // }
581  
582 //
583 // WARNINGS AND ERRORS
584 //
585 bool hadErrors = false;
586 string errtext = String.Empty;
587 if (results.Errors.Count > 0)
588 {
589 foreach (CompilerError CompErr in results.Errors)
590 {
591 string severity = CompErr.IsWarning ? "Warning" : "Error";
592  
593 KeyValuePair<int, int> errorPos;
594  
595 // Show 5 errors max, but check entire list for errors
596  
597 if (severity == "Error")
598 {
599 // C# scripts will not have a linemap since theres no line translation involved.
600 if (!m_lineMaps.ContainsKey(assembly))
601 errorPos = new KeyValuePair<int, int>(CompErr.Line, CompErr.Column);
602 else
603 errorPos = FindErrorPosition(CompErr.Line, CompErr.Column, m_lineMaps[assembly]);
604  
605 string text = CompErr.ErrorText;
606  
607 // Use LSL type names
608 if (lang == enumCompileType.lsl)
609 text = ReplaceTypes(CompErr.ErrorText);
610  
611 // The Second Life viewer's script editor begins
612 // countingn lines and columns at 0, so we subtract 1.
613 errtext += String.Format("({0},{1}): {4} {2}: {3}\n",
614 errorPos.Key - 1, errorPos.Value - 1,
615 CompErr.ErrorNumber, text, severity);
616 hadErrors = true;
617 }
618 }
619 }
620  
621 if (hadErrors)
622 {
623 throw new Exception(errtext);
624 }
625  
626 // On today's highly asynchronous systems, the result of
627 // the compile may not be immediately apparent. Wait a
628 // reasonable amount of time before giving up on it.
629  
630 if (!File.Exists(assembly))
631 {
632 for (int i = 0; i < 20 && !File.Exists(assembly); i++)
633 {
634 System.Threading.Thread.Sleep(250);
635 }
636 // One final chance...
637 if (!File.Exists(assembly))
638 {
639 errtext = String.Empty;
640 errtext += "No compile error. But not able to locate compiled file.";
641 throw new Exception(errtext);
642 }
643 }
644  
645 // m_log.DebugFormat("[Compiler] Compiled new assembly "+
646 // "for {0}", asset);
647  
648 // Because windows likes to perform exclusive locks, we simply
649 // write out a textual representation of the file here
650 //
651 // Read the binary file into a buffer
652 //
653 FileInfo fi = new FileInfo(assembly);
654  
655 if (fi == null)
656 {
657 errtext = String.Empty;
658 errtext += "No compile error. But not able to stat file.";
659 throw new Exception(errtext);
660 }
661  
662 Byte[] data = new Byte[fi.Length];
663  
664 try
665 {
666 FileStream fs = File.Open(assembly, FileMode.Open, FileAccess.Read);
667 fs.Read(data, 0, data.Length);
668 fs.Close();
669 }
670 catch (Exception)
671 {
672 errtext = String.Empty;
673 errtext += "No compile error. But not able to open file.";
674 throw new Exception(errtext);
675 }
676  
677 // Convert to base64
678 //
679 string filetext = System.Convert.ToBase64String(data);
680  
681 Byte[] buf = Encoding.ASCII.GetBytes(filetext);
682  
683 FileStream sfs = File.Create(assembly + ".text");
684 sfs.Write(buf, 0, buf.Length);
685 sfs.Close();
686  
687 return assembly;
688 }
689  
690 private class kvpSorter : IComparer<KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>>>
691 {
692 public int Compare(KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> a,
693 KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> b)
694 {
695 int kc = a.Key.Key.CompareTo(b.Key.Key);
696 return (kc != 0) ? kc : a.Key.Value.CompareTo(b.Key.Value);
697 }
698 }
699  
700 public static KeyValuePair<int, int> FindErrorPosition(int line,
701 int col, Dictionary<KeyValuePair<int, int>,
702 KeyValuePair<int, int>> positionMap)
703 {
704 if (positionMap == null || positionMap.Count == 0)
705 return new KeyValuePair<int, int>(line, col);
706  
707 KeyValuePair<int, int> ret = new KeyValuePair<int, int>();
708  
709 if (positionMap.TryGetValue(new KeyValuePair<int, int>(line, col),
710 out ret))
711 return ret;
712  
713 var sorted = new List<KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>>>(positionMap);
714  
715 sorted.Sort(new kvpSorter());
716  
717 int l = 1;
718 int c = 1;
719 int pl = 1;
720  
721 foreach (KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> posmap in sorted)
722 {
723 //m_log.DebugFormat("[Compiler]: Scanning line map {0},{1} --> {2},{3}", posmap.Key.Key, posmap.Key.Value, posmap.Value.Key, posmap.Value.Value);
724 int nl = posmap.Value.Key + line - posmap.Key.Key; // New, translated LSL line and column.
725 int nc = posmap.Value.Value + col - posmap.Key.Value;
726 // Keep going until we find the first point passed line,col.
727 if (posmap.Key.Key > line)
728 {
729 //m_log.DebugFormat("[Compiler]: Line is larger than requested {0},{1}, returning {2},{3}", line, col, l, c);
730 if (pl < line)
731 {
732 //m_log.DebugFormat("[Compiler]: Previous line ({0}) is less than requested line ({1}), setting column to 1.", pl, line);
733 c = 1;
734 }
735 break;
736 }
737 if (posmap.Key.Key == line && posmap.Key.Value > col)
738 {
739 // Never move l,c backwards.
740 if (nl > l || (nl == l && nc > c))
741 {
742 //m_log.DebugFormat("[Compiler]: Using offset relative to this: {0} + {1} - {2}, {3} + {4} - {5} = {6}, {7}",
743 // posmap.Value.Key, line, posmap.Key.Key, posmap.Value.Value, col, posmap.Key.Value, nl, nc);
744 l = nl;
745 c = nc;
746 }
747 //m_log.DebugFormat("[Compiler]: Column is larger than requested {0},{1}, returning {2},{3}", line, col, l, c);
748 break;
749 }
750 pl = posmap.Key.Key;
751 l = posmap.Value.Key;
752 c = posmap.Value.Value;
753 }
754 return new KeyValuePair<int, int>(l, c);
755 }
756  
757 string ReplaceTypes(string message)
758 {
759 message = message.Replace(
760 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString",
761 "string");
762  
763 message = message.Replace(
764 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger",
765 "integer");
766  
767 message = message.Replace(
768 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat",
769 "float");
770  
771 message = message.Replace(
772 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.list",
773 "list");
774  
775 return message;
776 }
777  
778  
779 private static void WriteMapFile(string filename, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
780 {
781 string mapstring = String.Empty;
782 foreach (KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> kvp in linemap)
783 {
784 KeyValuePair<int, int> k = kvp.Key;
785 KeyValuePair<int, int> v = kvp.Value;
786 mapstring += String.Format("{0},{1},{2},{3}\n", k.Key, k.Value, v.Key, v.Value);
787 }
788  
789 Byte[] mapbytes = Encoding.ASCII.GetBytes(mapstring);
790 FileStream mfs = File.Create(filename);
791 mfs.Write(mapbytes, 0, mapbytes.Length);
792 mfs.Close();
793 }
794  
795  
796 private static Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> ReadMapFile(string filename)
797 {
798 Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap;
799 try
800 {
801 StreamReader r = File.OpenText(filename);
802 linemap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
803  
804 string line;
805 while ((line = r.ReadLine()) != null)
806 {
807 String[] parts = line.Split(new Char[] { ',' });
808 int kk = System.Convert.ToInt32(parts[0]);
809 int kv = System.Convert.ToInt32(parts[1]);
810 int vk = System.Convert.ToInt32(parts[2]);
811 int vv = System.Convert.ToInt32(parts[3]);
812  
813 KeyValuePair<int, int> k = new KeyValuePair<int, int>(kk, kv);
814 KeyValuePair<int, int> v = new KeyValuePair<int, int>(vk, vv);
815  
816 linemap[k] = v;
817 }
818 }
819 catch
820 {
821 linemap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
822 }
823 return linemap;
824 }
825 }
826 }