clockwerk-opensim-stable – 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 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 // Deal with an occasional segv in the compiler.
614 // Rarely, if ever, occurs twice in succession.
615 // Line # == 0 and no file name are indications that
616 // this is a native stack trace rather than a normal
617 // error log.
618 if (results.Errors.Count > 0)
619 {
620 if (!retried && (results.Errors[0].FileName == null || results.Errors[0].FileName == String.Empty) &&
621 results.Errors[0].Line == 0)
622 {
623 // System.Console.WriteLine("retrying failed compilation");
624 retried = true;
625 }
626 else
627 {
628 complete = true;
629 }
630 }
631 else
632 {
633 complete = true;
634 }
635 } while (!complete);
636 break;
637 // case enumCompileType.js:
638 // results = JScodeProvider.CompileAssemblyFromSource(
639 // parameters, Script);
640 // break;
641 case enumCompileType.yp:
642 results = YPcodeProvider.CompileAssemblyFromSource(
643 parameters, Script);
644 break;
645 default:
646 throw new Exception("Compiler is not able to recongnize " +
647 "language type \"" + lang.ToString() + "\"");
648 }
649  
650 // Check result
651 // Go through errors
652  
653 //
654 // WARNINGS AND ERRORS
655 //
656 bool hadErrors = false;
657 string errtext = String.Empty;
658  
659 if (results.Errors.Count > 0)
660 {
661 foreach (CompilerError CompErr in results.Errors)
662 {
663 string severity = CompErr.IsWarning ? "Warning" : "Error";
664  
665 KeyValuePair<int, int> errorPos;
666  
667 // Show 5 errors max, but check entire list for errors
668  
669 if (severity == "Error")
670 {
671 // C# scripts will not have a linemap since theres no line translation involved.
672 if (!m_lineMaps.ContainsKey(assembly))
673 errorPos = new KeyValuePair<int, int>(CompErr.Line, CompErr.Column);
674 else
675 errorPos = FindErrorPosition(CompErr.Line, CompErr.Column, m_lineMaps[assembly]);
676  
677 string text = CompErr.ErrorText;
678  
679 // Use LSL type names
680 if (lang == enumCompileType.lsl)
681 text = ReplaceTypes(CompErr.ErrorText);
682  
683 // The Second Life viewer's script editor begins
684 // countingn lines and columns at 0, so we subtract 1.
685 errtext += String.Format("({0},{1}): {4} {2}: {3}\n",
686 errorPos.Key - 1, errorPos.Value - 1,
687 CompErr.ErrorNumber, text, severity);
688 hadErrors = true;
689 }
690 }
691 }
692  
693 if (hadErrors)
694 {
695 throw new Exception(errtext);
696 }
697  
698 // On today's highly asynchronous systems, the result of
699 // the compile may not be immediately apparent. Wait a
700 // reasonable amount of time before giving up on it.
701  
702 if (!File.Exists(assembly))
703 {
704 for (int i = 0; i < 20 && !File.Exists(assembly); i++)
705 {
706 System.Threading.Thread.Sleep(250);
707 }
708 // One final chance...
709 if (!File.Exists(assembly))
710 {
711 errtext = String.Empty;
712 errtext += "No compile error. But not able to locate compiled file.";
713 throw new Exception(errtext);
714 }
715 }
716  
717 // m_log.DebugFormat("[Compiler] Compiled new assembly "+
718 // "for {0}", asset);
719  
720 // Because windows likes to perform exclusive locks, we simply
721 // write out a textual representation of the file here
722 //
723 // Read the binary file into a buffer
724 //
725 FileInfo fi = new FileInfo(assembly);
726  
727 if (fi == null)
728 {
729 errtext = String.Empty;
730 errtext += "No compile error. But not able to stat file.";
731 throw new Exception(errtext);
732 }
733  
734 Byte[] data = new Byte[fi.Length];
735  
736 try
737 {
738 FileStream fs = File.Open(assembly, FileMode.Open, FileAccess.Read);
739 fs.Read(data, 0, data.Length);
740 fs.Close();
741 }
742 catch (Exception)
743 {
744 errtext = String.Empty;
745 errtext += "No compile error. But not able to open file.";
746 throw new Exception(errtext);
747 }
748  
749 // Convert to base64
750 //
751 string filetext = System.Convert.ToBase64String(data);
752  
753 Byte[] buf = Encoding.ASCII.GetBytes(filetext);
754  
755 FileStream sfs = File.Create(assembly + ".text");
756 sfs.Write(buf, 0, buf.Length);
757 sfs.Close();
758  
759 return assembly;
760 }
761  
762 private class kvpSorter : IComparer<KeyValuePair<int, int>>
763 {
764 public int Compare(KeyValuePair<int, int> a,
765 KeyValuePair<int, int> b)
766 {
767 return a.Key.CompareTo(b.Key);
768 }
769 }
770  
771 public static KeyValuePair<int, int> FindErrorPosition(int line,
772 int col, Dictionary<KeyValuePair<int, int>,
773 KeyValuePair<int, int>> positionMap)
774 {
775 if (positionMap == null || positionMap.Count == 0)
776 return new KeyValuePair<int, int>(line, col);
777  
778 KeyValuePair<int, int> ret = new KeyValuePair<int, int>();
779  
780 if (positionMap.TryGetValue(new KeyValuePair<int, int>(line, col),
781 out ret))
782 return ret;
783  
784 List<KeyValuePair<int, int>> sorted =
785 new List<KeyValuePair<int, int>>(positionMap.Keys);
786  
787 sorted.Sort(new kvpSorter());
788  
789 int l = 1;
790 int c = 1;
791  
792 foreach (KeyValuePair<int, int> cspos in sorted)
793 {
794 if (cspos.Key >= line)
795 {
796 if (cspos.Key > line)
797 return new KeyValuePair<int, int>(l, c);
798 if (cspos.Value > col)
799 return new KeyValuePair<int, int>(l, c);
800 c = cspos.Value;
801 if (c == 0)
802 c++;
803 }
804 else
805 {
806 l = cspos.Key;
807 }
808 }
809 return new KeyValuePair<int, int>(l, c);
810 }
811  
812 string ReplaceTypes(string message)
813 {
814 message = message.Replace(
815 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString",
816 "string");
817  
818 message = message.Replace(
819 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger",
820 "integer");
821  
822 message = message.Replace(
823 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat",
824 "float");
825  
826 message = message.Replace(
827 "OpenSim.Region.ScriptEngine.Shared.LSL_Types.list",
828 "list");
829  
830 return message;
831 }
832  
833  
834 private static void WriteMapFile(string filename, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
835 {
836 string mapstring = String.Empty;
837 foreach (KeyValuePair<KeyValuePair<int, int>, KeyValuePair<int, int>> kvp in linemap)
838 {
839 KeyValuePair<int, int> k = kvp.Key;
840 KeyValuePair<int, int> v = kvp.Value;
841 mapstring += String.Format("{0},{1},{2},{3}\n", k.Key, k.Value, v.Key, v.Value);
842 }
843  
844 Byte[] mapbytes = Encoding.ASCII.GetBytes(mapstring);
845 FileStream mfs = File.Create(filename);
846 mfs.Write(mapbytes, 0, mapbytes.Length);
847 mfs.Close();
848 }
849  
850  
851 private static Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> ReadMapFile(string filename)
852 {
853 Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap;
854 try
855 {
856 StreamReader r = File.OpenText(filename);
857 linemap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
858  
859 string line;
860 while ((line = r.ReadLine()) != null)
861 {
862 String[] parts = line.Split(new Char[] { ',' });
863 int kk = System.Convert.ToInt32(parts[0]);
864 int kv = System.Convert.ToInt32(parts[1]);
865 int vk = System.Convert.ToInt32(parts[2]);
866 int vv = System.Convert.ToInt32(parts[3]);
867  
868 KeyValuePair<int, int> k = new KeyValuePair<int, int>(kk, kv);
869 KeyValuePair<int, int> v = new KeyValuePair<int, int>(vk, vv);
870  
871 linemap[k] = v;
872 }
873 }
874 catch
875 {
876 linemap = new Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>();
877 }
878 return linemap;
879 }
880 }
881 }