Files
CMLeonOS/interpreter/UniLua/LuaAPI.cs

1639 lines
36 KiB
C#
Raw Normal View History

2026-02-03 02:44:58 +08:00
using System;
// #define DEBUG_RECORD_INS
namespace UniLua
{
public interface ILoadInfo
{
int ReadByte();
int PeekByte();
}
public delegate int CSharpFunctionDelegate(ILuaState state);
public interface ILuaAPI
{
LuaState NewThread();
ThreadStatus Load( ILoadInfo loadinfo, string name, string mode );
DumpStatus Dump( LuaWriter writeFunc );
ThreadStatus GetContext( out int context );
void Call( int numArgs, int numResults );
void CallK( int numArgs, int numResults,
int context, CSharpFunctionDelegate continueFunc );
ThreadStatus PCall( int numArgs, int numResults, int errFunc);
ThreadStatus PCallK( int numArgs, int numResults, int errFunc,
int context, CSharpFunctionDelegate continueFunc );
ThreadStatus Resume( ILuaState from, int numArgs );
int Yield( int numResults );
int YieldK( int numResults,
int context, CSharpFunctionDelegate continueFunc );
int AbsIndex( int index );
int GetTop();
void SetTop( int top );
void Remove( int index );
void Insert( int index );
void Replace( int index );
void Copy( int fromIndex, int toIndex );
void XMove( ILuaState to, int n );
bool CheckStack( int size );
bool GetStack( int level, LuaDebug ar );
int Error();
int UpvalueIndex( int i );
string GetUpvalue( int funcIndex, int n );
string SetUpvalue( int funcIndex, int n );
void CreateTable( int narray, int nrec );
void NewTable();
bool Next( int index );
void RawGetI( int index, int n );
void RawSetI( int index, int n );
void RawGet( int index );
void RawSet( int index );
void GetField( int index, string key );
void SetField( int index, string key );
void GetTable( int index );
void SetTable( int index );
void Concat( int n );
LuaType Type( int index );
string TypeName( LuaType t );
bool IsNil( int index );
bool IsNone( int index );
bool IsNoneOrNil( int index );
bool IsString( int index );
bool IsTable( int index );
bool IsFunction( int index );
bool Compare( int index1, int index2, LuaEq op );
bool RawEqual( int index1, int index2 );
int RawLen( int index );
void Len( int index );
void PushNil();
void PushBoolean( bool b );
void PushNumber( double n );
void PushInteger( int n );
void PushUnsigned( uint n );
string PushString( string s );
void PushCSharpFunction( CSharpFunctionDelegate f );
void PushCSharpClosure( CSharpFunctionDelegate f, int n );
void PushValue( int index );
void PushGlobalTable();
void PushLightUserData( object o );
void PushUInt64( UInt64 o );
bool PushThread();
void Pop( int n );
bool GetMetaTable( int index );
bool SetMetaTable( int index );
void GetGlobal( string name );
void SetGlobal( string name );
string ToString( int index );
double ToNumberX( int index, out bool isnum );
double ToNumber( int index );
int ToIntegerX( int index, out bool isnum );
int ToInteger( int index );
uint ToUnsignedX( int index, out bool isnum );
uint ToUnsigned( int index );
bool ToBoolean( int index );
UInt64 ToUInt64( int index );
UInt64 ToUInt64X( int index, out bool isnum );
object ToObject( int index );
object ToUserData( int index );
ILuaState ToThread( int index );
ThreadStatus Status { get; }
string DebugGetInstructionHistory();
}
public interface ILuaState : ILuaAPI, ILuaAuxLib
{
}
public static class LuaAPI
{
public static ILuaState NewState()
{
return new LuaState();
}
}
internal delegate void PFuncDelegate<T>(ref T ud);
public partial class LuaState : ILuaState
{
LuaState ILuaAPI.NewThread()
{
LuaState newLua = new LuaState(G);
Top.V.SetThValue(newLua);
ApiIncrTop();
newLua.HookMask = HookMask;
newLua.BaseHookCount = BaseHookCount;
newLua.Hook = Hook;
newLua.ResetHookCount();
return newLua;
}
// private void F_LoadBinary( object ud )
// {
// byte[] bytes = (byte[])ud;
// var reader = new BinaryBytesReader( bytes );
// Undump u = new Undump( reader );
// LuaProto proto = Undump.LoadBinary( this, loadinfo, "" );
// var cl = new LuaLClosure( proto );
// for( int i=0; i<proto.Upvalues.Count; ++i )
// {
// cl.Upvals[i] = new LuaUpvalue();
// }
// Top.Value = cl;
// IncrTop();
// }
// ThreadStatus ILuaAPI.LoadBinary( byte[] bytes )
// {
// var status = D_PCall( F_LoadBinary, bytes, Top, ErrFunc );
// if( status == ThreadStatus.LUA_OK )
// {
// var cl = (Top-1).Value as LuaLClosure;
// if( cl.Upvals.Count == 1 )
// {
// cl.Upvals[0].V.Value = G.Registry.GetInt( LuaDef.LUA_RIDX_GLOBALS );
// }
// }
// return status;
// }
struct LoadParameter
{
public LuaState L;
public ILoadInfo LoadInfo;
public string Name;
public string Mode;
public LoadParameter(LuaState l, ILoadInfo loadinfo, string name, string mode)
{
L = l;
LoadInfo = loadinfo;
Name = name;
Mode = mode;
}
}
private void CheckMode( string given, string expected )
{
if( given != null && given.IndexOf(expected[0]) == -1 )
{
O_PushString( string.Format(
"attempt to load a {0} chunk (mode is '{1}')",
expected, given ) );
D_Throw( ThreadStatus.LUA_ERRSYNTAX );
}
}
private static void F_Load(ref LoadParameter param)
{
var L = param.L;
LuaProto proto;
var c = param.LoadInfo.PeekByte();
if( c == LuaConf.LUA_SIGNATURE[0] )
{
L.CheckMode( param.Mode, "binary" );
proto = Undump.LoadBinary(L, param.LoadInfo, param.Name);
}
else
{
L.CheckMode( param.Mode, "text" );
proto = Parser.Parse(L, param.LoadInfo, param.Name);
}
var cl = new LuaLClosureValue( proto );
Utl.Assert(cl.Upvals.Length == cl.Proto.Upvalues.Count);
L.Top.V.SetClLValue(cl);
L.IncrTop();
}
private static PFuncDelegate<LoadParameter> DG_F_Load = F_Load;
ThreadStatus ILuaAPI.Load( ILoadInfo loadinfo, string name, string mode )
{
var param = new LoadParameter(this, loadinfo, name, mode);
var status = D_PCall( DG_F_Load, ref param, Top.Index, ErrFunc );
if( status == ThreadStatus.LUA_OK ) {
var below = Stack[Top.Index-1];
Utl.Assert(below.V.TtIsFunction() && below.V.ClIsLuaClosure());
var cl = below.V.ClLValue();
if(cl.Upvals.Length == 1) {
var gt = G.Registry.V.HValue().GetInt(LuaDef.LUA_RIDX_GLOBALS);
cl.Upvals[0].V.V.SetObj(ref gt.V);
}
}
return status;
}
DumpStatus ILuaAPI.Dump( LuaWriter writeFunc )
{
Utl.ApiCheckNumElems( this, 1 );
var below = Stack[Top.Index-1];
if(!below.V.TtIsFunction() || !below.V.ClIsLuaClosure())
return DumpStatus.ERROR;
var o = below.V.ClLValue();
if(o == null)
return DumpStatus.ERROR;
return DumpState.Dump(o.Proto, writeFunc, false);
}
ThreadStatus ILuaAPI.GetContext( out int context )
{
if( (CI.CallStatus & CallStatus.CIST_YIELDED) != 0 )
{
context = CI.Context;
return CI.Status;
}
else
{
context = default(int);
return ThreadStatus.LUA_OK;
}
}
void ILuaAPI.Call( int numArgs, int numResults )
{
// StkId func = Top - (numArgs + 1);
// if( !D_PreCall( func, numResults ) )
// {
// V_Execute();
// }
API.CallK( numArgs, numResults, 0, null );
}
void ILuaAPI.CallK( int numArgs, int numResults,
int context, CSharpFunctionDelegate continueFunc )
{
Utl.ApiCheck( continueFunc == null || !CI.IsLua,
"cannot use continuations inside hooks" );
Utl.ApiCheckNumElems( this, numArgs + 1 );
Utl.ApiCheck( Status == ThreadStatus.LUA_OK,
"cannot do calls on non-normal thread" );
CheckResults( numArgs, numResults );
var func = Stack[Top.Index - (numArgs+1)];
// need to prepare continuation?
if( continueFunc != null && NumNonYieldable == 0 )
{
CI.ContinueFunc = continueFunc;
CI.Context = context;
D_Call( func, numResults, true );
}
// no continuation or no yieldable
else
{
D_Call( func, numResults, false );
}
AdjustResults( numResults );
}
private struct CallS
{
public LuaState L;
public int FuncIndex;
public int NumResults;
}
private static void F_Call(ref CallS ud)
{
CallS c = (CallS)ud;
c.L.D_Call(c.L.Stack[c.FuncIndex], c.NumResults, false);
}
private static PFuncDelegate<CallS> DG_F_Call = F_Call;
private void CheckResults( int numArgs, int numResults )
{
Utl.ApiCheck( numResults == LuaDef.LUA_MULTRET ||
CI.TopIndex - Top.Index >= numResults - numArgs,
"results from function overflow current stack size" );
}
private void AdjustResults( int numResults )
{
if( numResults == LuaDef.LUA_MULTRET &&
CI.TopIndex < Top.Index )
{
CI.TopIndex = Top.Index;
}
}
ThreadStatus ILuaAPI.PCall( int numArgs, int numResults, int errFunc)
{
return API.PCallK( numArgs, numResults, errFunc, 0, null );
}
ThreadStatus ILuaAPI.PCallK( int numArgs, int numResults, int errFunc,
int context, CSharpFunctionDelegate continueFunc )
{
Utl.ApiCheck( continueFunc == null || !CI.IsLua,
"cannot use continuations inside hooks" );
Utl.ApiCheckNumElems( this, numArgs + 1 );
Utl.ApiCheck( Status == ThreadStatus.LUA_OK,
"cannot do calls on non-normal thread" );
CheckResults( numArgs, numResults );
int func;
if( errFunc == 0 )
func = 0;
else
{
StkId addr;
if( !Index2Addr( errFunc, out addr ) )
Utl.InvalidIndex();
func = addr.Index;
}
ThreadStatus status;
CallS c = new CallS();
c.L = this;
c.FuncIndex = Top.Index - (numArgs + 1);
if( continueFunc == null || NumNonYieldable > 0 ) // no continuation or no yieldable?
{
c.NumResults = numResults;
status = D_PCall( DG_F_Call, ref c, c.FuncIndex, func );
}
else
{
int ciIndex = CI.Index;
CI.ContinueFunc = continueFunc;
CI.Context = context;
CI.ExtraIndex = c.FuncIndex;
CI.OldAllowHook = AllowHook;
CI.OldErrFunc = ErrFunc;
ErrFunc = func;
CI.CallStatus |= CallStatus.CIST_YPCALL;
D_Call( Stack[c.FuncIndex], numResults, true );
CallInfo ci = BaseCI[ciIndex];
ci.CallStatus &= ~CallStatus.CIST_YPCALL;
ErrFunc = ci.OldErrFunc;
status = ThreadStatus.LUA_OK;
}
AdjustResults( numResults );
return status;
}
private void FinishCSharpCall()
{
CallInfo ci = CI;
Utl.Assert( ci.ContinueFunc != null ); // must have a continuation
Utl.Assert( NumNonYieldable == 0 );
// finish `CallK'
AdjustResults( ci.NumResults );
// call continuation function
if( (ci.CallStatus & CallStatus.CIST_STAT) == 0 ) // no call status?
{
ci.Status = ThreadStatus.LUA_YIELD; // `default' status
}
Utl.Assert( ci.Status != ThreadStatus.LUA_OK );
ci.CallStatus = ( ci.CallStatus
& ~( CallStatus.CIST_YPCALL | CallStatus.CIST_STAT))
| CallStatus.CIST_YIELDED;
int n = ci.ContinueFunc( this ); // call
Utl.ApiCheckNumElems( this, n );
// finish `D_PreCall'
D_PosCall( Top.Index-n );
}
private void Unroll()
{
while( true )
{
if( CI.Index == 0 ) // stack is empty?
return; // coroutine finished normally
if( !CI.IsLua ) // C# function?
FinishCSharpCall();
else // Lua function
{
V_FinishOp(); // finish interrupted instruction
V_Execute(); // execute down to higher C# `boundary'
}
}
}
struct UnrollParam
{
public LuaState L;
}
private static void UnrollWrap(ref UnrollParam param)
{
param.L.Unroll();
}
private static PFuncDelegate<UnrollParam> DG_Unroll = UnrollWrap;
private void ResumeError( string msg, int firstArg )
{
Top = Stack[firstArg];
Top.V.SetSValue(msg);
IncrTop();
D_Throw( ThreadStatus.LUA_RESUME_ERROR );
}
// check whether thread has suspended protected call
private CallInfo FindPCall()
{
for(int i=CI.Index; i>=0; --i) {
var ci = BaseCI[i];
if( (ci.CallStatus & CallStatus.CIST_YPCALL) != 0 )
{ return ci; }
}
return null; // no pending pcall
}
private bool Recover( ThreadStatus status )
{
CallInfo ci = FindPCall();
if( ci == null ) // no recover point
return false;
StkId oldTop = Stack[ci.ExtraIndex];
F_Close( oldTop );
SetErrorObj( status, oldTop );
CI = ci;
AllowHook = ci.OldAllowHook;
NumNonYieldable = 0;
ErrFunc = ci.OldErrFunc;
ci.CallStatus |= CallStatus.CIST_STAT;
ci.Status = status;
return true;
}
// do the work for `lua_resume' in protected mode
private void Resume(int firstArg)
{
int numCSharpCalls = NumCSharpCalls;
CallInfo ci = CI;
if( numCSharpCalls >= LuaLimits.LUAI_MAXCCALLS )
ResumeError( "C stack overflow", firstArg );
if( Status == ThreadStatus.LUA_OK ) // may be starting a coroutine
{
if( ci.Index > 0 ) // not in base level
{
ResumeError( "cannot resume non-suspended coroutine", firstArg );
}
if( !D_PreCall( Stack[firstArg-1], LuaDef.LUA_MULTRET ) ) // Lua function?
{
V_Execute(); // call it
}
}
else if( Status != ThreadStatus.LUA_YIELD )
{
ResumeError( "cannot resume dead coroutine", firstArg );
}
else // resume from previous yield
{
Status = ThreadStatus.LUA_OK;
ci.FuncIndex = ci.ExtraIndex;
if( ci.IsLua ) // yielded inside a hook?
{
V_Execute(); // just continue running Lua code
}
else // `common' yield
{
if( ci.ContinueFunc != null )
{
ci.Status = ThreadStatus.LUA_YIELD; // `default' status
ci.CallStatus |= CallStatus.CIST_YIELDED;
int n = ci.ContinueFunc( this ); // call continuation
Utl.ApiCheckNumElems( this, n );
firstArg = Top.Index - n; // yield results come from continuation
}
D_PosCall(firstArg);
}
Unroll();
}
Utl.Assert( numCSharpCalls == NumCSharpCalls );
}
struct ResumeParam
{
public LuaState L;
public int firstArg;
}
private static void ResumeWrap(ref ResumeParam param)
{
param.L.Resume(param.firstArg);
}
private static PFuncDelegate<ResumeParam> DG_Resume = ResumeWrap;
ThreadStatus ILuaAPI.Resume( ILuaState from, int numArgs )
{
LuaState fromState = from as LuaState;
NumCSharpCalls = (fromState != null) ? fromState.NumCSharpCalls + 1 : 1;
NumNonYieldable = 0; // allow yields
Utl.ApiCheckNumElems( this, (Status == ThreadStatus.LUA_OK) ? numArgs + 1 : numArgs );
var resumeParam = new ResumeParam();
resumeParam.L = this;
resumeParam.firstArg = Top.Index-numArgs;
ThreadStatus status = D_RawRunProtected( DG_Resume, ref resumeParam );
if( status == ThreadStatus.LUA_RESUME_ERROR ) // error calling `lua_resume'?
{
status = ThreadStatus.LUA_ERRRUN;
}
else // yield or regular error
{
while( status != ThreadStatus.LUA_OK && status != ThreadStatus.LUA_YIELD ) // error?
{
if( Recover( status ) ) // recover point?
{
var unrollParam = new UnrollParam();
unrollParam.L = this;
status = D_RawRunProtected( DG_Unroll, ref unrollParam );
}
else // unrecoverable error
{
Status = status; // mark thread as `dead'
SetErrorObj( status, Top );
CI.TopIndex = Top.Index;
break;
}
}
Utl.Assert( status == Status );
}
NumNonYieldable = 1; // do not allow yields
NumCSharpCalls--;
Utl.Assert( NumCSharpCalls == ((fromState != null) ? fromState.NumCSharpCalls : 0) );
return status;
}
int ILuaAPI.Yield( int numResults )
{
return API.YieldK( numResults, 0, null );
}
int ILuaAPI.YieldK( int numResults,
int context, CSharpFunctionDelegate continueFunc )
{
CallInfo ci = CI;
Utl.ApiCheckNumElems( this, numResults );
if( NumNonYieldable > 0 )
{
if( this != G.MainThread )
G_RunError( "attempt to yield across metamethod/C-call boundary" );
else
G_RunError( "attempt to yield from outside a coroutine" );
}
Status = ThreadStatus.LUA_YIELD;
ci.ExtraIndex = ci.FuncIndex; // save current `func'
if( ci.IsLua ) // inside a hook
{
Utl.ApiCheck( continueFunc == null, "hooks cannot continue after yielding" );
}
else
{
ci.ContinueFunc = continueFunc;
if( ci.ContinueFunc != null ) // is there a continuation
{
ci.Context = context;
}
ci.FuncIndex = Top.Index - (numResults + 1);
D_Throw( ThreadStatus.LUA_YIELD );
}
Utl.Assert( (ci.CallStatus & CallStatus.CIST_HOOKED) != 0 ); // must be inside a hook
return 0;
}
int ILuaAPI.AbsIndex( int index )
{
return (index > 0 || index <= LuaDef.LUA_REGISTRYINDEX)
? index
: Top.Index - CI.FuncIndex + index;
}
int ILuaAPI.GetTop()
{
return Top.Index - (CI.FuncIndex + 1);
}
void ILuaAPI.SetTop( int index )
{
if( index >= 0 )
{
Utl.ApiCheck(index <= StackLast-(CI.FuncIndex+1), "new top too large");
int newTop = CI.FuncIndex+1+index;
for(int i=Top.Index; i<newTop; ++i)
{ Stack[i].V.SetNilValue(); }
Top = Stack[newTop];
}
else
{
Utl.ApiCheck( -(index+1) <= (Top.Index - (CI.FuncIndex + 1)), "invalid new top" );
Top = Stack[Top.Index + index + 1];
}
}
void ILuaAPI.Remove( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
for(int i=addr.Index+1; i<Top.Index; ++i)
{ Stack[i-1].V.SetObj(ref Stack[i].V); }
Top = Stack[Top.Index-1];
}
void ILuaAPI.Insert( int index )
{
StkId p;
if( !Index2Addr( index, out p ) )
Utl.InvalidIndex();
int i = Top.Index;
while(i > p.Index) {
Stack[i].V.SetObj( ref Stack[i-1].V );
i--;
}
p.V.SetObj(ref Top.V);
}
private void MoveTo( StkId fr, int index )
{
StkId to;
if( !Index2Addr( index, out to ) )
Utl.InvalidIndex();
to.V.SetObj(ref fr.V);
}
void ILuaAPI.Replace( int index )
{
Utl.ApiCheckNumElems( this, 1 );
MoveTo( Stack[Top.Index-1], index );
Top = Stack[Top.Index-1];
}
void ILuaAPI.Copy( int fromIndex, int toIndex )
{
StkId fr;
if( !Index2Addr( fromIndex, out fr ) )
Utl.InvalidIndex();
MoveTo( fr, toIndex );
}
void ILuaAPI.XMove( ILuaState to, int n )
{
var toLua = to as LuaState;
if( (LuaState)this == toLua )
return;
Utl.ApiCheckNumElems( this, n );
Utl.ApiCheck( G == toLua.G, "moving among independent states" );
Utl.ApiCheck( toLua.CI.TopIndex - toLua.Top.Index >= n, "not enough elements to move" );
int index = Top.Index-n;
Top = Stack[index];
for(int i=0; i<n; ++i)
{ StkId.inc(ref toLua.Top).V.SetObj(ref Stack[index+i].V); }
}
private void GrowStack(int size)
{
D_GrowStack(size);
}
struct GrowStackParam
{
public LuaState L;
public int size;
}
private static void GrowStackWrap(ref GrowStackParam param)
{
param.L.GrowStack(param.size);
}
private static PFuncDelegate<GrowStackParam> DG_GrowStack = GrowStackWrap;
bool ILuaAPI.CheckStack(int size)
{
bool res = false;
if(StackLast - Top.Index > size)
{ res = true; }
// need to grow stack
else {
int inuse = Top.Index + LuaDef.EXTRA_STACK;
if(inuse > LuaConf.LUAI_MAXSTACK - size)
res = false;
else {
var param = new GrowStackParam();
param.L = this;
param.size = size;
res = D_RawRunProtected(DG_GrowStack, ref param)==ThreadStatus.LUA_OK;
}
}
if(res && CI.TopIndex < Top.Index + size)
CI.TopIndex = Top.Index + size; // adjust frame top
return res;
}
int ILuaAPI.Error()
{
Utl.ApiCheckNumElems( this, 1 );
G_ErrorMsg();
return 0;
}
int ILuaAPI.UpvalueIndex( int i )
{
return LuaDef.LUA_REGISTRYINDEX - i;
}
private string AuxUpvalue(StkId addr, int n, out StkId val)
{
val = null;
if(!addr.V.TtIsFunction())
return null;
if(addr.V.ClIsLuaClosure()) {
var f = addr.V.ClLValue();
var p = f.Proto;
if(!(1 <= n && n <= p.Upvalues.Count))
return null;
val = f.Upvals[n-1].V;
var name = p.Upvalues[n-1].Name;
return (name == null) ? "" : name;
}
else if(addr.V.ClIsCsClosure()) {
var f = addr.V.ClCsValue();
if(!(1 <= n && n <= f.Upvals.Length))
return null;
val = f.Upvals[n-1];
return "";
}
else return null;
}
string ILuaAPI.GetUpvalue( int funcIndex, int n )
{
StkId addr;
if( !Index2Addr( funcIndex, out addr ) )
return null;
StkId val;
var name = AuxUpvalue(addr, n, out val);
if(name == null)
return null;
Top.V.SetObj(ref val.V);
ApiIncrTop();
return name;
}
string ILuaAPI.SetUpvalue( int funcIndex, int n )
{
StkId addr;
if( !Index2Addr( funcIndex, out addr ) )
return null;
Utl.ApiCheckNumElems( this, 1 );
StkId val;
var name = AuxUpvalue(addr, n, out val);
if(name == null)
return null;
Top = Stack[Top.Index-1];
val.V.SetObj(ref Top.V);
return name;
}
void ILuaAPI.CreateTable( int narray, int nrec )
{
var tbl = new LuaTable(this);
Top.V.SetHValue(tbl);
ApiIncrTop();
if(narray > 0 || nrec > 0)
{ tbl.Resize(narray, nrec); }
}
void ILuaAPI.NewTable()
{
API.CreateTable( 0, 0 );
}
bool ILuaAPI.Next( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
throw new System.Exception("table expected");
var tbl = addr.V.HValue();
if( tbl == null )
throw new System.Exception("table expected");
var key = Stack[Top.Index-1];
if( tbl.Next( key, Top ) )
{
ApiIncrTop();
return true;
}
else
{
Top = Stack[Top.Index-1];
return false;
}
}
void ILuaAPI.RawGetI( int index, int n )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.ApiCheck( false, "table expected" );
var tbl = addr.V.HValue();
Utl.ApiCheck( tbl != null, "table expected" );
Top.V.SetObj(ref tbl.GetInt(n).V);
ApiIncrTop();
}
// void ILuaAPI.DebugRawGetI( int index, int n )
// {
// StkId addr;
// if( !Index2Addr( index, out addr ) )
// Utl.ApiCheck( false, "table expected" );
// var tbl = addr.Value as LuaTable;
// Utl.ApiCheck( tbl != null, "table expected" );
// var key = new LuaNumber( n );
// LuaObject outKey, outValue;
// tbl.DebugGet( key, out outKey, out outValue );
// Top.Value = outKey;
// ApiIncrTop();
// Top.Value = outValue;
// ApiIncrTop();
// }
string ILuaAPI.DebugGetInstructionHistory()
{
#if DEBUG_RECORD_INS
var sb = new System.Text.StringBuilder();
foreach( var i in InstructionHistory ) {
sb.AppendLine( i.ToString() );
}
return sb.ToString();
#else
return "DEBUG_RECORD_INS not defined";
#endif
}
void ILuaAPI.RawGet( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
throw new System.Exception("table expected");
if(!addr.V.TtIsTable())
throw new System.Exception("table expected");
var tbl = addr.V.HValue();
var below = Stack[Top.Index-1];
below.V.SetObj( ref tbl.Get( ref below.V ).V );
}
void ILuaAPI.RawSetI( int index, int n )
{
Utl.ApiCheckNumElems( this, 1 );
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
Utl.ApiCheck( addr.V.TtIsTable(), "table expected" );
var tbl = addr.V.HValue();
tbl.SetInt( n, ref Stack[Top.Index-1].V );
Top = Stack[Top.Index-1];
}
void ILuaAPI.RawSet( int index )
{
Utl.ApiCheckNumElems( this, 2 );
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
Utl.ApiCheck( addr.V.TtIsTable(), "table expected" );
var tbl = addr.V.HValue();
tbl.Set( ref Stack[Top.Index-2].V, ref Stack[Top.Index-1].V );
Top = Stack[Top.Index-2];
}
void ILuaAPI.GetField( int index, string key )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
Top.V.SetSValue(key);
var below = Top;
ApiIncrTop();
V_GetTable( addr, below, below );
}
void ILuaAPI.SetField( int index, string key )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
StkId.inc(ref Top).V.SetSValue( key );
V_SetTable( addr, Stack[Top.Index-1], Stack[Top.Index-2] );
Top = Stack[Top.Index-2];
}
void ILuaAPI.GetTable( int index )
{
StkId addr;
if(! Index2Addr( index, out addr ) )
Utl.InvalidIndex();
var below = Stack[Top.Index - 1];
V_GetTable( addr, below, below );
}
void ILuaAPI.SetTable( int index )
{
StkId addr;
Utl.ApiCheckNumElems( this, 2 );
if(! Index2Addr( index, out addr ) )
Utl.InvalidIndex();
var key = Stack[Top.Index - 2];
var val = Stack[Top.Index - 1];
V_SetTable( addr, key, val);
Top = Stack[Top.Index-2];
}
void ILuaAPI.Concat( int n )
{
Utl.ApiCheckNumElems( this, n );
if( n >= 2 )
{
V_Concat( n );
}
else if( n == 0 )
{
Top.V.SetSValue("");
ApiIncrTop();
}
}
LuaType ILuaAPI.Type( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
return LuaType.LUA_TNONE;
return (LuaType)addr.V.Tt;
}
internal static string TypeName( LuaType t )
{
switch( t )
{
case LuaType.LUA_TNIL:
return "nil";
case LuaType.LUA_TBOOLEAN:
return "boolean";
case LuaType.LUA_TLIGHTUSERDATA:
return "userdata";
case LuaType.LUA_TUINT64:
return "UInt64";
case LuaType.LUA_TNUMBER:
return "number";
case LuaType.LUA_TSTRING:
return "string";
case LuaType.LUA_TTABLE:
return "table";
case LuaType.LUA_TFUNCTION:
return "function";
case LuaType.LUA_TUSERDATA:
return "userdata";
case LuaType.LUA_TTHREAD:
return "thread";
case LuaType.LUA_TPROTO:
return "proto";
case LuaType.LUA_TUPVAL:
return "upval";
default:
return "no value";
}
}
string ILuaAPI.TypeName( LuaType t )
{
return TypeName(t);
}
internal string ObjTypeName( ref TValue v )
{
return TypeName((LuaType)v.Tt);
}
// ApiIncrTop() Top CI.Top
internal void O_PushString( string s )
{
Top.V.SetSValue(s);
IncrTop();
}
bool ILuaAPI.IsNil( int index )
{
return API.Type( index ) == LuaType.LUA_TNIL;
}
bool ILuaAPI.IsNone( int index )
{
return API.Type( index ) == LuaType.LUA_TNONE;
}
bool ILuaAPI.IsNoneOrNil( int index )
{
LuaType t = API.Type( index );
return t == LuaType.LUA_TNONE ||
t == LuaType.LUA_TNIL;
}
bool ILuaAPI.IsString( int index )
{
LuaType t = API.Type( index );
return( t == LuaType.LUA_TSTRING || t == LuaType.LUA_TNUMBER );
}
bool ILuaAPI.IsTable( int index )
{
return API.Type( index ) == LuaType.LUA_TTABLE;
}
bool ILuaAPI.IsFunction( int index )
{
return API.Type( index ) == LuaType.LUA_TFUNCTION;
}
bool ILuaAPI.Compare( int index1, int index2, LuaEq op )
{
StkId addr1;
if( !Index2Addr( index1, out addr1 ) )
Utl.InvalidIndex();
StkId addr2;
if( !Index2Addr( index2, out addr2 ) )
Utl.InvalidIndex();
switch( op )
{
case LuaEq.LUA_OPEQ: return EqualObj( ref addr1.V, ref addr2.V, false );
case LuaEq.LUA_OPLT: return V_LessThan( addr1, addr2 );
case LuaEq.LUA_OPLE: return V_LessEqual( addr1, addr2 );
default: Utl.ApiCheck( false, "invalid option" ); return false;
}
}
bool ILuaAPI.RawEqual( int index1, int index2 )
{
StkId addr1;
if( !Index2Addr( index1, out addr1 ) )
return false;
StkId addr2;
if( !Index2Addr( index2, out addr2 ) )
return false;
return V_RawEqualObj( ref addr1.V, ref addr2.V );
}
int ILuaAPI.RawLen( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
switch( addr.V.Tt )
{
case (int)LuaType.LUA_TSTRING:
{
var s = addr.V.SValue();
return s == null ? 0 : s.Length;
}
case (int)LuaType.LUA_TUSERDATA:
{
return addr.V.RawUValue().Length;
}
case (int)LuaType.LUA_TTABLE:
{
return addr.V.HValue().Length;
}
default: return 0;
}
}
void ILuaAPI.Len( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
V_ObjLen( Top, addr );
ApiIncrTop();
}
void ILuaAPI.PushNil()
{
Top.V.SetNilValue();
ApiIncrTop();
}
void ILuaAPI.PushBoolean( bool b )
{
Top.V.SetBValue( b );
ApiIncrTop();
}
void ILuaAPI.PushNumber( double n )
{
Top.V.SetNValue( n );
ApiIncrTop();
}
void ILuaAPI.PushInteger( int n )
{
Top.V.SetNValue( (double)n );
ApiIncrTop();
}
void ILuaAPI.PushUnsigned( uint n )
{
Top.V.SetNValue( (double)n );
ApiIncrTop();
}
string ILuaAPI.PushString( string s )
{
if( s == null )
{
API.PushNil();
return null;
}
else
{
Top.V.SetSValue(s);
ApiIncrTop();
return s;
}
}
void ILuaAPI.PushCSharpFunction( CSharpFunctionDelegate f )
{
API.PushCSharpClosure( f, 0 );
}
void ILuaAPI.PushCSharpClosure( CSharpFunctionDelegate f, int n )
{
if( n == 0 )
{
Top.V.SetClCsValue( new LuaCsClosureValue( f ) );
}
else
{
// UpValue C# function
Utl.ApiCheckNumElems( this, n );
Utl.ApiCheck( n <= LuaLimits.MAXUPVAL, "upvalue index too large" );
LuaCsClosureValue cscl = new LuaCsClosureValue( f, n );
int index = Top.Index - n;
Top = Stack[index];
for(int i=0; i<n; ++i)
{ cscl.Upvals[i].V.SetObj( ref Stack[index+i].V ); }
Top.V.SetClCsValue( cscl );
}
ApiIncrTop();
}
void ILuaAPI.PushValue( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
Top.V.SetObj(ref addr.V);
ApiIncrTop();
}
void ILuaAPI.PushGlobalTable()
{
API.RawGetI( LuaDef.LUA_REGISTRYINDEX, LuaDef.LUA_RIDX_GLOBALS );
}
void ILuaAPI.PushLightUserData( object o )
{
Top.V.SetPValue( o );
ApiIncrTop();
}
void ILuaAPI.PushUInt64( UInt64 o )
{
Top.V.SetUInt64Value( o );
ApiIncrTop();
}
bool ILuaAPI.PushThread()
{
Top.V.SetThValue(this);
ApiIncrTop();
return G.MainThread == (LuaState)this;
}
void ILuaAPI.Pop( int n )
{
API.SetTop( -n-1 );
}
bool ILuaAPI.GetMetaTable( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
LuaTable mt;
switch( addr.V.Tt )
{
case (int)LuaType.LUA_TTABLE:
{
var tbl = addr.V.HValue();
mt = tbl.MetaTable;
break;
}
case (int)LuaType.LUA_TUSERDATA:
{
var ud = addr.V.RawUValue();
mt = ud.MetaTable;
break;
}
default:
{
mt = G.MetaTables[addr.V.Tt];
break;
}
}
if( mt == null )
return false;
else
{
Top.V.SetHValue( mt );
ApiIncrTop();
return true;
}
}
bool ILuaAPI.SetMetaTable( int index )
{
Utl.ApiCheckNumElems( this, 1 );
StkId addr;
if( !Index2Addr( index, out addr ) )
Utl.InvalidIndex();
var below = Stack[Top.Index - 1];
LuaTable mt;
if( below.V.TtIsNil() )
mt = null;
else
{
Utl.ApiCheck( below.V.TtIsTable(), "table expected" );
mt = below.V.HValue();
}
switch( addr.V.Tt )
{
case (int)LuaType.LUA_TTABLE:
{
var tbl = addr.V.HValue();
tbl.MetaTable = mt;
break;
}
case (int)LuaType.LUA_TUSERDATA:
{
var ud = addr.V.RawUValue();
ud.MetaTable = mt;
break;
}
default:
{
G.MetaTables[addr.V.Tt] = mt;
break;
}
}
Top = Stack[Top.Index - 1];
return true;
}
void ILuaAPI.GetGlobal( string name )
{
var gt = G.Registry.V.HValue().GetInt( LuaDef.LUA_RIDX_GLOBALS );
StkId.inc(ref Top).V.SetSValue(name);
V_GetTable(gt, Stack[Top.Index-1], Stack[Top.Index-1]);
}
void ILuaAPI.SetGlobal( string name )
{
var gt = G.Registry.V.HValue().GetInt( LuaDef.LUA_RIDX_GLOBALS );
StkId.inc(ref Top).V.SetSValue(name);
V_SetTable(gt, Stack[Top.Index-1], Stack[Top.Index-2]);
Top = Stack[Top.Index-2];
}
string ILuaAPI.ToString( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
return null;
if(addr.V.TtIsString()) {
return addr.V.OValue as string;
}
if(!V_ToString(ref addr.V)) {
return null;
}
if( !Index2Addr( index, out addr ) )
return null;
Utl.Assert(addr.V.TtIsString());
return addr.V.OValue as string;
}
double ILuaAPI.ToNumberX( int index, out bool isnum )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
{
isnum = false;
return 0.0;
}
if(addr.V.TtIsNumber()) {
isnum = true;
return addr.V.NValue;
}
if(addr.V.TtIsString()) {
var n = new TValue();
if(V_ToNumber(addr, ref n)) {
isnum = true;
return n.NValue;
}
}
isnum = false;
return 0;
}
double ILuaAPI.ToNumber( int index )
{
bool isnum;
return API.ToNumberX( index, out isnum );
}
int ILuaAPI.ToIntegerX( int index, out bool isnum )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
{
isnum = false;
return 0;
}
if(addr.V.TtIsNumber()) {
isnum = true;
return (int)addr.V.NValue;
}
if(addr.V.TtIsString()) {
var n = new TValue();
if(V_ToNumber(addr, ref n)) {
isnum = true;
return (int)n.NValue;
}
}
isnum = false;
return 0;
}
int ILuaAPI.ToInteger( int index )
{
bool isnum;
return API.ToIntegerX( index, out isnum );
}
uint ILuaAPI.ToUnsignedX( int index, out bool isnum )
{
StkId addr;
if( !Index2Addr( index, out addr ) ) {
isnum = false;
return 0;
}
if( addr.V.TtIsNumber() ) {
isnum = true;
return (uint)addr.V.NValue;
}
if( addr.V.TtIsString() ) {
var n = new TValue();
if(V_ToNumber(addr, ref n)) {
isnum = true;
return (uint)n.NValue;
}
}
isnum = false;
return 0;
}
uint ILuaAPI.ToUnsigned( int index )
{
bool isnum;
return API.ToUnsignedX( index, out isnum );
}
bool ILuaAPI.ToBoolean( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
return false;
return !IsFalse(ref addr.V);
}
UInt64 ILuaAPI.ToUInt64X( int index, out bool isnum )
{
StkId addr;
if( !Index2Addr( index, out addr ) ) {
isnum = false;
return 0;
}
if( !addr.V.TtIsUInt64() ) {
isnum = false;
return 0;
}
isnum = true;
return addr.V.UInt64Value;
}
UInt64 ILuaAPI.ToUInt64( int index )
{
bool isnum;
return API.ToUInt64X( index, out isnum );
}
object ILuaAPI.ToObject( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
return null;
return addr.V.OValue;
}
object ILuaAPI.ToUserData( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
return null;
switch(addr.V.Tt) {
case (int)LuaType.LUA_TUSERDATA:
throw new System.NotImplementedException();
case (int)LuaType.LUA_TLIGHTUSERDATA: { return addr.V.OValue; }
case (int)LuaType.LUA_TUINT64: { return addr.V.UInt64Value; }
default: return null;
}
}
ILuaState ILuaAPI.ToThread( int index )
{
StkId addr;
if( !Index2Addr( index, out addr ) )
return null;
return addr.V.TtIsThread() ? addr.V.OValue as ILuaState : null;
}
private bool Index2Addr( int index, out StkId addr )
{
CallInfo ci = CI;
if( index > 0 )
{
var addrIndex = ci.FuncIndex + index;
Utl.ApiCheck( index <= ci.TopIndex - (ci.FuncIndex + 1), "unacceptable index" );
if( addrIndex >= Top.Index ) {
addr = default(StkId);
return false;
}
addr = Stack[addrIndex];
return true;
}
else if( index > LuaDef.LUA_REGISTRYINDEX )
{
Utl.ApiCheck( index != 0 && -index <= Top.Index - (ci.FuncIndex + 1), "invalid index" );
addr = Stack[Top.Index + index];
return true;
}
else if( index == LuaDef.LUA_REGISTRYINDEX )
{
addr = G.Registry;
return true;
}
// upvalues
else
{
index = LuaDef.LUA_REGISTRYINDEX - index;
Utl.ApiCheck( index <= LuaLimits.MAXUPVAL + 1, "upvalue index too large" );
var func = Stack[ci.FuncIndex];
Utl.Assert(func.V.TtIsFunction());
if(func.V.ClIsLcsClosure()) {
addr = default(StkId);
return false;
}
Utl.Assert(func.V.ClIsCsClosure());
var clcs = func.V.ClCsValue();
if(index > clcs.Upvals.Length) {
addr = default(StkId);
return false;
}
addr = clcs.Upvals[index-1];
return true;
}
}
// private void RegisterGlobalFunc( string name, CSharpFunctionDelegate f )
// {
// API.PushCSharpFunction( f );
// API.SetGlobal( name );
// }
}
}