opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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 js = 3,
63 yp = 4
64 }
65  
66 /// <summary>
67 /// This contains number of lines WE use for header when compiling script. User will get error in line x-LinesToRemoveOnError when error occurs.
68 /// </summary>
69 public int LinesToRemoveOnError = 3;
70 private enumCompileType DefaultCompileLanguage;
71 private bool WriteScriptSourceToDebugFile;
72 private bool CompileWithDebugInformation;
73 private Dictionary<string, bool> AllowedCompilers = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);
74 private Dictionary<string, enumCompileType> LanguageMapping = new Dictionary<string, enumCompileType>(StringComparer.CurrentCultureIgnoreCase);
75 private bool m_insertCoopTerminationCalls;
76  
77 private string FilePrefix;
78 private string ScriptEnginesPath = null;
79 // mapping between LSL and C# line/column numbers
80 private ICodeConverter LSL_Converter;
81  
82 private List<string> m_warnings = new List<string>();
83  
84 // private object m_syncy = new object();
85  
86 private static CSharpCodeProvider CScodeProvider = new CSharpCodeProvider();
87 private static VBCodeProvider VBcodeProvider = new VBCodeProvider();
88 // private static JScriptCodeProvider JScodeProvider = new JScriptCodeProvider();
89 private static CSharpCodeProvider YPcodeProvider = new CSharpCodeProvider(); // YP is translated into CSharp
90 private static YP2CSConverter YP_Converter = new YP2CSConverter();
91  
92 // private static int instanceID = new Random().Next(0, int.MaxValue); // Unique number to use on our compiled files
93 private static UInt64 scriptCompileCounter = 0; // And a counter
94  
95 public IScriptEngine m_scriptEngine;
96 private Dictionary<string, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>> m_lineMaps =
97 new Dictionary<string, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>>();
98  
99 public bool in_startup = true;
100  
101 public Compiler(IScriptEngine scriptEngine)
102 {
103 m_scriptEngine = scriptEngine;
104 ScriptEnginesPath = scriptEngine.ScriptEnginePath;
105 ReadConfig();
106 }
107  
108 public void ReadConfig()
109 {
110 // Get some config
111 WriteScriptSourceToDebugFile = m_scriptEngine.Config.GetBoolean("WriteScriptSourceToDebugFile", false);
112 CompileWithDebugInformation = m_scriptEngine.Config.GetBoolean("CompileWithDebugInformation", true);
113 bool DeleteScriptsOnStartup = m_scriptEngine.Config.GetBoolean("DeleteScriptsOnStartup", true);
114 m_insertCoopTerminationCalls = m_scriptEngine.Config.GetString("ScriptStopStrategy", "abort") == "co-op";
115  
116 // Get file prefix from scriptengine name and make it file system safe:
117 FilePrefix = "CommonCompiler";
118 foreach (char c in Path.GetInvalidFileNameChars())
119 {
120 FilePrefix = FilePrefix.Replace(c, '_');
121 }
122  
123 if (in_startup)
124 {
125 in_startup = false;
126 CreateScriptsDirectory();
127  
128 // First time we start? Delete old files
129 if (DeleteScriptsOnStartup)
130 DeleteOldFiles();
131 }
132  
133 // Map name and enum type of our supported languages
134 LanguageMapping.Add(enumCompileType.cs.ToString(), enumCompileType.cs);
135 LanguageMapping.Add(enumCompileType.vb.ToString(), enumCompileType.vb);
136 LanguageMapping.Add(enumCompileType.lsl.ToString(), enumCompileType.lsl);
137 LanguageMapping.Add(enumCompileType.js.ToString(), enumCompileType.js);
138 LanguageMapping.Add(enumCompileType.yp.ToString(), enumCompileType.yp);
139  
140 // Allowed compilers
141 string allowComp = m_scriptEngine.Config.GetString("AllowedCompilers", "lsl");
142 AllowedCompilers.Clear();
143  
144 #if DEBUG
145 m_log.Debug("[Compiler]: Allowed languages: " + allowComp);
146 #endif
147  
148  
149 foreach (string strl in allowComp.Split(','))
150 {
151 string strlan = strl.Trim(" \t".ToCharArray()).ToLower();
152 if (!LanguageMapping.ContainsKey(strlan))
153 {
154 m_log.Error("[Compiler]: Config error. Compiler is unable to recognize language type \"" + strlan + "\" specified in \"AllowedCompilers\".");
155 }
156 else
157 {
158 #if DEBUG
159 //m_log.Debug("[Compiler]: Config OK. Compiler recognized language type \"" + strlan + "\" specified in \"AllowedCompilers\".");
160 #endif
161 }
162 AllowedCompilers.Add(strlan, true);
163 }
164 if (AllowedCompilers.Count == 0)
165 m_log.Error("[Compiler]: Config error. Compiler could not recognize any language in \"AllowedCompilers\". Scripts will not be executed!");
166  
167 // Default language
168 string defaultCompileLanguage = m_scriptEngine.Config.GetString("DefaultCompileLanguage", "lsl").ToLower();
169  
170 // Is this language recognized at all?
171 if (!LanguageMapping.ContainsKey(defaultCompileLanguage))
172 {
173 m_log.Error("[Compiler]: " +
174 "Config error. Default language \"" + defaultCompileLanguage + "\" specified in \"DefaultCompileLanguage\" is not recognized as a valid language. Changing default to: \"lsl\".");
175 defaultCompileLanguage = "lsl";
176 }
177  
178 // Is this language in allow-list?
179 if (!AllowedCompilers.ContainsKey(defaultCompileLanguage))
180 {
181 m_log.Error("[Compiler]: " +
182 "Config error. Default language \"" + defaultCompileLanguage + "\"specified in \"DefaultCompileLanguage\" is not in list of \"AllowedCompilers\". Scripts may not be executed!");
183 }
184 else
185 {
186 #if DEBUG
187 // m_log.Debug("[Compiler]: " +
188 // "Config OK. Default language \"" + defaultCompileLanguage + "\" specified in \"DefaultCompileLanguage\" is recognized as a valid language.");
189 #endif
190 // LANGUAGE IS IN ALLOW-LIST
191 DefaultCompileLanguage = LanguageMapping[defaultCompileLanguage];
192 }
193  
194 // We now have an allow-list, a mapping list, and a default language
195  
196 }
197  
198 /// <summary>
199 /// Create the directory where compiled scripts are stored.
200 /// </summary>
201 private void CreateScriptsDirectory()
202 {
203 if (!Directory.Exists(ScriptEnginesPath))
204 {
205 try
206 {
207 Directory.CreateDirectory(ScriptEnginesPath);
208 }
209 catch (Exception ex)
210 {
211 m_log.Error("[Compiler]: Exception trying to create ScriptEngine directory \"" + ScriptEnginesPath + "\": " + ex.ToString());
212 }
213 }
214  
215 if (!Directory.Exists(Path.Combine(ScriptEnginesPath,
216 m_scriptEngine.World.RegionInfo.RegionID.ToString())))
217 {
218 try
219 {
220 Directory.CreateDirectory(Path.Combine(ScriptEnginesPath,
221 m_scriptEngine.World.RegionInfo.RegionID.ToString()));
222 }
223 catch (Exception ex)
224 {
225 m_log.Error("[Compiler]: Exception trying to create ScriptEngine directory \"" + Path.Combine(ScriptEnginesPath,
226 m_scriptEngine.World.RegionInfo.RegionID.ToString()) + "\": " + ex.ToString());
227 }
228 }
229 }
230  
231 /// <summary>
232 /// Delete old script files
233 /// </summary>
234 private void DeleteOldFiles()
235 {
236 foreach (string file in Directory.GetFiles(Path.Combine(ScriptEnginesPath,
237 m_scriptEngine.World.RegionInfo.RegionID.ToString()), FilePrefix + "_compiled*"))
238 {
239 try
240 {
241 File.Delete(file);
242 }
243 catch (Exception ex)
244 {
245 m_log.Error("[Compiler]: Exception trying delete old script file \"" + file + "\": " + ex.ToString());
246 }
247 }
248 foreach (string file in Directory.GetFiles(Path.Combine(ScriptEnginesPath,
249 m_scriptEngine.World.RegionInfo.RegionID.ToString()), FilePrefix + "_source*"))
250 {
251 try
252 {
253 File.Delete(file);
254 }
255 catch (Exception ex)
256 {
257 m_log.Error("[Compiler]: Exception trying delete old script file \"" + file + "\": " + ex.ToString());
258 }
259 }
260 }
261  
262 ////private ICodeCompiler icc = codeProvider.CreateCompiler();
263 //public string CompileFromFile(string LSOFileName)
264 //{
265 // switch (Path.GetExtension(LSOFileName).ToLower())
266 // {
267 // case ".txt":
268 // case ".lsl":
269 // Common.ScriptEngineBase.Shared.SendToDebug("Source code is LSL, converting to CS");
270 // return CompileFromLSLText(File.ReadAllText(LSOFileName));
271 // case ".cs":
272 // Common.ScriptEngineBase.Shared.SendToDebug("Source code is CS");
273 // return CompileFromCSText(File.ReadAllText(LSOFileName));
274 // default:
275 // throw new Exception("Unknown script type.");
276 // }
277 //}
278  
279 public string GetCompilerOutput(string assetID)
280 {
281 return Path.Combine(ScriptEnginesPath, Path.Combine(
282 m_scriptEngine.World.RegionInfo.RegionID.ToString(),
283 FilePrefix + "_compiled_" + assetID + ".dll"));
284 }
285  
286 public string GetCompilerOutput(UUID assetID)
287 {
288 return GetCompilerOutput(assetID.ToString());
289 }
290  
291 /// <summary>
292 /// Converts script from LSL to CS and calls CompileFromCSText
293 /// </summary>
294 /// <param name="Script">LSL script</param>
295 /// <returns>Filename to .dll assembly</returns>
296 public void PerformScriptCompile(string Script, string asset, UUID ownerUUID,
297 out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
298 {
299 // m_log.DebugFormat("[Compiler]: Compiling script\n{0}", Script);
300  
301 IScriptModuleComms comms = m_scriptEngine.World.RequestModuleInterface<IScriptModuleComms>();
302  
303 linemap = null;
304 m_warnings.Clear();
305  
306 assembly = GetCompilerOutput(asset);
307  
308 if (!Directory.Exists(ScriptEnginesPath))
309 {
310 try
311 {
312 Directory.CreateDirectory(ScriptEnginesPath);
313 }
314 catch (Exception)
315 {
316 }
317 }
318  
319 if (!Directory.Exists(Path.Combine(ScriptEnginesPath,
320 m_scriptEngine.World.RegionInfo.RegionID.ToString())))
321 {
322 try
323 {
324 Directory.CreateDirectory(ScriptEnginesPath);
325 }
326 catch (Exception)
327 {
328 }
329 }
330  
331 // Don't recompile if we already have it
332 // Performing 3 file exists tests for every script can still be slow
333 if (File.Exists(assembly) && File.Exists(assembly + ".text") && File.Exists(assembly + ".map"))
334 {
335 // If we have already read this linemap file, then it will be in our dictionary.
336 // Don't build another copy of the dictionary (saves memory) and certainly
337 // don't keep reading the same file from disk multiple times.
338 if (!m_lineMaps.ContainsKey(assembly))
339 m_lineMaps[assembly] = ReadMapFile(assembly + ".map");
340 linemap = m_lineMaps[assembly];
341 return;
342 }
343  
344 if (Script == String.Empty)
345 {
346 throw new Exception("Cannot find script assembly and no script text present");
347 }
348  
349 enumCompileType language = DefaultCompileLanguage;
350  
351 if (Script.StartsWith("//c#", true, CultureInfo.InvariantCulture))
352 language = enumCompileType.cs;
353 if (Script.StartsWith("//vb", true, CultureInfo.InvariantCulture))
354 {
355 language = enumCompileType.vb;
356 // We need to remove //vb, it won't compile with that
357  
358 Script = Script.Substring(4, Script.Length - 4);
359 }
360 if (Script.StartsWith("//lsl", true, CultureInfo.InvariantCulture))
361 language = enumCompileType.lsl;
362  
363 if (Script.StartsWith("//js", true, CultureInfo.InvariantCulture))
364 language = enumCompileType.js;
365  
366 if (Script.StartsWith("//yp", true, CultureInfo.InvariantCulture))
367 language = enumCompileType.yp;
368  
369 // m_log.DebugFormat("[Compiler]: Compile language is {0}", language);
370  
371 if (!AllowedCompilers.ContainsKey(language.ToString()))
372 {
373 // Not allowed to compile to this language!
374 string errtext = String.Empty;
375 errtext += "The compiler for language \"" + language.ToString() + "\" is not in list of allowed compilers. Script will not be executed!";
376 throw new Exception(errtext);
377 }
378  
379 if (m_scriptEngine.World.Permissions.CanCompileScript(ownerUUID, (int)language) == false)
380 {
381 // Not allowed to compile to this language!
382 string errtext = String.Empty;
383 errtext += ownerUUID + " is not in list of allowed users for this scripting language. Script will not be executed!";
384 throw new Exception(errtext);
385 }
386  
387 string compileScript = Script;
388  
389 if (language == enumCompileType.lsl)
390 {
391 // Its LSL, convert it to C#
392 LSL_Converter = (ICodeConverter)new CSCodeGenerator(comms, m_insertCoopTerminationCalls);
393 compileScript = LSL_Converter.Convert(Script);
394  
395 // copy converter warnings into our warnings.
396 foreach (string warning in LSL_Converter.GetWarnings())
397 {
398 AddWarning(warning);
399 }
400  
401 linemap = ((CSCodeGenerator)LSL_Converter).PositionMap;
402 // Write the linemap to a file and save it in our dictionary for next time.
403 m_lineMaps[assembly] = linemap;
404 WriteMapFile(assembly + ".map", linemap);
405 }
406  
407 if (language == enumCompileType.yp)
408 {
409 // Its YP, convert it to C#
410 compileScript = YP_Converter.Convert(Script);
411 }
412  
413 switch (language)
414 {
415 case enumCompileType.cs:
416 case enumCompileType.lsl:
417 compileScript = CreateCSCompilerScript(
418 compileScript,
419 m_scriptEngine.ScriptClassName,
420 m_scriptEngine.ScriptBaseClassName,
421 m_scriptEngine.ScriptBaseClassParameters);
422 break;
423 case enumCompileType.vb:
424 compileScript = CreateVBCompilerScript(
425 compileScript, m_scriptEngine.ScriptClassName, m_scriptEngine.ScriptBaseClassName);
426 break;
427 // case enumCompileType.js:
428 // compileScript = CreateJSCompilerScript(compileScript, m_scriptEngine.ScriptBaseClassName);
429 // break;
430 case enumCompileType.yp:
431 compileScript = CreateYPCompilerScript(
432 compileScript, m_scriptEngine.ScriptClassName,m_scriptEngine.ScriptBaseClassName);
433 break;
434 }
435  
436 assembly = CompileFromDotNetText(compileScript, language, asset, assembly);
437 }
438  
439 public string[] GetWarnings()
440 {
441 return m_warnings.ToArray();
442 }
443  
444 private void AddWarning(string warning)
445 {
446 if (!m_warnings.Contains(warning))
447 {
448 m_warnings.Add(warning);
449 }
450 }
451  
452 // private static string CreateJSCompilerScript(string compileScript)
453 // {
454 // compileScript = String.Empty +
455 // "import OpenSim.Region.ScriptEngine.Shared; import System.Collections.Generic;\r\n" +
456 // "package SecondLife {\r\n" +
457 // "class Script extends OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass { \r\n" +
458 // compileScript +
459 // "} }\r\n";
460 // return compileScript;
461 // }
462  
463 private static string CreateCSCompilerScript(
464 string compileScript, string className, string baseClassName, ParameterInfo[] constructorParameters)
465 {
466 compileScript = string.Format(
467 @"using OpenSim.Region.ScriptEngine.Shared;
468 using System.Collections.Generic;
469  
470 namespace SecondLife
471 {{
472 public class {0} : {1}
473 {{
474 public {0}({2}) : base({3}) {{}}
475 {4}
476 }}
477 }}",
478 className,
479 baseClassName,
480 constructorParameters != null
481 ? string.Join(", ", Array.ConvertAll<ParameterInfo, string>(constructorParameters, pi => pi.ToString()))
482 : "",
483 constructorParameters != null
484 ? string.Join(", ", Array.ConvertAll<ParameterInfo, string>(constructorParameters, pi => pi.Name))
485 : "",
486 compileScript);
487  
488 return compileScript;
489 }
490  
491 private static string CreateYPCompilerScript(string compileScript, string className, string baseClassName)
492 {
493 compileScript = String.Empty +
494 "using OpenSim.Region.ScriptEngine.Shared.YieldProlog; " +
495 "using OpenSim.Region.ScriptEngine.Shared; using System.Collections.Generic;\r\n" +
496 String.Empty + "namespace SecondLife { " +
497 String.Empty + "public class " + className + " : " + baseClassName + " { \r\n" +
498 //@"public Script() { } " +
499 @"static OpenSim.Region.ScriptEngine.Shared.YieldProlog.YP YP=null; " +
500 @"public " + className + "() { YP= new OpenSim.Region.ScriptEngine.Shared.YieldProlog.YP(); } " +
501 compileScript +
502 "} }\r\n";
503  
504 return compileScript;
505 }
506  
507 private static string CreateVBCompilerScript(string compileScript, string className, string baseClassName)
508 {
509 compileScript = String.Empty +
510 "Imports OpenSim.Region.ScriptEngine.Shared: Imports System.Collections.Generic: " +
511 String.Empty + "NameSpace SecondLife:" +
512 String.Empty + "Public Class " + className + ": Inherits " + baseClassName +
513 "\r\nPublic Sub New()\r\nEnd Sub: " +
514 compileScript +
515 ":End Class :End Namespace\r\n";
516  
517 return compileScript;
518 }
519  
520 /// <summary>
521 /// Compile .NET script to .Net assembly (.dll)
522 /// </summary>
523 /// <param name="Script">CS script</param>
524 /// <returns>Filename to .dll assembly</returns>
525 internal string CompileFromDotNetText(string Script, enumCompileType lang, string asset, string assembly)
526 {
527 // m_log.DebugFormat("[Compiler]: Compiling to assembly\n{0}", Script);
528  
529 string ext = "." + lang.ToString();
530  
531 // Output assembly name
532 scriptCompileCounter++;
533 try
534 {
535 File.Delete(assembly);
536 }
537 catch (Exception e) // NOTLEGIT - Should be just FileIOException
538 {
539 throw new Exception("Unable to delete old existing " +
540 "script-file before writing new. Compile aborted: " +
541 e.ToString());
542 }
543  
544 // DEBUG - write source to disk
545 if (WriteScriptSourceToDebugFile)
546 {
547 string srcFileName = FilePrefix + "_source_" +
548 Path.GetFileNameWithoutExtension(assembly) + ext;
549 try
550 {
551 File.WriteAllText(Path.Combine(Path.Combine(
552 ScriptEnginesPath,
553 m_scriptEngine.World.RegionInfo.RegionID.ToString()),
554 srcFileName), Script);
555 }
556 catch (Exception ex) //NOTLEGIT - Should be just FileIOException
557 {
558 m_log.Error("[Compiler]: Exception while " +
559 "trying to write script source to file \"" +
560 srcFileName + "\": " + ex.ToString());
561 }
562 }
563  
564 // Do actual compile
565 CompilerParameters parameters = new CompilerParameters();
566  
567 parameters.IncludeDebugInformation = true;
568  
569 string rootPath = AppDomain.CurrentDomain.BaseDirectory;
570  
571 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
572 "OpenSim.Region.ScriptEngine.Shared.dll"));
573 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
574 "OpenSim.Region.ScriptEngine.Shared.Api.Runtime.dll"));
575 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
576 "OpenMetaverseTypes.dll"));
577  
578 if (m_scriptEngine.ScriptReferencedAssemblies != null)
579 Array.ForEach<string>(
580 m_scriptEngine.ScriptReferencedAssemblies,
581 a => parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, a)));
582  
583 if (lang == enumCompileType.yp)
584 {
585 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
586 "OpenSim.Region.ScriptEngine.Shared.YieldProlog.dll"));
587 }
588  
589 parameters.GenerateExecutable = false;
590 parameters.OutputAssembly = assembly;
591 parameters.IncludeDebugInformation = CompileWithDebugInformation;
592 //parameters.WarningLevel = 1; // Should be 4?
593 parameters.TreatWarningsAsErrors = false;
594  
595 CompilerResults results;
596 switch (lang)
597 {
598 case enumCompileType.vb:
599 results = VBcodeProvider.CompileAssemblyFromSource(
600 parameters, Script);
601 break;
602 case enumCompileType.cs:
603 case enumCompileType.lsl:
604 bool complete = false;
605 bool retried = false;
606 do
607 {
608 lock (CScodeProvider)
609 {
610 results = CScodeProvider.CompileAssemblyFromSource(
611 parameters, Script);
612 }
613  
614 // Deal with an occasional segv in the compiler.
615 // Rarely, if ever, occurs twice in succession.
616 // Line # == 0 and no file name are indications that
617 // this is a native stack trace rather than a normal
618 // error log.
619 if (results.Errors.Count > 0)
620 {
621 if (!retried && string.IsNullOrEmpty(results.Errors[0].FileName) &&
622 results.Errors[0].Line == 0)
623 {
624 // System.Console.WriteLine("retrying failed compilation");
625 retried = true;
626 }
627 else
628 {
629 complete = true;
630 }
631 }
632 else
633 {
634 complete = true;
635 }
636 } while (!complete);
637 break;
638 // case enumCompileType.js:
639 // results = JScodeProvider.CompileAssemblyFromSource(
640 // parameters, Script);
641 // break;
642 case enumCompileType.yp:
643 results = YPcodeProvider.CompileAssemblyFromSource(
644 parameters, Script);
645 break;
646 default:
647 throw new Exception("Compiler is not able to recongnize " +
648 "language type \"" + lang.ToString() + "\"");
649 }
650  
651 // foreach (Type type in results.CompiledAssembly.GetTypes())
652 // {
653 // foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
654 // {
655 // m_log.DebugFormat("[COMPILER]: {0}.{1}", type.FullName, method.Name);
656 // }
657 // }
658  
659 //
660 // WARNINGS AND ERRORS
661 //
662 bool hadErrors = false;
663 string errtext = String.Empty;
664 if (results.Errors.Count > 0)
665 {
666 foreach (CompilerError CompErr in results.Errors)
667 {
668 string severity = CompErr.IsWarning ? "Warning" : "Error";
669  
670 KeyValuePair<int, int> errorPos;
671  
672 // Show 5 errors max, but check entire list for errors
673  
674 if (severity == "Error")
675 {
676 // C# scripts will not have a linemap since theres no line translation involved.
677 if (!m_lineMaps.ContainsKey(assembly))
678 errorPos = new KeyValuePair<int, int>(CompErr.Line, CompErr.Column);
679 else
680 errorPos = FindErrorPosition(CompErr.Line, CompErr.Column, m_lineMaps[assembly]);
681  
682 string text = CompErr.ErrorText;
683  
684 // Use LSL type names
685 if (lang == enumCompileType.lsl)
686 text = ReplaceTypes(CompErr.ErrorText);
687  
688 // The Second Life viewer's script editor begins
689 // countingn lines and columns at 0, so we subtract 1.
690 errtext += String.Format("({0},{1}): {4} {2}: {3}\n",
691 errorPos.Key - 1, errorPos.Value - 1,
692 CompErr.ErrorNumber, text, severity);
693 hadErrors = true;
694 }
695 }
696 }
697  
698 if (hadErrors)
699 {
700 throw new Exception(errtext);
701 }
702  
703 // On today's highly asynchronous systems, the result of
704 // the compile may not be immediately apparent. Wait a
705 // reasonable amount of time before giving up on it.
706  
707 if (!File.Exists(assembly))
708 {
709 for (int i = 0; i < 20 && !File.Exists(assembly); i++)
710 {
711 System.Threading.Thread.Sleep(250);
712 }
713 // One final chance...
714 if (!File.Exists(assembly))
715 {
716 errtext = String.Empty;
717 errtext += "No compile error. But not able to locate compiled file.";
718 throw new Exception(errtext);
719 }
720 }
721  
722 // m_log.DebugFormat("[Compiler] Compiled new assembly "+
723 // "for {0}", asset);
724  
725 // Because windows likes to perform exclusive locks, we simply
726 // write out a textual representation of the file here
727 //
728 // Read the binary file into a buffer
729 //
730 FileInfo fi = new FileInfo(assembly);
731  
732 if (fi == null)
733 {
734 errtext = String.Empty;
735 errtext += "No compile error. But not able to stat file.";
736 throw new Exception(errtext);
737 }
738  
739 Byte[] data = new Byte[fi.Length];
740  
741 try
742 {
743 FileStream fs = File.Open(assembly, FileMode.Open, FileAccess.Read);
744 fs.Read(data, 0, data.Length);
745 fs.Close();
746 }
747 catch (Exception)
748 {
749 errtext = String.Empty;
750 errtext += "No compile error. But not able to open file.";
751 throw new Exception(errtext);
752 }
753  
754 // Convert to base64
755 //
756 string filetext = System.Convert.ToBase64String(data);
757  
758 Byte[] buf = Encoding.ASCII.GetBytes(filetext);
759  
760 FileStream sfs = File.Create(assembly + ".text");
761 sfs.Write(buf, 0, buf.Length);
762 sfs.Close();
763  
764 return assembly;
765 }
766  
767 private class kvpSorter : IComparer<KeyValuePair<int, int>>
768 {
769 public int Compare(KeyValuePair<int, int> a,
770 KeyValuePair<int, int> b)
771 {
772 return a.Key.CompareTo(b.Key);
773 }
774 }
775  
776 public static KeyValuePair<int, int> FindErrorPosition(int line,
777 int col, Dictionary<KeyValuePair<int, int>,
778 KeyValuePair<int, int>> positionMap)
779 {
780 if (positionMap == null || positionMap.Count == 0)
781 return new KeyValuePair<int, int>(line, col);
782  
783 KeyValuePair<int, int> ret = new KeyValuePair<int, int>();
784  
785 if (positionMap.TryGetValue(new KeyValuePair<int, int>(line, col),
786 out ret))
787 return ret;
788  
789 List<KeyValuePair<int, int>> sorted =
790 new List<KeyValuePair<int, int>>(positionMap.Keys);
791  
792 sorted.Sort(new kvpSorter());
793  
794 int l = 1;
795 int c = 1;
796  
797 foreach (KeyValuePair<int, int> cspos in sorted)
798 {
799 if (cspos.Key >= line)
800 {
801 if (cspos.Key > line)
802 return new KeyValuePair<int, int>(l, c);
803 if (cspos.Value > col)
804 return new KeyValuePair<int, int>(l, c);
805 c = cspos.Value;
806 if (c == 0)
807 c++;
808 }
809 else
810 {
811 l = cspos.Key;
812 }
813 }
814 return new KeyValuePair<int, int>(l, c);
815 }
816  
817 string ReplaceTypes(string message)
818 {
819 message = message.Replace(
820 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString",
821 "string");
822  
823 message = message.Replace(
824 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger",
825 "integer");
826  
827 message = message.Replace(
828 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat",
829 "float");
830  
831 message = message.Replace(
832 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.list",
833 "list");
834  
835 return message;
836 }
837  
838  
839 private static void WriteMapFile(string filename, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
840 {
841 string mapstring = String.Empty;
842 foreach (KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> kvp in linemap)
843 {
844 KeyValuePair<int, int> k = kvp.Key;
845 KeyValuePair<int, int> v = kvp.Value;
846 mapstring += String.Format("{0},{1},{2},{3}\n", k.Key, k.Value, v.Key, v.Value);
847 }
848  
849 Byte[] mapbytes = Encoding.ASCII.GetBytes(mapstring);
850 FileStream mfs = File.Create(filename);
851 mfs.Write(mapbytes, 0, mapbytes.Length);
852 mfs.Close();
853 }
854  
855  
856 private static Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> ReadMapFile(string filename)
857 {
858 Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap;
859 try
860 {
861 StreamReader r = File.OpenText(filename);
862 linemap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
863  
864 string line;
865 while ((line = r.ReadLine()) != null)
866 {
867 String[] parts = line.Split(new Char[] { ',' });
868 int kk = System.Convert.ToInt32(parts[0]);
869 int kv = System.Convert.ToInt32(parts[1]);
870 int vk = System.Convert.ToInt32(parts[2]);
871 int vv = System.Convert.ToInt32(parts[3]);
872  
873 KeyValuePair<int, int> k = new KeyValuePair<int, int>(kk, kv);
874 KeyValuePair<int, int> v = new KeyValuePair<int, int>(vk, vv);
875  
876 linemap[k] = v;
877 }
878 }
879 catch
880 {
881 linemap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
882 }
883 return linemap;
884 }
885 }
886 }