From 85068dae60db02b168a29ffd75e1408e30d279e1 Mon Sep 17 00:00:00 2001
From: Melanie Thielker
Date: Sat, 27 Sep 2008 05:31:43 +0000
Subject: Add friendly error messages to both engines.

---
 .../ScriptEngine/DotNetEngine/EventQueueManager.cs |   7 +
 .../DotNetEngine/EventQueueThreadClass.cs          |  92 ++++++---
 .../ScriptEngine/DotNetEngine/ScriptManager.cs     |   5 +-
 .../Region/ScriptEngine/Interfaces/ICompiler.cs    |   5 +-
 .../ScriptEngine/Interfaces/IScriptInstance.cs     |   3 +
 .../Shared/CodeTools/CSCodeGenerator.cs            |  19 +-
 .../ScriptEngine/Shared/CodeTools/Compiler.cs      | 225 ++++++++++++++-------
 .../ScriptEngine/Shared/Instance/ScriptInstance.cs |  51 ++++-
 OpenSim/Region/ScriptEngine/XEngine/XEngine.cs     |   5 +-
 9 files changed, 303 insertions(+), 109 deletions(-)

diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
index 634a12b..4a094e2 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
@@ -139,6 +139,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
             public string functionName;
             public DetectParams[] llDetectParams;
             public object[] param;
+            public Dictionary<KeyValuePair<int,int>,KeyValuePair<int,int>>
+                    LineMap;
         }
 
         #endregion
@@ -349,6 +351,9 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
                     return false;
                 }
 
+                InstanceData id = m_ScriptEngine.m_ScriptManager.GetScript(
+                        localID, itemID);
+
                 // Create a structure and add data
                 QueueItemStruct QIS = new QueueItemStruct();
                 QIS.localID = localID;
@@ -356,6 +361,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
                 QIS.functionName = FunctionName;
                 QIS.llDetectParams = qParams;
                 QIS.param = param;
+                if (id != null)
+                    QIS.LineMap = id.LineMap;
 
                 // Add it to queue
                 eventQueue.Enqueue(QIS);
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
index 2d60ed5..16dafdc 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
@@ -27,6 +27,7 @@
 
 using System;
 using System.Collections;
+using System.Collections.Generic;
 using System.Reflection;
 using System.Text.RegularExpressions;
 using System.Threading;
@@ -37,6 +38,7 @@ using OpenSim.Framework;
 using OpenSim.Region.Environment.Scenes;
 using OpenSim.Region.Environment.Scenes.Scripting;
 using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Region.ScriptEngine.Shared.CodeTools;
 
 namespace OpenSim.Region.ScriptEngine.DotNetEngine
 {
@@ -198,10 +200,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
                     }
                     catch (Exception e)
                     {
-                        if (lastScriptEngine != null)
-                            lastScriptEngine.Log.Error("[" + ScriptEngineName +
-                                    "]: Exception in EventQueueThreadLoop: " +
-                                    e.ToString());
                     }
                 }
             }
@@ -290,32 +288,32 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
                         catch (Exception e)
                         {
                             InExecution = false;
+                            string text = FormatException(e, QIS.LineMap);
+
                             // DISPLAY ERROR INWORLD
-                            string text = "Error executing script function \"" +
-                                    QIS.functionName + "\":\r\n";
 
-                            if (e.InnerException != null)
-                            {
-                                // Send inner exception
-                                string line = " (unknown line)";
-                                Regex rx = new Regex(@"SecondLife\.Script\..+[\s:](?<line>\d+)\.?\r?$", RegexOptions.Compiled);
-                                if (rx.Match(e.InnerException.ToString()).Success)
-                                    line = " (line " + rx.Match(e.InnerException.ToString()).Result("${line}") + ")";
-                                text += e.InnerException.Message.ToString() + line;
-                            }
-                            else
-                            {
-                                text += "\r\n";
-                                // Send normal
-                                text += e.Message.ToString();
-                            }
-                            if (KillCurrentScript)
-                                text += "\r\nScript will be deactivated!";
+//                            if (e.InnerException != null)
+//                            {
+//                                // Send inner exception
+//                                string line = " (unknown line)";
+//                                Regex rx = new Regex(@"SecondLife\.Script\..+[\s:](?<line>\d+)\.?\r?$", RegexOptions.Compiled);
+//                                if (rx.Match(e.InnerException.ToString()).Success)
+//                                    line = " (line " + rx.Match(e.InnerException.ToString()).Result("${line}") + ")";
+//                                text += e.InnerException.Message.ToString() + line;
+//                            }
+//                            else
+//                            {
+//                                text += "\r\n";
+//                                // Send normal
+//                                text += e.Message.ToString();
+//                            }
+//                            if (KillCurrentScript)
+//                                text += "\r\nScript will be deactivated!";
 
                             try
                             {
-                                if (text.Length > 1500)
-                                    text = text.Substring(0, 1500);
+                                if (text.Length >= 1100)
+                                    text = text.Substring(0, 1099);
                                 IScriptHost m_host =
                                     m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
                                 m_ScriptEngine.World.SimChat(
@@ -343,10 +341,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
                                         QIS.localID, QIS.itemID);
                                 }
                             }
-
-                            // Pass it on so it's displayed on the console
-                            // and in the logs (mikem 2008.06.02).
-                            throw e.InnerException;
                         }
                         finally
                         {
@@ -358,5 +352,45 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
                 }
             }
         }
+
+        string FormatException(Exception e, Dictionary<KeyValuePair<int,int>,
+                KeyValuePair<int,int>> LineMap)
+        {
+            string message = "Runtime error:\n" + e.InnerException.StackTrace;
+            string[] lines = message.Split(new char[] {'\n'});
+
+            foreach (string line in lines)
+            {
+                if (line.Contains("SecondLife.Script"))
+                {
+                    int idx = line.IndexOf(':');
+                    if (idx != -1)
+                    {
+                        string val = line.Substring(idx+1);
+                        int lineNum = 0;
+                        if (int.TryParse(val, out lineNum))
+                        {
+                            KeyValuePair<int, int> pos =
+                                    Compiler.FindErrorPosition(
+                                    lineNum, 0, LineMap);
+
+                            int scriptLine = pos.Key;
+                            int col = pos.Value;
+                            if (scriptLine == 0)
+                                scriptLine++;
+                            if (col == 0)
+                                col++;
+                            message = string.Format("Runtime error:\n" +
+                                    "Line ({0}): {1}", scriptLine - 1,
+                                    e.InnerException.Message);
+
+                            return message;
+                        }
+                    }
+                }
+            }
+
+            return message;
+        }
     }
 }
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
index 8329805..1c1dcf3 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
@@ -53,6 +53,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
         public int StartParam;
         public AppDomain AppDomain;
         public Dictionary<string, IScriptApi> Apis;
+        public Dictionary<KeyValuePair<int,int>, KeyValuePair<int,int>>
+                LineMap;
     }
 
     public class ScriptManager
@@ -159,6 +161,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
                         m_scriptEngine.m_AppDomainManager.LoadScript(
                         CompiledScriptFile, out id.AppDomain);
 
+                id.LineMap = LSLCompiler.LineMap();
                 id.Script = CompiledScript;
                 id.Source = Script;
                 id.StartParam = startParam;
@@ -212,7 +215,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
                 try
                 {
                     // DISPLAY ERROR INWORLD
-                    string text = "Error compiling script:\r\n" +
+                    string text = "Error compiling script:\n" +
                             e.Message.ToString();
                     if (text.Length > 1100)
                         text = text.Substring(0, 1099);
diff --git a/OpenSim/Region/ScriptEngine/Interfaces/ICompiler.cs b/OpenSim/Region/ScriptEngine/Interfaces/ICompiler.cs
index efb05d3..6273da9 100644
--- a/OpenSim/Region/ScriptEngine/Interfaces/ICompiler.cs
+++ b/OpenSim/Region/ScriptEngine/Interfaces/ICompiler.cs
@@ -33,7 +33,8 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
 {
     public interface ICompiler
     {
-        void Configure(IConfig configSource);
-        void Compile(string text, string outFile, List<IScriptApi> apiList);
+        string PerformScriptCompile(string source, string asset);
+        Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>
+                LineMap();
     }
 }
diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
index 0c807a8..dee7970 100644
--- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
@@ -96,5 +96,8 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
         void DestroyScriptInstance();
 
         IScriptApi GetApi(string name);
+
+        Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> LineMap
+                { get; set; }
     }
 }
diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/CSCodeGenerator.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/CSCodeGenerator.cs
index 86d6188..e8f2b71 100644
--- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/CSCodeGenerator.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/CSCodeGenerator.cs
@@ -89,7 +89,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
             ResetCounters();
             Parser p = new LSLSyntax(new yyLSLSyntax(), new ErrorHandler(true));
             // Obviously this needs to be in a try/except block.
-            LSL2CSCodeTransformer codeTransformer = new LSL2CSCodeTransformer(p.Parse(script));
+            LSL2CSCodeTransformer codeTransformer;
+            try
+            {
+                codeTransformer = new LSL2CSCodeTransformer(p.Parse(script));
+            }
+            catch (CSToolsException e)
+            {
+                string message;
+
+                // LL start numbering lines at 0 - geeks!
+                //
+                message = String.Format("Line ({0},{1}) {2}",
+                        e.slInfo.lineNumber - 1,
+                        e.slInfo.charPosition - 1, e.Message);
+
+                throw new Exception(message);
+            }
+
             m_astRoot = codeTransformer.Transform();
 
             string retstr = String.Empty;
diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs
index ec864e1..c6026fb 100644
--- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs
@@ -38,7 +38,7 @@ using OpenSim.Region.ScriptEngine.Interfaces;
 
 namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
 {
-    public class Compiler
+    public class Compiler : ICompiler
     {
         // private static readonly log4net.ILog m_log
         //     = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
@@ -255,18 +255,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
         /// <returns>Filename to .dll assembly</returns>
         public string PerformScriptCompile(string Script, string asset)
         {
+            m_positionMap = null;
+
             string OutFile = Path.Combine(ScriptEnginesPath, Path.Combine(
                     m_scriptEngine.World.RegionInfo.RegionID.ToString(),
                     FilePrefix + "_compiled_" + asset + ".dll"));
 //            string OutFile = Path.Combine(ScriptEnginesPath,
 //                    FilePrefix + "_compiled_" + asset + ".dll");
 
-            if (File.Exists(OutFile))
-            {
-                m_scriptEngine.Log.DebugFormat("[Compiler] Returning existing assembly for {0}", asset);
-                return OutFile;
-            }
-
             if (!Directory.Exists(ScriptEnginesPath))
             {
                 try
@@ -327,38 +323,26 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
                 compileScript = LSL_Converter.Convert(Script);
 
                 m_positionMap = ((CSCodeGenerator) LSL_Converter).PositionMap;
+            }
 
-                l = enumCompileType.cs;
+            // Check this late so the map is generated on sim start
+            //
+            if (File.Exists(OutFile))
+            {
+                m_scriptEngine.Log.DebugFormat("[Compiler] Returning existing assembly for {0}", asset);
+                return OutFile;
             }
 
             if (l == enumCompileType.yp)
             {
                 // Its YP, convert it to C#
                 compileScript = YP_Converter.Convert(Script);
-                // We have our own processor now
-                //l = enumCompileType.cs;
-            }
-
-            // Insert additional assemblies here
-
-            //ADAM: Disabled for the moment until it's working right.
-            bool enableCommanderLSL = false;
-
-            if (enableCommanderLSL == true && ((l == enumCompileType.cs) || (l == enumCompileType.yp)))
-            {
-                foreach (KeyValuePair<string,
-                    ICommander> com
-                    in m_scriptEngine.World.GetCommanders())
-                {
-                    compileScript = com.Value.GenerateRuntimeAPI() + compileScript;
-                }
             }
 
-            // End of insert
-
             switch (l)
             {
                 case enumCompileType.cs:
+                case enumCompileType.lsl:
                     compileScript = CreateCSCompilerScript(compileScript);
                     break;
                 case enumCompileType.vb:
@@ -372,10 +356,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
                     break;
             }
 
-//            m_log.Debug("[ScriptEngine.DotNetEngine]: Preparing to compile the following LSL to C# translated code");
-//            m_log.Debug("");
-//            m_log.Debug(compileScript);
-
             return CompileFromDotNetText(compileScript, l, asset);
         }
 
@@ -442,26 +422,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
             // Output assembly name
             scriptCompileCounter++;
             string OutFile = Path.Combine(ScriptEnginesPath, Path.Combine(
-                                              m_scriptEngine.World.RegionInfo.RegionID.ToString(),
-                                              FilePrefix + "_compiled_" + asset + ".dll"));
-#if DEBUG
-//            m_scriptEngine.Log.Debug("[Compiler]: Starting compile of \"" + OutFile + "\".");
-#endif
+                    m_scriptEngine.World.RegionInfo.RegionID.ToString(),
+                    FilePrefix + "_compiled_" + asset + ".dll"));
             try
             {
                 File.Delete(OutFile);
             }
-            catch (Exception e) // NOTLEGIT - Should be just catching FileIOException
+            catch (Exception e) // NOTLEGIT - Should be just FileIOException
             {
-                //m_scriptEngine.Log.Error("[Compiler]: Unable to delete old existring script-file before writing new. Compile aborted: " + e.ToString());
-                throw new Exception("Unable to delete old existring script-file before writing new. Compile aborted: " + e.ToString());
+                throw new Exception("Unable to delete old existing "+
+                        "script-file before writing new. Compile aborted: " +
+                        e.ToString());
             }
-            //string OutFile = Path.Combine("ScriptEngines", "SecondLife.Script.dll");
 
             // DEBUG - write source to disk
             if (WriteScriptSourceToDebugFile)
             {
-                string srcFileName = FilePrefix + "_source_" + Path.GetFileNameWithoutExtension(OutFile) + ext;
+                string srcFileName = FilePrefix + "_source_" +
+                        Path.GetFileNameWithoutExtension(OutFile) + ext;
                 try
                 {
                     File.WriteAllText(Path.Combine(Path.Combine(
@@ -469,9 +447,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
                         m_scriptEngine.World.RegionInfo.RegionID.ToString()),
                         srcFileName), Script);
                 }
-                catch (Exception ex) // NOTLEGIT - Should be just catching FileIOException
+                catch (Exception ex) //NOTLEGIT - Should be just FileIOException
                 {
-                    m_scriptEngine.Log.Error("[Compiler]: Exception while trying to write script source to file \"" + srcFileName + "\": " + ex.ToString());
+                    m_scriptEngine.Log.Error("[Compiler]: Exception while "+
+                            "trying to write script source to file \"" +
+                            srcFileName + "\": " + ex.ToString());
                 }
             }
 
@@ -480,22 +460,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
 
             parameters.IncludeDebugInformation = true;
 
-            // Add all available assemblies
-//             foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
-//             {
-//                 Console.WriteLine("Adding assembly: " + asm.Location);
-//                 parameters.ReferencedAssemblies.Add(asm.Location);
-//             }
+            string rootPath =
+                Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
 
-            string rootPath = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
-            // string rootPathSE = Path.GetDirectoryName(GetType().Assembly.Location);
-            //Console.WriteLine("Assembly location: " + rootPath);
-            parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, "OpenSim.Region.ScriptEngine.Shared.dll"));
-            parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, "OpenSim.Region.ScriptEngine.Shared.Api.Runtime.dll"));
+            parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
+                    "OpenSim.Region.ScriptEngine.Shared.dll"));
+            parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
+                    "OpenSim.Region.ScriptEngine.Shared.Api.Runtime.dll"));
 
             if (lang == enumCompileType.yp)
             {
-                parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, "OpenSim.Region.ScriptEngine.Shared.YieldProlog.dll"));
+                parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
+                        "OpenSim.Region.ScriptEngine.Shared.YieldProlog.dll"));
             }
 
             parameters.GenerateExecutable = false;
@@ -504,24 +480,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
             //parameters.WarningLevel = 1; // Should be 4?
             parameters.TreatWarningsAsErrors = false;
 
-//Console.WriteLine(Script);
             CompilerResults results;
             switch (lang)
             {
                 case enumCompileType.vb:
-                    results = VBcodeProvider.CompileAssemblyFromSource(parameters, Script);
+                    results = VBcodeProvider.CompileAssemblyFromSource(
+                            parameters, Script);
                     break;
                 case enumCompileType.cs:
-                    results = CScodeProvider.CompileAssemblyFromSource(parameters, Script);
+                case enumCompileType.lsl:
+                    results = CScodeProvider.CompileAssemblyFromSource(
+                            parameters, Script);
                     break;
                 case enumCompileType.js:
-                    results = JScodeProvider.CompileAssemblyFromSource(parameters, Script);
+                    results = JScodeProvider.CompileAssemblyFromSource(
+                            parameters, Script);
                     break;
                 case enumCompileType.yp:
-                    results = YPcodeProvider.CompileAssemblyFromSource(parameters, Script);
+                    results = YPcodeProvider.CompileAssemblyFromSource(
+                            parameters, Script);
                     break;
                 default:
-                    throw new Exception("Compiler is not able to recongnize language type \"" + lang.ToString() + "\"");
+                    throw new Exception("Compiler is not able to recongnize "+
+                            "language type \"" + lang.ToString() + "\"");
             }
 
             // Check result
@@ -530,11 +511,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
             //
             // WARNINGS AND ERRORS
             //
+            int display = 5;
             if (results.Errors.Count > 0)
             {
                 string errtext = String.Empty;
                 foreach (CompilerError CompErr in results.Errors)
                 {
+                    // Show 5 errors max
+                    //
+                    if (display <= 0)
+                        break;
+                    display--;
+
                     string severity = "Error";
                     if ( CompErr.IsWarning )
                     {
@@ -543,22 +531,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
 
                     KeyValuePair<int, int> lslPos;
 
-                    try
-                    {
-                        lslPos = m_positionMap[new KeyValuePair<int, int>(CompErr.Line, CompErr.Column)];
-                    }
-                    catch (KeyNotFoundException)  // we don't have this line/column mapped
-                    {
-                        m_scriptEngine.Log.Debug(String.Format("[Compiler]: Lookup of C# line {0}, column {1} failed.", CompErr.Line, CompErr.Column));
-                        lslPos = new KeyValuePair<int, int>(-CompErr.Line, -CompErr.Column);
-                    }
+                    lslPos = FindErrorPosition(CompErr.Line, CompErr.Column);
+
+                    string text = CompErr.ErrorText;
+
+                    // Use LSL type names
+                    if (lang == enumCompileType.lsl)
+                        text = ReplaceTypes(CompErr.ErrorText);
 
                     // The Second Life viewer's script editor begins
                     // countingn lines and columns at 0, so we subtract 1.
-                    errtext += String.Format("Line {0}, column {1}, {4} Number: {2}, '{3}'\r\n", lslPos.Key - 1, lslPos.Value - 1, CompErr.ErrorNumber, CompErr.ErrorText, severity);
+                    errtext += String.Format("Line ({0},{1}): {4} {2}: {3}\n",
+                            lslPos.Key - 1, lslPos.Value - 1,
+                            CompErr.ErrorNumber, text, severity);
                 }
                 
-                Console.WriteLine("[COMPILER MESSAGES]: " + errtext);
                 if (!File.Exists(OutFile))
                 {
                     throw new Exception(errtext);
@@ -574,8 +561,100 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
                 errtext += "No compile error. But not able to locate compiled file.";
                 throw new Exception(errtext);
             }
-            m_scriptEngine.Log.DebugFormat("[Compiler] Compiled new assembly for {0}", asset);
+            m_scriptEngine.Log.DebugFormat("[Compiler] Compiled new assembly "+
+                    "for {0}", asset);
             return OutFile;
         }
+
+        public KeyValuePair<int, int> FindErrorPosition(int line, int col)
+        {
+            return FindErrorPosition(line, col, m_positionMap);
+        }
+
+        private class kvpSorter : IComparer<KeyValuePair<int,int>>
+        {
+            public int Compare(KeyValuePair<int,int> a,
+                    KeyValuePair<int,int> b)
+            {
+                return a.Key.CompareTo(b.Key);
+            }
+        }
+
+        public static KeyValuePair<int, int> FindErrorPosition(int line,
+                int col, Dictionary<KeyValuePair<int, int>,
+                KeyValuePair<int, int>> positionMap)
+        {
+            if (positionMap == null || positionMap.Count == 0)
+                return new KeyValuePair<int, int>(line, col);
+
+            KeyValuePair<int, int> ret = new KeyValuePair<int, int>();
+
+            if (positionMap.TryGetValue(new KeyValuePair<int, int>(line, col),
+                    out ret))
+                return ret;
+
+            List<KeyValuePair<int,int>> sorted =
+                    new List<KeyValuePair<int,int>>(positionMap.Keys);
+
+            sorted.Sort(new kvpSorter());
+
+            int l = 1;
+            int c = 1;
+
+            foreach (KeyValuePair<int, int> cspos in sorted)
+            {
+                if (cspos.Key >= line)
+                {
+                    if (cspos.Key > line)
+                        return new KeyValuePair<int, int>(l, c);
+                    if (cspos.Value > col)
+                        return new KeyValuePair<int, int>(l, c);
+                    c = cspos.Value;
+                    if (c == 0)
+                        c++;
+                }
+                else
+                {
+                    l = cspos.Key;
+                }
+            }
+            return new KeyValuePair<int, int>(l, c);
+        }
+
+        string ReplaceTypes(string message)
+        {
+            message = message.Replace(
+                    "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString",
+                    "string");
+
+            message = message.Replace(
+                    "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger",
+                    "integer");
+
+            message = message.Replace(
+                    "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat",
+                    "float");
+
+            message = message.Replace(
+                    "OpenSim.Region.ScriptEngine.Shared.LSL_Types.list",
+                    "list");
+
+            return message;
+        }
+
+        public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>
+                LineMap()
+        {
+            if (m_positionMap == null)
+                return null;
+
+            Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> ret =
+                new Dictionary<KeyValuePair<int,int>, KeyValuePair<int, int>>();
+
+            foreach (KeyValuePair<int, int> kvp in m_positionMap.Keys)
+                ret.Add(kvp, m_positionMap[kvp]);
+
+            return ret;
+        }
     }
 }
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
index c8d60af..aa9ace4 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
@@ -80,6 +80,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
         private int m_ControlEventsInQueue = 0;
         private int m_LastControlLevel = 0;
         private bool m_CollisionInQueue = false;
+        private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>
+                m_LineMap;
+
+        public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>
+                LineMap
+        {
+            get { return m_LineMap; }
+            set { m_LineMap = value; }
+        }
 
         private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>();
 
@@ -628,7 +637,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
                                 try
                                 {
                                     // DISPLAY ERROR INWORLD
-                                    string text = "Runtime error:\n" + e.InnerException.ToString();
+                                    string text = FormatException(e);
+
                                     if (text.Length > 1000)
                                         text = text.Substring(0, 1000);
                                     m_Engine.World.SimChat(Utils.StringToBytes(text),
@@ -812,5 +822,44 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
         {
             return String.Format("{0} {1} on {2}", m_ScriptName, m_ItemID, m_PrimName);   
         }
+
+        string FormatException(Exception e)
+        {
+            string message = "Runtime error:\n" + e.InnerException.StackTrace;
+            string[] lines = message.Split(new char[] {'\n'});
+
+            foreach (string line in lines)
+            {
+                if (line.Contains("SecondLife.Script"))
+                {
+                    int idx = line.IndexOf(':');
+                    if (idx != -1)
+                    {
+                        string val = line.Substring(idx+1);
+                        int lineNum = 0;
+                        if (int.TryParse(val, out lineNum))
+                        {
+                            KeyValuePair<int, int> pos =
+                                    Compiler.FindErrorPosition(
+                                    lineNum, 0, LineMap);
+
+                            int scriptLine = pos.Key;
+                            int col = pos.Value;
+                            if (scriptLine == 0)
+                                scriptLine++;
+                            if (col == 0)
+                                col++;
+                            message = string.Format("Runtime error:\n" +
+                                    "Line ({0}): {1}", scriptLine - 1,
+                                    e.InnerException.Message);
+
+                            return message;
+                        }
+                    }
+                }
+            }
+
+            return message;
+        }
     }
 }
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
index a37cbb4..6bafd69 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
@@ -59,7 +59,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
         private int m_MaxScriptQueue;
         private Scene m_Scene;
         private IConfig m_ScriptConfig;
-        private Compiler m_Compiler;
+        private ICompiler m_Compiler;
         private int m_MinThreads;
         private int m_MaxThreads ;
         private int m_IdleTimeout;
@@ -510,7 +510,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
                 try
                 {
                     // DISPLAY ERROR INWORLD
-                    string text = "Error compiling script:\r\n" + e.Message.ToString();
+                    string text = "Error compiling script:\n" + e.ToString();
                     if (text.Length > 1000)
                         text = text.Substring(0, 1000);
                     World.SimChat(Utils.StringToBytes(text),
@@ -585,6 +585,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
                             part.ParentGroup.RootPart.Name, item.Name);
 
                     instance.AppDomain = appDomain;
+                    instance.LineMap = m_Compiler.LineMap();
 
                     m_Scripts[itemID] = instance;
                 }
-- 
cgit v1.1