OpenSimMirror/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptInlines.cs

667 lines
28 KiB
C#

/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
/**
* @brief Generate code for the backend API calls.
*/
namespace OpenSim.Region.ScriptEngine.XMREngine
{
public abstract class TokenDeclInline : TokenDeclVar {
public static VarDict inlineFunctions = CreateDictionary ();
public abstract void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args);
private static string[] noCheckRuns;
private static string[] keyReturns;
protected bool isTaggedCallsCheckRun;
/**
* @brief Create a dictionary of inline backend API functions.
*/
private static VarDict CreateDictionary ()
{
/*
* For those listed in noCheckRun, we just generate the call (simple computations).
* For all others, we generate the call then a call to CheckRun().
*/
noCheckRuns = new string[] {
"llBase64ToString",
"llCSV2List",
"llDeleteSubList",
"llDeleteSubString",
"llDumpList2String",
"llEscapeURL",
"llEuler2Rot",
"llGetListEntryType",
"llGetListLength",
"llGetSubString",
"llGetUnixTime",
"llInsertString",
"llList2CSV",
"llList2Float",
"llList2Integer",
"llList2Key",
"llList2List",
"llList2ListStrided",
"llList2Rot",
"llList2String",
"llList2Vector",
"llListFindList",
"llListInsertList",
"llListRandomize",
"llListReplaceList",
"llListSort",
"llListStatistics",
"llMD5String",
"llParseString2List",
"llParseStringKeepNulls",
"llRot2Euler",
"llStringLength",
"llStringToBase64",
"llStringTrim",
"llSubStringIndex",
"llUnescapeURL"
};
/*
* These functions really return a 'key' even though we see them as
* returning 'string' because OpenSim has key and string as same type.
*/
keyReturns = new string[] {
"llAvatarOnLinkSitTarget",
"llAvatarOnSitTarget",
"llDetectedKey",
"llDetectedOwner",
"llGenerateKey",
"llGetCreator",
"llGetInventoryCreator",
"llGetInventoryKey",
"llGetKey",
"llGetLandOwnerAt",
"llGetLinkKey",
"llGetNotecardLine",
"llGetNumberOfNotecardLines",
"llGetOwner",
"llGetOwnerKey",
"llGetPermissionsKey",
"llHTTPRequest",
"llList2Key",
"llRequestAgentData",
"llRequestDisplayName",
"llRequestInventoryData",
"llRequestSecureURL",
"llRequestSimulatorData",
"llRequestURL",
"llRequestUsername",
"llSendRemoteData",
"llTransferLindenDollars"
};
VarDict ifd = new VarDict (false);
Type[] oneDoub = new Type[] { typeof (double) };
Type[] twoDoubs = new Type[] { typeof (double), typeof (double) };
/*
* Mono generates an FPU instruction for many math calls.
*/
new TokenDeclInline_LLAbs (ifd);
new TokenDeclInline_Math (ifd, "llAcos(float)", "Acos", oneDoub);
new TokenDeclInline_Math (ifd, "llAsin(float)", "Asin", oneDoub);
new TokenDeclInline_Math (ifd, "llAtan2(float,float)", "Atan2", twoDoubs);
new TokenDeclInline_Math (ifd, "llCos(float)", "Cos", oneDoub);
new TokenDeclInline_Math (ifd, "llFabs(float)", "Abs", oneDoub);
new TokenDeclInline_Math (ifd, "llLog(float)", "Log", oneDoub);
new TokenDeclInline_Math (ifd, "llLog10(float)", "Log10", oneDoub);
new TokenDeclInline_Math (ifd, "llPow(float,float)", "Pow", twoDoubs);
new TokenDeclInline_LLRound (ifd);
new TokenDeclInline_Math (ifd, "llSin(float)", "Sin", oneDoub);
new TokenDeclInline_Math (ifd, "llSqrt(float)", "Sqrt", oneDoub);
new TokenDeclInline_Math (ifd, "llTan(float)", "Tan", oneDoub);
/*
* Something weird about the code generation for these calls, so they all have their own handwritten code generators.
*/
new TokenDeclInline_GetFreeMemory (ifd);
new TokenDeclInline_GetUsedMemory (ifd);
/*
* These are all the xmr...() calls directly in XMRInstAbstract.
* Includes the calls from ScriptBaseClass that has all the stubs
* which convert XMRInstAbstract to the various <NAME>_Api contexts.
*/
MethodInfo[] absmeths = typeof (XMRInstAbstract).GetMethods ();
AddInterfaceMethods (ifd, absmeths, null);
return ifd;
}
/**
* @brief Add API functions from the given interface to list of built-in functions.
* Only functions beginning with a lower-case letter are entered, all others ignored.
* @param ifd = internal function dictionary to add them to
* @param ifaceMethods = list of API functions
* @param acf = which field in XMRInstanceSuperType holds method's 'this' pointer
*/
// this one accepts only names beginning with a lower-case letter
public static void AddInterfaceMethods (VarDict ifd, MethodInfo[] ifaceMethods, FieldInfo acf)
{
List<MethodInfo> lcms = new List<MethodInfo> (ifaceMethods.Length);
foreach (MethodInfo meth in ifaceMethods)
{
string name = meth.Name;
if ((name[0] >= 'a') && (name[0] <= 'z')) {
lcms.Add (meth);
}
}
AddInterfaceMethods (ifd, lcms.GetEnumerator (), acf);
}
// this one accepts all methods given to it
public static void AddInterfaceMethods (VarDict ifd, IEnumerator<MethodInfo> ifaceMethods, FieldInfo acf)
{
if (ifd == null) ifd = inlineFunctions;
for (ifaceMethods.Reset (); ifaceMethods.MoveNext ();) {
MethodInfo ifaceMethod = ifaceMethods.Current;
string key = ifaceMethod.Name;
try {
/*
* See if we will generate a call to CheckRun() right
* after we generate a call to the function.
* If function begins with xmr, assume we will not call CheckRun()
* Otherwise, assume we will call CheckRun()
*/
bool dcr = !key.StartsWith ("xmr");
foreach (string ncr in noCheckRuns) {
if (ncr == key) {
dcr = false;
break;
}
}
/*
* Add function to dictionary.
*/
new TokenDeclInline_BEApi (ifd, dcr, ifaceMethod, acf);
} catch {
///??? IGNORE ANY THAT FAIL - LIKE UNRECOGNIZED TYPE ???///
///??? and OVERLOADED NAMES ???///
}
}
}
/**
* @brief Add an inline function definition to the dictionary.
* @param ifd = dictionary to add inline definition to
* @param doCheckRun = true iff the generated code or the function itself can possibly call CheckRun()
* @param nameArgSig = inline function signature string, in form <name>(<arglsltypes>,...)
* @param retType = return type, use TokenTypeVoid if no return value
*/
protected TokenDeclInline (VarDict ifd,
bool doCheckRun,
string nameArgSig,
TokenType retType)
: base (null, null, null)
{
this.retType = retType;
this.triviality = doCheckRun ? Triviality.complex : Triviality.trivial;
int j = nameArgSig.IndexOf ('(');
this.name = new TokenName (null, nameArgSig.Substring (0, j ++));
this.argDecl = new TokenArgDecl (null);
if (nameArgSig[j] != ')') {
int i;
TokenName name;
TokenType type;
for (i = j; nameArgSig[i] != ')'; i ++) {
if (nameArgSig[i] == ',') {
type = TokenType.FromLSLType (null, nameArgSig.Substring (j, i - j));
name = new TokenName (null, "arg" + this.argDecl.varDict.Count);
this.argDecl.AddArg (type, name);
j = i + 1;
}
}
type = TokenType.FromLSLType (null, nameArgSig.Substring (j, i - j));
name = new TokenName (null, "arg" + this.argDecl.varDict.Count);
this.argDecl.AddArg (type, name);
}
this.location = new CompValuInline (this);
if (ifd == null) ifd = inlineFunctions;
ifd.AddEntry (this);
}
protected TokenDeclInline (VarDict ifd,
bool doCheckRun,
MethodInfo methInfo)
: base (null, null, null)
{
TokenType retType = TokenType.FromSysType (null, methInfo.ReturnType);
this.isTaggedCallsCheckRun = IsTaggedCallsCheckRun (methInfo);
this.name = new TokenName (null, methInfo.Name);
this.retType = GetRetType (methInfo, retType);
this.argDecl = GetArgDecl (methInfo.GetParameters ());
this.triviality = (doCheckRun || this.isTaggedCallsCheckRun) ? Triviality.complex : Triviality.trivial;
this.location = new CompValuInline (this);
if (ifd == null) ifd = inlineFunctions;
ifd.AddEntry (this);
}
private static TokenArgDecl GetArgDecl (ParameterInfo[] parameters)
{
TokenArgDecl argDecl = new TokenArgDecl (null);
foreach (ParameterInfo pi in parameters) {
TokenType type = TokenType.FromSysType (null, pi.ParameterType);
TokenName name = new TokenName (null, pi.Name);
argDecl.AddArg (type, name);
}
return argDecl;
}
/**
* @brief The above code assumes all methods beginning with 'xmr' are trivial, ie,
* they do not call CheckRun() and also we do not generate a CheckRun()
* call after they return. So if an 'xmr' method does call CheckRun(), it
* must be tagged with attribute 'xmrMethodCallsCheckRunAttribute' so we know
* the method is not trivial. But in neither case do we emit our own call
* to CheckRun(), the 'xmr' method must do its own. We do however set up a
* call label before the call to the non-trivial 'xmr' method so when we are
* restoring the call stack, the restore will call directly in to the 'xmr'
* method without re-executing any code before the call to the 'xmr' method.
*/
private static bool IsTaggedCallsCheckRun (MethodInfo methInfo)
{
return (methInfo != null) &&
Attribute.IsDefined (methInfo, typeof (xmrMethodCallsCheckRunAttribute));
}
/**
* @brief The dumbass OpenSim has key and string as the same type so non-ll
* methods must be tagged with xmrMethodReturnsKeyAttribute if we
* are to think they return a key type, otherwise we will think they
* return string.
*/
private static TokenType GetRetType (MethodInfo methInfo, TokenType retType)
{
if ((methInfo != null) && (retType != null) && (retType is TokenTypeStr)) {
if (Attribute.IsDefined (methInfo, typeof (xmrMethodReturnsKeyAttribute))) {
return ChangeToKeyType (retType);
}
string mn = methInfo.Name;
foreach (string kr in keyReturns) {
if (kr == mn) return ChangeToKeyType (retType);
}
}
return retType;
}
private static TokenType ChangeToKeyType (TokenType retType)
{
if (retType is TokenTypeLSLString) {
retType = new TokenTypeLSLKey (null);
} else {
retType = new TokenTypeKey (null);
}
return retType;
}
public virtual MethodInfo GetMethodInfo ()
{
return null;
}
/**
* @brief Print out a list of all the built-in functions and constants.
*/
public delegate void WriteLine (string str);
public static void PrintBuiltins (bool inclNoisyTag, WriteLine writeLine)
{
writeLine ("\nBuilt-in functions:\n");
SortedDictionary<string, TokenDeclInline> bifs = new SortedDictionary<string, TokenDeclInline> ();
foreach (TokenDeclVar bif in TokenDeclInline.inlineFunctions) {
bifs.Add (bif.fullName, (TokenDeclInline)bif);
}
foreach (TokenDeclInline bif in bifs.Values) {
char noisy = (!inclNoisyTag || !IsTaggedNoisy (bif.GetMethodInfo ())) ? ' ' : (bif.retType is TokenTypeVoid) ? 'N' : 'R';
writeLine (noisy + " " + bif.retType.ToString ().PadLeft (8) + " " + bif.fullName);
}
if (inclNoisyTag) {
writeLine ("\nN - stub that writes name and arguments to stdout");
writeLine ("R - stub that writes name and arguments to stdout then reads return value from stdin");
writeLine (" format is: function_name : return_value");
writeLine (" example: llKey2Name:\"Kunta Kinte\"");
}
writeLine ("\nBuilt-in constants:\n");
SortedDictionary<string, ScriptConst> scs = new SortedDictionary<string, ScriptConst> ();
int widest = 0;
foreach (ScriptConst sc in ScriptConst.scriptConstants.Values) {
if (widest < sc.name.Length) widest = sc.name.Length;
scs.Add (sc.name, sc);
}
foreach (ScriptConst sc in scs.Values) {
writeLine (" " + sc.rVal.type.ToString ().PadLeft (8) + " " + sc.name.PadRight (widest) + " = " + BuiltInConstVal (sc.rVal));
}
}
public static bool IsTaggedNoisy (MethodInfo methInfo)
{
return (methInfo != null) && Attribute.IsDefined (methInfo, typeof (xmrMethodIsNoisyAttribute));
}
public static string BuiltInConstVal (CompValu rVal)
{
if (rVal is CompValuInteger) {
int x = ((CompValuInteger)rVal).x;
return "0x" + x.ToString ("X8") + " = " + x.ToString ().PadLeft (11);
}
if (rVal is CompValuFloat) return ((CompValuFloat)rVal).x.ToString ();
if (rVal is CompValuString) {
StringBuilder sb = new StringBuilder ();
PrintParam (sb, ((CompValuString)rVal).x);
return sb.ToString ();
}
if (rVal is CompValuSField) {
FieldInfo fi = ((CompValuSField)rVal).field;
StringBuilder sb = new StringBuilder ();
PrintParam (sb, fi.GetValue (null));
return sb.ToString ();
}
return rVal.ToString (); // just prints the type
}
public static void PrintParam (StringBuilder sb, object p)
{
if (p == null) {
sb.Append ("null");
} else if (p is LSL_List) {
sb.Append ('[');
object[] d = ((LSL_List)p).Data;
for (int i = 0; i < d.Length; i ++) {
if (i > 0) sb.Append (',');
PrintParam (sb, d[i]);
}
sb.Append (']');
} else if (p is LSL_Rotation) {
LSL_Rotation r = (LSL_Rotation)p;
sb.Append ('<');
sb.Append (r.x);
sb.Append (',');
sb.Append (r.y);
sb.Append (',');
sb.Append (r.z);
sb.Append (',');
sb.Append (r.s);
sb.Append ('>');
} else if (p is LSL_String) {
PrintParamString (sb, (string)(LSL_String)p);
} else if (p is LSL_Vector) {
LSL_Vector v = (LSL_Vector)p;
sb.Append ('<');
sb.Append (v.x);
sb.Append (',');
sb.Append (v.y);
sb.Append (',');
sb.Append (v.z);
sb.Append ('>');
} else if (p is string) {
PrintParamString (sb, (string)p);
} else {
sb.Append (p.ToString ());
}
}
public static void PrintParamString (StringBuilder sb, string p)
{
sb.Append ('"');
foreach (char c in p) {
if (c == '\b') {
sb.Append ("\\b");
continue;
}
if (c == '\n') {
sb.Append ("\\n");
continue;
}
if (c == '\r') {
sb.Append ("\\r");
continue;
}
if (c == '\t') {
sb.Append ("\\t");
continue;
}
if (c == '"') {
sb.Append ("\\\"");
continue;
}
if (c == '\\') {
sb.Append ("\\\\");
continue;
}
sb.Append (c);
}
sb.Append ('"');
}
}
/**
* @brief Code generators...
* @param scg = script we are generating code for
* @param result = type/location for result (type matches function definition)
* @param args = type/location of arguments (types match function definition)
*/
public class TokenDeclInline_LLAbs : TokenDeclInline {
public TokenDeclInline_LLAbs (VarDict ifd)
: base (ifd, false, "llAbs(integer)", new TokenTypeInt (null)) { }
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
{
ScriptMyLabel itsPosLabel = scg.ilGen.DefineLabel ("llAbstemp");
args[0].PushVal (scg, errorAt);
scg.ilGen.Emit (errorAt, OpCodes.Dup);
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4_0);
scg.ilGen.Emit (errorAt, OpCodes.Bge_S, itsPosLabel);
scg.ilGen.Emit (errorAt, OpCodes.Neg);
scg.ilGen.MarkLabel (itsPosLabel);
result.Pop (scg, errorAt, retType);
}
}
public class TokenDeclInline_Math : TokenDeclInline {
private MethodInfo methInfo;
public TokenDeclInline_Math (VarDict ifd, string sig, string name, Type[] args)
: base (ifd, false, sig, new TokenTypeFloat (null))
{
methInfo = ScriptCodeGen.GetStaticMethod (typeof (System.Math), name, args);
}
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
{
for (int i = 0; i < args.Length; i ++) {
args[i].PushVal (scg, errorAt, argDecl.types[i]);
}
scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
result.Pop (scg, errorAt, retType);
}
}
public class TokenDeclInline_LLRound : TokenDeclInline {
private static MethodInfo roundMethInfo = ScriptCodeGen.GetStaticMethod (typeof (System.Math), "Round",
new Type[] { typeof (double), typeof (MidpointRounding) });
public TokenDeclInline_LLRound (VarDict ifd)
: base (ifd, false, "llRound(float)", new TokenTypeInt (null)) { }
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
{
args[0].PushVal (scg, errorAt, new TokenTypeFloat (null));
scg.ilGen.Emit (errorAt, OpCodes.Ldc_I4, (int)System.MidpointRounding.AwayFromZero);
scg.ilGen.Emit (errorAt, OpCodes.Call, roundMethInfo);
result.Pop (scg, errorAt, new TokenTypeFloat (null));
}
}
public class TokenDeclInline_GetFreeMemory : TokenDeclInline {
private static readonly MethodInfo getFreeMemMethInfo = typeof (XMRInstAbstract).GetMethod ("xmrHeapLeft", new Type[] { });
public TokenDeclInline_GetFreeMemory (VarDict ifd)
: base (ifd, false, "llGetFreeMemory()", new TokenTypeInt (null)) { }
// appears as llGetFreeMemory() in script source code
// but actually calls xmrHeapLeft()
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
{
scg.PushXMRInst ();
scg.ilGen.Emit (errorAt, OpCodes.Call, getFreeMemMethInfo);
result.Pop (scg, errorAt, new TokenTypeInt (null));
}
}
public class TokenDeclInline_GetUsedMemory : TokenDeclInline {
private static readonly MethodInfo getUsedMemMethInfo = typeof (XMRInstAbstract).GetMethod ("xmrHeapUsed", new Type[] { });
public TokenDeclInline_GetUsedMemory (VarDict ifd)
: base (ifd, false, "llGetUsedMemory()", new TokenTypeInt (null)) { }
// appears as llGetUsedMemory() in script source code
// but actually calls xmrHeapUsed()
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
{
scg.PushXMRInst ();
scg.ilGen.Emit (errorAt, OpCodes.Call, getUsedMemMethInfo);
result.Pop (scg, errorAt, new TokenTypeInt (null));
}
}
/**
* @brief Generate code for the usual ll...() functions.
*/
public class TokenDeclInline_BEApi : TokenDeclInline {
// private static readonly MethodInfo fixLLParcelMediaQuery = ScriptCodeGen.GetStaticMethod
// (typeof (XMRInstAbstract), "FixLLParcelMediaQuery", new Type[] { typeof (LSL_List) });
// private static readonly MethodInfo fixLLParcelMediaCommandList = ScriptCodeGen.GetStaticMethod
// (typeof (XMRInstAbstract), "FixLLParcelMediaCommandList", new Type[] { typeof (LSL_List) });
public bool doCheckRun;
private FieldInfo apiContextField;
private MethodInfo methInfo;
/**
* @brief Constructor
* @param ifd = dictionary to add the function to
* @param dcr = append a call to CheckRun()
* @param methInfo = ll...() method to be called
*/
public TokenDeclInline_BEApi (VarDict ifd, bool dcr, MethodInfo methInfo, FieldInfo acf)
: base (ifd, dcr, methInfo)
{
this.methInfo = methInfo;
doCheckRun = dcr;
apiContextField = acf;
}
public override MethodInfo GetMethodInfo ()
{
return methInfo;
}
/**
* @brief Generate call to backend API function (eg llSay()) maybe followed by a call to CheckRun().
* @param scg = script being compiled
* @param result = where to place result (might be void)
* @param args = script-visible arguments to pass to API function
*/
public override void CodeGen (ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
{
if (isTaggedCallsCheckRun)
{ // see if 'xmr' method that calls CheckRun() internally
new ScriptCodeGen.CallLabel (scg, errorAt); // if so, put a call label immediately before it
// .. so restoring the frame will jump immediately to the
// .. call without re-executing any code before this
}
if (!methInfo.IsStatic)
{
scg.PushXMRInst (); // XMRInstanceSuperType pointer
if (apiContextField != null) // 'this' pointer for API function
scg.ilGen.Emit (errorAt, OpCodes.Ldfld, apiContextField);
}
for (int i = 0; i < args.Length; i ++) // push arguments, boxing/unboxing as needed
args[i].PushVal (scg, errorAt, argDecl.types[i]);
// this should not be needed
// if (methInfo.Name == "llParcelMediaQuery") {
// scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaQuery);
// }
// this should not be needed
// if (methInfo.Name == "llParcelMediaCommandList") {
// scg.ilGen.Emit (errorAt, OpCodes.Call, fixLLParcelMediaCommandList);
// }
if (methInfo.IsVirtual) // call API function
scg.ilGen.Emit (errorAt, OpCodes.Callvirt, methInfo);
else
scg.ilGen.Emit (errorAt, OpCodes.Call, methInfo);
result.Pop (scg, errorAt, retType); // pop result, boxing/unboxing as needed
if (isTaggedCallsCheckRun)
scg.openCallLabel = null;
if (doCheckRun)
scg.EmitCallCheckRun (errorAt, false); // maybe call CheckRun()
}
}
}