Implement non-wait co-operative termination of scripts for XEngine in addition to termination on wait.

This involves inserting opensim_reserved_CheckForCoopTermination() calls in lsl -> c# translation at any place where the script could be in a loop with no wait calls.
These places are for, while, do-while, label, user function call and manual event function call.
Call goes through to an XEngineScriptBase which extends ScriptBase.
IEngine is extended to supply necessary engine-specific parent class references and constructor parameters to Compiler.
Unfortunately, since XEngineScriptBase has to be passed WaitHandle in its constructor, older compiled scripts will fail to load with an error on the OpenSim console.
Such scripts will need to be recompiled, either by removing all *.dll files from the bin/ScriptEngines/<region-id> or by setting DeleteScriptsOnStartup = true in [XEngine] for one run.
Automatic recompilation may be implemented in a later commit.
This feature should not yet be used, default remains termination with Thread.Abort() which will work as normal once scripts are recompiled.
This commit is contained in:
Justin Clark-Casey (justincc)
2013-01-17 23:39:09 +00:00
parent 9c99ed26eb
commit c8afc8523b
8 changed files with 307 additions and 77 deletions

View File

@@ -49,6 +49,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
private List<string> m_warnings = new List<string>();
private IScriptModuleComms m_comms = null;
private bool m_insertCoopTerminationChecks;
private static string m_coopTerminationCheck = "opensim_reserved_CheckForCoopTermination();";
/// <summary>
/// Keep a record of the previous node when we do the parsing.
/// </summary>
/// <remarks>
/// We do this here because the parser generated by CSTools does not retain a reference to its parent node.
/// The previous node is required so we can correctly insert co-op termination checks when required.
/// </remarks>
// private SYMBOL m_previousNode;
/// <summary>
/// Creates an 'empty' CSCodeGenerator instance.
/// </summary>
@@ -58,9 +70,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
ResetCounters();
}
public CSCodeGenerator(IScriptModuleComms comms)
public CSCodeGenerator(IScriptModuleComms comms, bool insertCoopTerminationChecks)
{
m_comms = comms;
m_insertCoopTerminationChecks = insertCoopTerminationChecks;
ResetCounters();
}
@@ -155,7 +168,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
// here's the payload
retstr += GenerateLine();
foreach (SYMBOL s in m_astRoot.kids)
retstr += GenerateNode(s);
retstr += GenerateNode(m_astRoot, s);
// close braces!
m_braceCount--;
@@ -165,7 +178,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
// Removes all carriage return characters which may be generated in Windows platform. Is there
// cleaner way of doing this?
retstr=retstr.Replace("\r", "");
retstr = retstr.Replace("\r", "");
return retstr;
}
@@ -191,9 +204,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
/// Recursively called to generate each type of node. Will generate this
/// node, then all it's children.
/// </summary>
/// <param name="previousSymbol">The parent node.</param>
/// <param name="s">The current node to generate code for.</param>
/// <returns>String containing C# code for SYMBOL s.</returns>
private string GenerateNode(SYMBOL s)
private string GenerateNode(SYMBOL previousSymbol, SYMBOL s)
{
string retstr = String.Empty;
@@ -207,11 +221,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
else if (s is State)
retstr += GenerateState((State) s);
else if (s is CompoundStatement)
retstr += GenerateCompoundStatement((CompoundStatement) s);
retstr += GenerateCompoundStatement(previousSymbol, (CompoundStatement) s);
else if (s is Declaration)
retstr += GenerateDeclaration((Declaration) s);
else if (s is Statement)
retstr += GenerateStatement((Statement) s);
retstr += GenerateStatement(previousSymbol, (Statement) s);
else if (s is ReturnStatement)
retstr += GenerateReturnStatement((ReturnStatement) s);
else if (s is JumpLabel)
@@ -261,7 +275,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
else
{
foreach (SYMBOL kid in s.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(s, kid);
}
return retstr;
@@ -295,7 +309,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
retstr += GenerateLine(")");
foreach (SYMBOL kid in remainingKids)
retstr += GenerateNode(kid);
retstr += GenerateNode(gf, kid);
return retstr;
}
@@ -312,7 +326,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
foreach (SYMBOL s in gv.kids)
{
retstr += Indent();
retstr += GenerateNode(s);
retstr += GenerateNode(gv, s);
retstr += GenerateLine(";");
}
@@ -365,7 +379,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
retstr += GenerateLine(")");
foreach (SYMBOL kid in remainingKids)
retstr += GenerateNode(kid);
retstr += GenerateNode(se, kid);
return retstr;
}
@@ -404,7 +418,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
foreach (SYMBOL s in al.kids)
{
retstr += GenerateNode(s);
retstr += GenerateNode(al, s);
if (0 < comma--)
retstr += Generate(", ");
}
@@ -417,7 +431,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
/// </summary>
/// <param name="cs">The CompoundStatement node.</param>
/// <returns>String containing C# code for CompoundStatement cs.</returns>
private string GenerateCompoundStatement(CompoundStatement cs)
private string GenerateCompoundStatement(SYMBOL previousSymbol, CompoundStatement cs)
{
string retstr = String.Empty;
@@ -425,8 +439,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
retstr += GenerateIndentedLine("{");
m_braceCount++;
if (m_insertCoopTerminationChecks)
{
// We have to check in event functions as well because the user can manually call these.
if (previousSymbol is GlobalFunctionDefinition
|| previousSymbol is WhileStatement
|| previousSymbol is DoWhileStatement
|| previousSymbol is ForLoopStatement
|| previousSymbol is StateEvent)
retstr += GenerateIndentedLine(m_coopTerminationCheck);
}
foreach (SYMBOL kid in cs.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(cs, kid);
// closing brace
m_braceCount--;
@@ -450,13 +475,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
/// </summary>
/// <param name="s">The Statement node.</param>
/// <returns>String containing C# code for Statement s.</returns>
private string GenerateStatement(Statement s)
private string GenerateStatement(SYMBOL previousSymbol, Statement s)
{
string retstr = String.Empty;
bool printSemicolon = true;
retstr += Indent();
if (m_insertCoopTerminationChecks)
{
// We have to check in event functions as well because the user can manually call these.
if (previousSymbol is GlobalFunctionDefinition
|| previousSymbol is WhileStatement
|| previousSymbol is DoWhileStatement
|| previousSymbol is ForLoop
|| previousSymbol is StateEvent)
retstr += Generate(m_coopTerminationCheck);
}
if (0 < s.kids.Count)
{
// Jump label prints its own colon, we don't need a semicolon.
@@ -466,7 +502,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
// (MONO) error.
if (!(s.kids.Top is IdentExpression && 1 == s.kids.Count))
foreach (SYMBOL kid in s.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(s, kid);
}
if (printSemicolon)
@@ -487,10 +523,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
List<string> identifiers = new List<string>();
checkForMultipleAssignments(identifiers, a);
retstr += GenerateNode((SYMBOL) a.kids.Pop());
retstr += GenerateNode(a, (SYMBOL) a.kids.Pop());
retstr += Generate(String.Format(" {0} ", a.AssignmentType), a);
foreach (SYMBOL kid in a.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(a, kid);
return retstr;
}
@@ -563,7 +599,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
retstr += Generate("return ", rs);
foreach (SYMBOL kid in rs.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(rs, kid);
return retstr;
}
@@ -575,7 +611,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
/// <returns>String containing C# code for JumpLabel jl.</returns>
private string GenerateJumpLabel(JumpLabel jl)
{
return Generate(String.Format("{0}:", CheckName(jl.LabelName)), jl) + " NoOp();\n";
string labelStatement;
if (m_insertCoopTerminationChecks)
labelStatement = m_coopTerminationCheck + "\n";
else
labelStatement = "NoOp();\n";
return Generate(String.Format("{0}: ", CheckName(jl.LabelName)), jl) + labelStatement;
}
/// <summary>
@@ -598,14 +641,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
string retstr = String.Empty;
retstr += GenerateIndented("if (", ifs);
retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop());
retstr += GenerateLine(")");
// CompoundStatement handles indentation itself but we need to do it
// otherwise.
bool indentHere = ifs.kids.Top is Statement;
if (indentHere) m_braceCount++;
retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop());
if (indentHere) m_braceCount--;
if (0 < ifs.kids.Count) // do it again for an else
@@ -614,7 +657,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
indentHere = ifs.kids.Top is Statement;
if (indentHere) m_braceCount++;
retstr += GenerateNode((SYMBOL) ifs.kids.Pop());
retstr += GenerateNode(ifs, (SYMBOL) ifs.kids.Pop());
if (indentHere) m_braceCount--;
}
@@ -641,14 +684,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
string retstr = String.Empty;
retstr += GenerateIndented("while (", ws);
retstr += GenerateNode((SYMBOL) ws.kids.Pop());
retstr += GenerateNode(ws, (SYMBOL) ws.kids.Pop());
retstr += GenerateLine(")");
// CompoundStatement handles indentation itself but we need to do it
// otherwise.
bool indentHere = ws.kids.Top is Statement;
if (indentHere) m_braceCount++;
retstr += GenerateNode((SYMBOL) ws.kids.Pop());
retstr += GenerateNode(ws, (SYMBOL) ws.kids.Pop());
if (indentHere) m_braceCount--;
return retstr;
@@ -669,11 +712,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
// otherwise.
bool indentHere = dws.kids.Top is Statement;
if (indentHere) m_braceCount++;
retstr += GenerateNode((SYMBOL) dws.kids.Pop());
retstr += GenerateNode(dws, (SYMBOL) dws.kids.Pop());
if (indentHere) m_braceCount--;
retstr += GenerateIndented("while (", dws);
retstr += GenerateNode((SYMBOL) dws.kids.Pop());
retstr += GenerateNode(dws, (SYMBOL) dws.kids.Pop());
retstr += GenerateLine(");");
return retstr;
@@ -702,7 +745,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
retstr += Generate("; ");
// for (x = 0; x < 10; x++)
// ^^^^^^
retstr += GenerateNode((SYMBOL) fl.kids.Pop());
retstr += GenerateNode(fl, (SYMBOL) fl.kids.Pop());
retstr += Generate("; ");
// for (x = 0; x < 10; x++)
// ^^^
@@ -713,7 +756,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
// otherwise.
bool indentHere = fl.kids.Top is Statement;
if (indentHere) m_braceCount++;
retstr += GenerateNode((SYMBOL) fl.kids.Pop());
retstr += GenerateNode(fl, (SYMBOL) fl.kids.Pop());
if (indentHere) m_braceCount--;
return retstr;
@@ -758,7 +801,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
while (s is ParenthesisExpression)
s = (SYMBOL)s.kids.Pop();
retstr += GenerateNode(s);
retstr += GenerateNode(fls, s);
if (0 < comma--)
retstr += Generate(", ");
}
@@ -779,20 +822,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
{
// special case handling for logical and/or, see Mantis 3174
retstr += "((bool)(";
retstr += GenerateNode((SYMBOL)be.kids.Pop());
retstr += GenerateNode(be, (SYMBOL)be.kids.Pop());
retstr += "))";
retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol.Substring(0,1)), be);
retstr += "((bool)(";
foreach (SYMBOL kid in be.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(be, kid);
retstr += "))";
}
else
{
retstr += GenerateNode((SYMBOL)be.kids.Pop());
retstr += GenerateNode(be, (SYMBOL)be.kids.Pop());
retstr += Generate(String.Format(" {0} ", be.ExpressionSymbol), be);
foreach (SYMBOL kid in be.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(be, kid);
}
return retstr;
@@ -808,7 +851,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
string retstr = String.Empty;
retstr += Generate(ue.UnarySymbol, ue);
retstr += GenerateNode((SYMBOL) ue.kids.Pop());
retstr += GenerateNode(ue, (SYMBOL) ue.kids.Pop());
return retstr;
}
@@ -824,7 +867,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
retstr += Generate("(");
foreach (SYMBOL kid in pe.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(pe, kid);
retstr += Generate(")");
return retstr;
@@ -861,7 +904,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
// we wrap all typecasted statements in parentheses
retstr += Generate(String.Format("({0}) (", te.TypecastType), te);
retstr += GenerateNode((SYMBOL) te.kids.Pop());
retstr += GenerateNode(te, (SYMBOL) te.kids.Pop());
retstr += Generate(")");
return retstr;
@@ -931,7 +974,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
}
foreach (SYMBOL kid in fc.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(fc, kid);
retstr += Generate(")");
@@ -980,11 +1023,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
string retstr = String.Empty;
retstr += Generate(String.Format("new {0}(", vc.Type), vc);
retstr += GenerateNode((SYMBOL) vc.kids.Pop());
retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop());
retstr += Generate(", ");
retstr += GenerateNode((SYMBOL) vc.kids.Pop());
retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop());
retstr += Generate(", ");
retstr += GenerateNode((SYMBOL) vc.kids.Pop());
retstr += GenerateNode(vc, (SYMBOL) vc.kids.Pop());
retstr += Generate(")");
return retstr;
@@ -1000,13 +1043,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
string retstr = String.Empty;
retstr += Generate(String.Format("new {0}(", rc.Type), rc);
retstr += GenerateNode((SYMBOL) rc.kids.Pop());
retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop());
retstr += Generate(", ");
retstr += GenerateNode((SYMBOL) rc.kids.Pop());
retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop());
retstr += Generate(", ");
retstr += GenerateNode((SYMBOL) rc.kids.Pop());
retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop());
retstr += Generate(", ");
retstr += GenerateNode((SYMBOL) rc.kids.Pop());
retstr += GenerateNode(rc, (SYMBOL) rc.kids.Pop());
retstr += Generate(")");
return retstr;
@@ -1024,7 +1067,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
retstr += Generate(String.Format("new {0}(", lc.Type), lc);
foreach (SYMBOL kid in lc.kids)
retstr += GenerateNode(kid);
retstr += GenerateNode(lc, kid);
retstr += Generate(")");

View File

@@ -31,6 +31,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
//using Microsoft.JScript;
@@ -72,6 +73,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
private bool CompileWithDebugInformation;
private Dictionary<string, bool> AllowedCompilers = new Dictionary<string, bool>(StringComparer.CurrentCultureIgnoreCase);
private Dictionary<string, enumCompileType> LanguageMapping = new Dictionary<string, enumCompileType>(StringComparer.CurrentCultureIgnoreCase);
private bool m_insertCoopTerminationCalls;
private string FilePrefix;
private string ScriptEnginesPath = null;
@@ -95,20 +97,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
private Dictionary<string, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>> m_lineMaps =
new Dictionary<string, Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>>();
public bool in_startup = true;
public Compiler(IScriptEngine scriptEngine)
{
m_scriptEngine = scriptEngine;;
m_scriptEngine = scriptEngine;
ScriptEnginesPath = scriptEngine.ScriptEnginePath;
ReadConfig();
}
public bool in_startup = true;
public void ReadConfig()
{
// Get some config
WriteScriptSourceToDebugFile = m_scriptEngine.Config.GetBoolean("WriteScriptSourceToDebugFile", false);
CompileWithDebugInformation = m_scriptEngine.Config.GetBoolean("CompileWithDebugInformation", true);
bool DeleteScriptsOnStartup = m_scriptEngine.Config.GetBoolean("DeleteScriptsOnStartup", true);
m_insertCoopTerminationCalls = m_scriptEngine.Config.GetString("ScriptStopStrategy", "abort") == "co-op";
// Get file prefix from scriptengine name and make it file system safe:
FilePrefix = "CommonCompiler";
@@ -386,7 +390,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
if (language == enumCompileType.lsl)
{
// Its LSL, convert it to C#
LSL_Converter = (ICodeConverter)new CSCodeGenerator(comms);
LSL_Converter = (ICodeConverter)new CSCodeGenerator(comms, m_insertCoopTerminationCalls);
compileScript = LSL_Converter.Convert(Script);
// copy converter warnings into our warnings.
@@ -411,16 +415,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
{
case enumCompileType.cs:
case enumCompileType.lsl:
compileScript = CreateCSCompilerScript(compileScript);
compileScript = CreateCSCompilerScript(
compileScript, m_scriptEngine.ScriptBaseClassName, m_scriptEngine.ScriptBaseClassParameters);
break;
case enumCompileType.vb:
compileScript = CreateVBCompilerScript(compileScript);
compileScript = CreateVBCompilerScript(compileScript, m_scriptEngine.ScriptBaseClassName);
break;
// case enumCompileType.js:
// compileScript = CreateJSCompilerScript(compileScript);
// compileScript = CreateJSCompilerScript(compileScript, m_scriptEngine.ScriptBaseClassName);
// break;
case enumCompileType.yp:
compileScript = CreateYPCompilerScript(compileScript);
compileScript = CreateYPCompilerScript(compileScript, m_scriptEngine.ScriptBaseClassName);
break;
}
@@ -451,43 +456,59 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
// return compileScript;
// }
private static string CreateCSCompilerScript(string compileScript)
private static string CreateCSCompilerScript(
string compileScript, string baseClassName, ParameterInfo[] constructorParameters)
{
compileScript = String.Empty +
"using OpenSim.Region.ScriptEngine.Shared; using System.Collections.Generic;\r\n" +
String.Empty + "namespace SecondLife { " +
String.Empty + "public class Script : OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass { \r\n" +
@"public Script() { } " +
compileScript +
"} }\r\n";
compileScript = string.Format(
@"using OpenSim.Region.ScriptEngine.Shared;
using System.Collections.Generic;
namespace SecondLife
{{
public class Script : {0}
{{
public Script({1}) : base({2}) {{}}
{3}
}}
}}",
baseClassName,
constructorParameters != null
? string.Join(", ", Array.ConvertAll<ParameterInfo, string>(constructorParameters, pi => pi.ToString()))
: "",
constructorParameters != null
? string.Join(", ", Array.ConvertAll<ParameterInfo, string>(constructorParameters, pi => pi.Name))
: "",
compileScript);
return compileScript;
}
private static string CreateYPCompilerScript(string compileScript)
private static string CreateYPCompilerScript(string compileScript, string baseClassName)
{
compileScript = String.Empty +
"using OpenSim.Region.ScriptEngine.Shared.YieldProlog; " +
"using OpenSim.Region.ScriptEngine.Shared; using System.Collections.Generic;\r\n" +
String.Empty + "namespace SecondLife { " +
String.Empty + "public class Script : OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass { \r\n" +
String.Empty + "public class Script : " + baseClassName + " { \r\n" +
//@"public Script() { } " +
@"static OpenSim.Region.ScriptEngine.Shared.YieldProlog.YP YP=null; " +
@"public Script() { YP= new OpenSim.Region.ScriptEngine.Shared.YieldProlog.YP(); } " +
compileScript +
"} }\r\n";
return compileScript;
}
private static string CreateVBCompilerScript(string compileScript)
private static string CreateVBCompilerScript(string compileScript, string baseClassName)
{
compileScript = String.Empty +
"Imports OpenSim.Region.ScriptEngine.Shared: Imports System.Collections.Generic: " +
String.Empty + "NameSpace SecondLife:" +
String.Empty + "Public Class Script: Inherits OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass: " +
String.Empty + "Public Class Script: Inherits " + baseClassName +
"\r\nPublic Sub New()\r\nEnd Sub: " +
compileScript +
":End Class :End Namespace\r\n";
return compileScript;
}
@@ -549,6 +570,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,
"OpenMetaverseTypes.dll"));
if (m_scriptEngine.ScriptReferencedAssemblies != null)
Array.ForEach<string>(
m_scriptEngine.ScriptReferencedAssemblies,
a => parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, a)));
if (lang == enumCompileType.yp)
{
parameters.ReferencedAssemblies.Add(Path.Combine(rootPath,