Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
NLua
Commits
00eddcac
Commit
00eddcac
authored
Jun 03, 2012
by
Megax
Browse files
* Kisebb modositasok.
parent
a0afbf36
Changes
8
Hide whitespace changes
Inline
Side-by-side
KopiLua/lapi.cs
View file @
00eddcac
...
...
@@ -277,8 +277,6 @@ namespace KopiLua
public
static
int
lua_rawequal
(
lua_State
L
,
int
index1
,
int
index2
)
{
StkId
o1
=
index2adr
(
L
,
index1
);
StkId
o2
=
index2adr
(
L
,
index2
);
Console
.
WriteLine
(
"lua_rawequal: {0}"
,
(
o1
==
luaO_nilobject
||
o2
==
luaO_nilobject
));
Console
.
WriteLine
(
"lua_rawequal2: {0}"
,
luaO_rawequalObj
(
o1
,
o2
));
return
(
o1
==
luaO_nilobject
||
o2
==
luaO_nilobject
)
?
0
:
luaO_rawequalObj
(
o1
,
o2
);
}
...
...
@@ -392,11 +390,10 @@ namespace KopiLua
public
static
uint
lua_touserdata2
(
lua_State
L
,
int
idx
)
{
StkId
o
=
index2adr
(
L
,
idx
);
Console
.
WriteLine
(
"x0:{0}"
,
o
);
switch
(
ttype
(
o
))
{
case
LUA_TUSERDATA
:
Console
.
WriteLine
(
"x:{0}"
,
rawuvalue
(
o
).
len
);
return
(
rawuvalue
(
o
).
len
);
case
LUA_TLIGHTUSERDATA
:
Console
.
WriteLine
(
"x2:{0}"
,
rawuvalue
(
o
).
len
);
return
(
rawuvalue
(
o
).
len
);
default
:
Console
.
WriteLine
(
"x3:{0}"
,
0
);
return
0
;
case
LUA_TUSERDATA
:
return
(
rawuvalue
(
o
).
len
);
case
LUA_TLIGHTUSERDATA
:
return
(
rawuvalue
(
o
).
len
);
default
:
return
0
;
}
}
...
...
@@ -556,7 +553,6 @@ namespace KopiLua
}
public
static
void
lua_getfield
(
lua_State
L
,
int
idx
,
CharPtr
k
)
{
Console
.
WriteLine
(
"lllll:{0}"
,
k
);
StkId
t
;
TValue
key
=
new
TValue
();
lua_lock
(
L
);
...
...
@@ -624,7 +620,6 @@ namespace KopiLua
res
=
1
;
}
lua_unlock
(
L
);
Console
.
WriteLine
(
"nulllll: {0}"
,
res
);
return
res
;
}
...
...
@@ -835,7 +830,6 @@ namespace KopiLua
api_checkvalidindex
(
L
,
o
);
func
=
savestack
(
L
,
o
);
}
Console
.
WriteLine
(
"func:{0}"
,
func
);
c
.
func
=
L
.
top
-
(
nargs
+
1
);
/* function to be called */
c
.
nresults
=
nresults
;
status
=
luaD_pcall
(
L
,
f_call
,
c
,
savestack
(
L
,
c
.
func
),
func
);
...
...
KopiLua/lobject.cs
View file @
00eddcac
...
...
@@ -758,7 +758,6 @@ namespace KopiLua
public
static
int
luaO_rawequalObj
(
TValue
t1
,
TValue
t2
)
{
Console
.
WriteLine
(
"luaO_rawequalObj: {0}"
,
ttype
(
t1
)
!=
ttype
(
t2
));
if
(
ttype
(
t1
)
!=
ttype
(
t2
))
return
0
;
else
switch
(
ttype
(
t1
))
{
case
LUA_TNIL
:
...
...
@@ -771,9 +770,6 @@ namespace KopiLua
return
pvalue
(
t1
)
==
pvalue
(
t2
)
?
1
:
0
;
default
:
lua_assert
(
iscollectable
(
t1
));
Console
.
WriteLine
(
"luaO_rawequalObj1-0: {0}"
,
gcvalue
(
t1
));
Console
.
WriteLine
(
"luaO_rawequalObj1-1: {0}"
,
gcvalue
(
t2
));
Console
.
WriteLine
(
"luaO_rawequalObj1: {0}"
,
gcvalue
(
t1
)
==
gcvalue
(
t2
));
return
gcvalue
(
t1
)
==
gcvalue
(
t2
)
?
1
:
0
;
}
}
...
...
KopiLua/lstring.cs
View file @
00eddcac
...
...
@@ -107,7 +107,6 @@ namespace KopiLua
}
//return newlstr(L, str, l, h); /* not found */
TString
res
=
newlstr
(
L
,
str
,
l
,
h
);
Console
.
WriteLine
(
res
);
return
res
;
}
...
...
LuaInterface/Lua.cs
View file @
00eddcac
...
...
@@ -237,11 +237,8 @@ namespace LuaInterface
public
object
[]
DoFile
(
string
fileName
)
{
int
oldTop
=
KopiLua
.
Lua
.
lua_gettop
(
luaState
);
//Console.WriteLine("aaa: {0}",oldTop);
if
(
KopiLua
.
Lua
.
luaL_loadfile
(
luaState
,
fileName
)==
0
)
{
//Console.WriteLine("aaa2: {0}",KopiLua.Lua.lua_pcall(luaState, 0, -1, 0));
//Console.WriteLine("zzzzzzzzzzzzzzzzzz");
if
(
KopiLua
.
Lua
.
lua_pcall
(
luaState
,
0
,
-
1
,
0
)
==
0
)
return
translator
.
popValues
(
luaState
,
oldTop
);
else
...
...
@@ -348,7 +345,8 @@ namespace LuaInterface
public
LuaFunction
GetFunction
(
string
fullPath
)
{
object
obj
=
this
[
fullPath
];
return
(
obj
is
KopiLua
.
Lua
.
lua_CFunction
?
new
LuaFunction
((
KopiLua
.
Lua
.
lua_CFunction
)
obj
,
this
)
:
(
LuaFunction
)
obj
);
//return (obj is KopiLua.Lua.lua_CFunction ? new LuaFunction((KopiLua.Lua.lua_CFunction)obj,this) : (LuaFunction)obj);
return
(
obj
is
KopiLua
.
Lua
.
lua_CFunction
?
new
LuaFunction
((
KopiLua
.
Lua
.
lua_CFunction
)
obj
,
this
)
:
/*(LuaFunction)*/
new
LuaFunction
(
obj
.
GetHashCode
(),
this
));
}
/*
* Gets a function global variable as a delegate of
...
...
@@ -533,7 +531,7 @@ namespace LuaInterface
* Registers an object's method as a Lua function (global or table field)
* The method may have any signature
*/
public
LuaFunction
RegisterFunction
(
string
path
,
object
target
,
MethodInfo
function
)
public
LuaFunction
RegisterFunction
(
string
path
,
object
target
,
MethodInfo
function
)
{
// We leave nothing on the stack when we are done
int
oldTop
=
KopiLua
.
Lua
.
lua_gettop
(
luaState
);
...
...
@@ -545,7 +543,6 @@ namespace LuaInterface
LuaFunction
f
=
GetFunction
(
path
);
KopiLua
.
Lua
.
lua_settop
(
luaState
,
oldTop
);
return
f
;
}
...
...
LuaInterface/LuaLib/LuaLib.cs
View file @
00eddcac
...
...
@@ -270,7 +270,7 @@ namespace LuaWrap
public
static
int
luanet_rawnetobj
(
Lua
.
lua_State
luaState
,
int
obj
)
{
int
udata
=
Convert
.
ToInt32
(
Lua
.
lua_touserdata2
(
luaState
,
obj
)
)
;
int
udata
=
(
int
)
Lua
.
lua_touserdata2
(
luaState
,
obj
);
if
(
udata
!=
0
)
return
udata
;
return
-
1
;
...
...
@@ -284,25 +284,19 @@ namespace LuaWrap
public
static
int
checkudata_raw
(
Lua
.
lua_State
luaState
,
int
ud
,
string
tname
)
{
int
p
=
(
int
)
Lua
.
lua_touserdata2
(
luaState
,
ud
);
Console
.
WriteLine
(
"z:{0}"
,
p
);
if
(
p
!=
0
)
{
/* value is a userdata? */
Console
.
WriteLine
(
"z0:{0}"
,
p
);
//Console.WriteLine("z2:{0}", Lua.lua_getmetatable(luaState, ud));
if
(
Lua
.
lua_getmetatable
(
luaState
,
ud
)!=
0
)
{
/* does it have a metatable? */
Console
.
WriteLine
(
"z1:{0}"
,
p
);
Lua
.
lua_getfield
(
luaState
,
(
int
)
PseudoIndex
.
Registry
,
tname
);
/* get correct metatable */
//Console.WriteLine("z3:{0}", p);
bool
isEqual
=
Lua
.
lua_rawequal
(
luaState
,
-
1
,
-
2
).
ToBoolean
();
Console
.
WriteLine
(
"z2:{0}"
,
p
);
Console
.
WriteLine
(
"z2-1:{0}"
,
isEqual
);
// NASTY - we need our own version of the lua_pop macro
// lua_pop(L, 2); /* remove both metatables */
Lua
.
lua_settop
(
luaState
,
-(
2
)
-
1
);
Console
.
WriteLine
(
"z3:{0}"
,
p
);
if
(
isEqual
)
/* does it have the correct mt? */
return
p
;
}
...
...
@@ -330,29 +324,23 @@ Console.WriteLine("z3:{0}", p);
{
int
udata
;
Console
.
WriteLine
(
"asd:"
+(
Lua
.
lua_type
(
luaState
,
index
).
ToLuaType
()
==
LuaType
.
UserData
).
ToString
());
Console
.
WriteLine
(
"asd:1"
+
Lua
.
lua_type
(
luaState
,
index
));
if
(
Lua
.
lua_type
(
luaState
,
index
).
ToLuaType
()
==
LuaType
.
UserData
)
{
if
(
luaL_checkmetatable
(
luaState
,
index
))
{
udata
=
(
int
)
Lua
.
lua_touserdata2
(
luaState
,
index
);
Console
.
WriteLine
(
"asd0:{0}"
,
udata
);
if
(
udata
!=
0
)
return
udata
;
}
udata
=
checkudata_raw
(
luaState
,
index
,
"luaNet_class"
);
Console
.
WriteLine
(
"asd2:{0}"
,
udata
);
if
(
udata
!=
0
)
return
udata
;
udata
=
checkudata_raw
(
luaState
,
index
,
"luaNet_searchbase"
);
Console
.
WriteLine
(
"asd3:{0}"
,
udata
);
if
(
udata
!=
0
)
return
udata
;
udata
=
checkudata_raw
(
luaState
,
index
,
"luaNet_function"
);
Console
.
WriteLine
(
"asd4:{0}"
,
udata
);
if
(
udata
!=
0
)
return
udata
;
}
...
...
LuaInterface/Metatables.cs
View file @
00eddcac
namespace
LuaInterface
{
using
System
;
using
System.IO
;
using
System.Collections
;
using
System.Reflection
;
using
System.Diagnostics
;
using
System.Collections.Generic
;
using
LuaWrap
;
using
System.Runtime.InteropServices
;
/*
* Functions used in the metatables of userdata representing
* CLR objects
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
class
MetaFunctions
{
/*
* __index metafunction for CLR objects. Implemented in Lua.
*/
internal
static
string
luaIndexFunction
=
"local function index(obj,name)\n"
+
" local meta=getmetatable(obj)\n"
+
" local cached=meta.cache[name]\n"
+
" if cached~=nil then\n"
+
" return cached\n"
+
" else\n"
+
" local value,isFunc=get_object_member(obj,name)\n"
+
" if isFunc then\n"
+
" meta.cache[name]=value\n"
+
" end\n"
+
" return value\n"
+
" end\n"
+
"end\n"
+
"return index"
;
private
ObjectTranslator
translator
;
private
Hashtable
memberCache
=
new
Hashtable
();
internal
KopiLua
.
Lua
.
lua_CFunction
gcFunction
,
indexFunction
,
newindexFunction
,
baseIndexFunction
,
classIndexFunction
,
classNewindexFunction
,
execDelegateFunction
,
callConstructorFunction
,
toStringFunction
;
public
MetaFunctions
(
ObjectTranslator
translator
)
{
this
.
translator
=
translator
;
gcFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
collectObject
);
toStringFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
toString
);
indexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
getMethod
);
newindexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
setFieldOrProperty
);
baseIndexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
getBaseMethod
);
callConstructorFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
callConstructor
);
classIndexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
getClassMethod
);
classNewindexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
setClassFieldOrProperty
);
execDelegateFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
runFunctionDelegate
);
}
/*
* __call metafunction of CLR delegates, retrieves and calls the delegate.
*/
private
int
runFunctionDelegate
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
KopiLua
.
Lua
.
lua_CFunction
func
=
(
KopiLua
.
Lua
.
lua_CFunction
)
translator
.
getRawNetObject
(
luaState
,
1
);
KopiLua
.
Lua
.
lua_remove
(
luaState
,
1
);
return
func
(
luaState
);
}
/*
* __gc metafunction of CLR objects.
*/
private
int
collectObject
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
int
udata
=
LuaLib
.
luanet_rawnetobj
(
luaState
,
1
);
if
(
udata
!=
-
1
)
{
translator
.
collectObject
(
udata
);
}
else
{
// Debug.WriteLine("not found: " + udata);
}
return
0
;
}
/*
* __tostring metafunction of CLR objects.
*/
private
int
toString
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
!=
null
)
{
translator
.
push
(
luaState
,
obj
.
ToString
()
+
": "
+
obj
.
GetHashCode
());
}
else
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
/// <summary>
/// Debug tool to dump the lua stack
/// </summary>
/// FIXME, move somewhere else
public
static
void
dumpStack
(
ObjectTranslator
translator
,
KopiLua
.
Lua
.
lua_State
luaState
)
{
int
depth
=
KopiLua
.
Lua
.
lua_gettop
(
luaState
);
Debug
.
WriteLine
(
"lua stack depth: "
+
depth
);
for
(
int
i
=
1
;
i
<=
depth
;
i
++)
{
LuaType
type
=
KopiLua
.
Lua
.
lua_type
(
luaState
,
i
).
ToLuaType
();
// we dump stacks when deep in calls, calling typename while the stack is in flux can fail sometimes, so manually check for key types
string
typestr
=
(
type
==
LuaType
.
Table
)
?
"table"
:
KopiLua
.
Lua
.
lua_typename
(
luaState
,
Convert
.
ToInt32
(
type
)).
ToString
();
string
strrep
=
KopiLua
.
Lua
.
lua_tostring
(
luaState
,
i
).
ToString
();
if
(
type
==
LuaType
.
UserData
)
{
object
obj
=
translator
.
getRawNetObject
(
luaState
,
i
);
strrep
=
obj
.
ToString
();
}
Debug
.
Print
(
"{0}: ({1}) {2}"
,
i
,
typestr
,
strrep
);
}
}
/*
* Called by the __index metafunction of CLR objects in case the
* method is not cached or it is a field/property/event.
* Receives the object and the member name as arguments and returns
* either the value of the member or a delegate to call it.
* If the member does not exist returns nil.
*/
private
int
getMethod
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
)
{
translator
.
throwError
(
luaState
,
"trying to index an invalid object reference"
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
object
index
=
translator
.
getObject
(
luaState
,
2
);
//Type indexType = index.GetType();
string
methodName
=
index
as
string
;
// will be null if not a string arg
Type
objType
=
obj
.
GetType
();
// Handle the most common case, looking up the method by name
if
(
methodName
!=
null
&&
isMemberPresent
(
objType
,
methodName
))
return
getMember
(
luaState
,
objType
,
obj
,
methodName
,
BindingFlags
.
Instance
);
// Try to access by array if the type is right and index is an int (lua numbers always come across as double)
if
(
objType
.
IsArray
&&
index
is
double
)
{
object
[]
arr
=
(
object
[])
obj
;
translator
.
push
(
luaState
,
arr
[(
int
)((
double
)
index
)]);
}
else
{
// Try to use get_Item to index into this .net object
MethodInfo
getter
=
objType
.
GetMethod
(
"get_Item"
);
ParameterInfo
[]
actualParms
=
(
getter
!=
null
)
?
getter
.
GetParameters
()
:
null
;
if
(
actualParms
==
null
||
actualParms
.
Length
!=
1
)
{
translator
.
throwError
(
luaState
,
"method not found (or no indexer): "
+
index
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
else
{
// Get the index in a form acceptable to the getter
index
=
translator
.
getAsType
(
luaState
,
2
,
actualParms
[
0
].
ParameterType
);
object
[]
args
=
new
object
[
1
];
// Just call the indexer - if out of bounds an exception will happen
args
[
0
]
=
index
;
try
{
object
result
=
getter
.
Invoke
(
obj
,
args
);
translator
.
push
(
luaState
,
result
);
}
catch
(
TargetInvocationException
e
)
{
// Provide a more readable description for the common case of key not found
if
(
e
.
InnerException
is
KeyNotFoundException
)
translator
.
throwError
(
luaState
,
"key '"
+
index
+
"' not found "
);
else
translator
.
throwError
(
luaState
,
"exception indexing '"
+
index
+
"' "
+
e
.
Message
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
}
}
KopiLua
.
Lua
.
lua_pushboolean
(
luaState
,
0
);
return
2
;
}
/*
* __index metafunction of base classes (the base field of Lua tables).
* Adds a prefix to the method name to call the base version of the method.
*/
private
int
getBaseMethod
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
)
{
translator
.
throwError
(
luaState
,
"trying to index an invalid object reference"
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
KopiLua
.
Lua
.
lua_pushboolean
(
luaState
,
0
);
return
2
;
}
string
methodName
=
KopiLua
.
Lua
.
lua_tostring
(
luaState
,
2
).
ToString
();
if
(
methodName
==
null
)
{
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
KopiLua
.
Lua
.
lua_pushboolean
(
luaState
,
0
);
return
2
;
}
getMember
(
luaState
,
obj
.
GetType
(),
obj
,
"__luaInterface_base_"
+
methodName
,
BindingFlags
.
Instance
);
KopiLua
.
Lua
.
lua_settop
(
luaState
,
-
2
);
if
(
KopiLua
.
Lua
.
lua_type
(
luaState
,
-
1
).
ToLuaType
()
==
LuaType
.
Nil
)
{
KopiLua
.
Lua
.
lua_settop
(
luaState
,
-
2
);
return
getMember
(
luaState
,
obj
.
GetType
(),
obj
,
methodName
,
BindingFlags
.
Instance
);
}
KopiLua
.
Lua
.
lua_pushboolean
(
luaState
,
0
);
return
2
;
}
/// <summary>
/// Does this method exist as either an instance or static?
/// </summary>
/// <param name="objType"></param>
/// <param name="methodName"></param>
/// <returns></returns>
bool
isMemberPresent
(
IReflect
objType
,
string
methodName
)
{
object
cachedMember
=
checkMemberCache
(
memberCache
,
objType
,
methodName
);
if
(
cachedMember
!=
null
)
return
true
;
MemberInfo
[]
members
=
objType
.
GetMember
(
methodName
,
BindingFlags
.
Static
|
BindingFlags
.
Instance
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
return
(
members
.
Length
>
0
);
}
/*
* Pushes the value of a member or a delegate to call it, depending on the type of
* the member. Works with static or instance members.
* Uses reflection to find members, and stores the reflected MemberInfo object in
* a cache (indexed by the type of the object and the name of the member).
*/
private
int
getMember
(
KopiLua
.
Lua
.
lua_State
luaState
,
IReflect
objType
,
object
obj
,
string
methodName
,
BindingFlags
bindingType
)
{
bool
implicitStatic
=
false
;
MemberInfo
member
=
null
;
object
cachedMember
=
checkMemberCache
(
memberCache
,
objType
,
methodName
);
//object cachedMember=null;
if
(
cachedMember
is
KopiLua
.
Lua
.
lua_CFunction
)
{
translator
.
pushFunction
(
luaState
,
(
KopiLua
.
Lua
.
lua_CFunction
)
cachedMember
);
translator
.
push
(
luaState
,
true
);
return
2
;
}
else
if
(
cachedMember
!=
null
)
{
member
=
(
MemberInfo
)
cachedMember
;
}
else
{
MemberInfo
[]
members
=
objType
.
GetMember
(
methodName
,
bindingType
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
if
(
members
.
Length
>
0
)
member
=
members
[
0
];
else
{
// If we can't find any suitable instance members, try to find them as statics - but we only want to allow implicit static
// lookups for fields/properties/events -kevinh
members
=
objType
.
GetMember
(
methodName
,
bindingType
|
BindingFlags
.
Static
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
if
(
members
.
Length
>
0
)
{
member
=
members
[
0
];
implicitStatic
=
true
;
}
}
}
if
(
member
!=
null
)
{
if
(
member
.
MemberType
==
MemberTypes
.
Field
)
{
FieldInfo
field
=
(
FieldInfo
)
member
;
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
member
);
try
{
translator
.
push
(
luaState
,
field
.
GetValue
(
obj
));
}
catch
{
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
}
else
if
(
member
.
MemberType
==
MemberTypes
.
Property
)
{
PropertyInfo
property
=
(
PropertyInfo
)
member
;
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
member
);
try
{
object
val
=
property
.
GetValue
(
obj
,
null
);
translator
.
push
(
luaState
,
val
);
}
catch
(
ArgumentException
)
{
// If we can't find the getter in our class, recurse up to the base class and see
// if they can help.
if
(
objType
is
Type
&&
!(((
Type
)
objType
)
==
typeof
(
object
)))
return
getMember
(
luaState
,
((
Type
)
objType
).
BaseType
,
obj
,
methodName
,
bindingType
);
else
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
catch
(
TargetInvocationException
e
)
// Convert this exception into a Lua error
{
ThrowError
(
luaState
,
e
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
}
else
if
(
member
.
MemberType
==
MemberTypes
.
Event
)
{
EventInfo
eventInfo
=
(
EventInfo
)
member
;
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
member
);
translator
.
push
(
luaState
,
new
RegisterEventHandler
(
translator
.
pendingEvents
,
obj
,
eventInfo
));
}
else
if
(!
implicitStatic
)
{
if
(
member
.
MemberType
==
MemberTypes
.
NestedType
)
{
// kevinh - added support for finding nested types
// cache us
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
member
);
// Find the name of our class
string
name
=
member
.
Name
;
Type
dectype
=
member
.
DeclaringType
;
// Build a new long name and try to find the type by name
string
longname
=
dectype
.
FullName
+
"+"
+
name
;
Type
nestedType
=
translator
.
FindType
(
longname
);
translator
.
pushType
(
luaState
,
nestedType
);
}
else
{
// Member type must be 'method'
KopiLua
.
Lua
.
lua_CFunction
wrapper
=
new
KopiLua
.
Lua
.
lua_CFunction
((
new
LuaMethodWrapper
(
translator
,
objType
,
methodName
,
bindingType
)).
call
);
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
wrapper
);
translator
.
pushFunction
(
luaState
,
wrapper
);
translator
.
push
(
luaState
,
true
);
return
2
;
}
}
else
{
// If we reach this point we found a static method, but can't use it in this context because the user passed in an instance
translator
.
throwError
(
luaState
,
"can't pass instance to static method "
+
methodName
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
}
else
{
// kevinh - we want to throw an exception because meerly returning 'nil' in this case
// is not sufficient. valid data members may return nil and therefore there must be some
// way to know the member just doesn't exist.
translator
.
throwError
(
luaState
,
"unknown member name "
+
methodName
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
// push false because we are NOT returning a function (see luaIndexFunction)
translator
.
push
(
luaState
,
false
);
return
2
;
}
/*
* Checks if a MemberInfo object is cached, returning it or null.
*/
private
object
checkMemberCache
(
Hashtable
memberCache
,
IReflect
objType
,
string
memberName
)
{
Hashtable
members
=
(
Hashtable
)
memberCache
[
objType
];
if
(
members
!=
null
)
return
members
[
memberName
];
else
return
null
;
}
/*
* Stores a MemberInfo object in the member cache.
*/
private
void
setMemberCache
(
Hashtable
memberCache
,
IReflect
objType
,
string
memberName
,
object
member
)
{
Hashtable
members
=
(
Hashtable
)
memberCache
[
objType
];
if
(
members
==
null
)
{
members
=
new
Hashtable
();
memberCache
[
objType
]
=
members
;
}
members
[
memberName
]
=
member
;
}
/*
* __newindex metafunction of CLR objects. Receives the object,
* the member name and the value to be stored as arguments. Throws
* and error if the assignment is invalid.
*/
private
int
setFieldOrProperty
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
object
target
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
target
==
null
)
{
translator
.
throwError
(
luaState
,
"trying to index and invalid object reference"
);
return
0
;
}
Type
type
=
target
.
GetType
();
// First try to look up the parameter as a property name
string
detailMessage
;
bool
didMember
=
trySetMember
(
luaState
,
type
,
target
,
BindingFlags
.
Instance
,
out
detailMessage
);
if
(
didMember
)
return
0
;
// Must have found the property name
// We didn't find a property name, now see if we can use a [] style this accessor to set array contents
try
{
if
(
type
.
IsArray
&&
KopiLua
.
Lua
.
lua_isnumber
(
luaState
,
2
).
ToBoolean
())
{
int
index
=
(
int
)
KopiLua
.
Lua
.
lua_tonumber
(
luaState
,
2
);
Array
arr
=
(
Array
)
target
;
object
val
=
translator
.
getAsType
(
luaState
,
3
,
arr
.
GetType
().
GetElementType
());
arr
.
SetValue
(
val
,
index
);
}
else
{
// Try to see if we have a this[] accessor
MethodInfo
setter
=
type
.
GetMethod
(
"set_Item"
);
if
(
setter
!=
null
)
{
ParameterInfo
[]
args
=
setter
.
GetParameters
();
Type
valueType
=
args
[
1
].
ParameterType
;
// The new val ue the user specified
object
val
=
translator
.
getAsType
(
luaState
,
3
,
valueType
);
Type
indexType
=
args
[
0
].
ParameterType
;
object
index
=
translator
.
getAsType
(
luaState
,
2
,
indexType
);
object
[]
methodArgs
=
new
object
[
2
];
// Just call the indexer - if out of bounds an exception will happen
methodArgs
[
0
]
=
index
;
methodArgs
[
1
]
=
val
;
setter
.
Invoke
(
target
,
methodArgs
);
}
else
{
translator
.
throwError
(
luaState
,
detailMessage
);
// Pass the original message from trySetMember because it is probably best
}
}
}
catch
(
SEHException
)
{
// If we are seeing a C++ exception - this must actually be for Lua's private use. Let it handle it
throw
;
}
catch
(
Exception
e
)
{
ThrowError
(
luaState
,
e
);
}
return
0
;
}
/// <summary>
/// Tries to set a named property or field
/// </summary>
/// <param name="luaState"></param>
/// <param name="targetType"></param>
/// <param name="target"></param>
/// <param name="bindingType"></param>
/// <returns>false if unable to find the named member, true for success</returns>
private
bool
trySetMember
(
KopiLua
.
Lua
.
lua_State
luaState
,
IReflect
targetType
,
object
target
,
BindingFlags
bindingType
,
out
string
detailMessage
)
{
detailMessage
=
null
;
// No error yet
// If not already a string just return - we don't want to call tostring - which has the side effect of
// changing the lua typecode to string
// Note: We don't use isstring because the standard lua C isstring considers either strings or numbers to
// be true for isstring.
if
(
KopiLua
.
Lua
.
lua_type
(
luaState
,
2
).
ToLuaType
()
!=
LuaType
.
String
)
{
detailMessage
=
"property names must be strings"
;
return
false
;
}
// We only look up property names by string
string
fieldName
=
KopiLua
.
Lua
.
lua_tostring
(
luaState
,
2
).
ToString
();
if
(
fieldName
==
null
||
fieldName
.
Length
<
1
||
!(
char
.
IsLetter
(
fieldName
[
0
])
||
fieldName
[
0
]
==
'_'
))
{
detailMessage
=
"invalid property name"
;
return
false
;
}
// Find our member via reflection or the cache
MemberInfo
member
=
(
MemberInfo
)
checkMemberCache
(
memberCache
,
targetType
,
fieldName
);
if
(
member
==
null
)
{
MemberInfo
[]
members
=
targetType
.
GetMember
(
fieldName
,
bindingType
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
if
(
members
.
Length
>
0
)
{
member
=
members
[
0
];
setMemberCache
(
memberCache
,
targetType
,
fieldName
,
member
);
}
else
{
detailMessage
=
"field or property '"
+
fieldName
+
"' does not exist"
;
return
false
;
}
}
if
(
member
.
MemberType
==
MemberTypes
.
Field
)
{
FieldInfo
field
=
(
FieldInfo
)
member
;
object
val
=
translator
.
getAsType
(
luaState
,
3
,
field
.
FieldType
);
try
{
field
.
SetValue
(
target
,
val
);
}
catch
(
Exception
e
)
{
ThrowError
(
luaState
,
e
);
}
// We did a call
return
true
;
}
else
if
(
member
.
MemberType
==
MemberTypes
.
Property
)
{
PropertyInfo
property
=
(
PropertyInfo
)
member
;
object
val
=
translator
.
getAsType
(
luaState
,
3
,
property
.
PropertyType
);
try
{
property
.
SetValue
(
target
,
val
,
null
);
}
catch
(
Exception
e
)
{
ThrowError
(
luaState
,
e
);
}
// We did a call
return
true
;
}
detailMessage
=
"'"
+
fieldName
+
"' is not a .net field or property"
;
return
false
;
}
/*
* Writes to fields or properties, either static or instance. Throws an error
* if the operation is invalid.
*/
private
int
setMember
(
KopiLua
.
Lua
.
lua_State
luaState
,
IReflect
targetType
,
object
target
,
BindingFlags
bindingType
)
{
string
detail
;
bool
success
=
trySetMember
(
luaState
,
targetType
,
target
,
bindingType
,
out
detail
);
if
(!
success
)
translator
.
throwError
(
luaState
,
detail
);
return
0
;
}
/// <summary>
/// Convert a C# exception into a Lua error
/// </summary>
/// <param name="e"></param>
/// We try to look into the exception to give the most meaningful description
void
ThrowError
(
KopiLua
.
Lua
.
lua_State
luaState
,
Exception
e
)
{
// If we got inside a reflection show what really happened
TargetInvocationException
te
=
e
as
TargetInvocationException
;
if
(
te
!=
null
)
e
=
te
.
InnerException
;
translator
.
throwError
(
luaState
,
e
);
}
/*
* __index metafunction of type references, works on static members.
*/
private
int
getClassMethod
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
IReflect
klass
;
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
||
!(
obj
is
IReflect
))
{
translator
.
throwError
(
luaState
,
"trying to index an invalid type reference"
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
else
klass
=
(
IReflect
)
obj
;
if
(
KopiLua
.
Lua
.
lua_isnumber
(
luaState
,
2
).
ToBoolean
())
{
int
size
=
(
int
)
KopiLua
.
Lua
.
lua_tonumber
(
luaState
,
2
);
translator
.
push
(
luaState
,
Array
.
CreateInstance
(
klass
.
UnderlyingSystemType
,
size
));
return
1
;
}
else
{
string
methodName
=
KopiLua
.
Lua
.
lua_tostring
(
luaState
,
2
).
ToString
();
if
(
methodName
==
null
)
{
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
else
return
getMember
(
luaState
,
klass
,
null
,
methodName
,
BindingFlags
.
FlattenHierarchy
|
BindingFlags
.
Static
);
}
}
/*
* __newindex function of type references, works on static members.
*/
private
int
setClassFieldOrProperty
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
IReflect
target
;
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
||
!(
obj
is
IReflect
))
{
translator
.
throwError
(
luaState
,
"trying to index an invalid type reference"
);
return
0
;
}
else
target
=
(
IReflect
)
obj
;
return
setMember
(
luaState
,
target
,
null
,
BindingFlags
.
FlattenHierarchy
|
BindingFlags
.
Static
);
}
/*
* __call metafunction of type references. Searches for and calls
* a constructor for the type. Returns nil if the constructor is not
* found or if the arguments are invalid. Throws an error if the constructor
* generates an exception.
*/
private
int
callConstructor
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
MethodCache
validConstructor
=
new
MethodCache
();
IReflect
klass
;
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
||
!(
obj
is
IReflect
))
{
translator
.
throwError
(
luaState
,
"trying to call constructor on an invalid type reference"
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
else
klass
=
(
IReflect
)
obj
;
KopiLua
.
Lua
.
lua_remove
(
luaState
,
1
);
ConstructorInfo
[]
constructors
=
klass
.
UnderlyingSystemType
.
GetConstructors
();
foreach
(
ConstructorInfo
constructor
in
constructors
)
{
bool
isConstructor
=
matchParameters
(
luaState
,
constructor
,
ref
validConstructor
);
if
(
isConstructor
)
{
try
{
translator
.
push
(
luaState
,
constructor
.
Invoke
(
validConstructor
.
args
));
}
catch
(
TargetInvocationException
e
)
{
ThrowError
(
luaState
,
e
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
catch
{
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
return
1
;
}
}
string
constructorName
=
(
constructors
.
Length
==
0
)
?
"unknown"
:
constructors
[
0
].
Name
;
translator
.
throwError
(
luaState
,
String
.
Format
(
"{0} does not contain constructor({1}) argument match"
,
klass
.
UnderlyingSystemType
,
constructorName
));
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
/*
* Matches a method against its arguments in the Lua stack. Returns
* if the match was succesful. It it was also returns the information
* necessary to invoke the method.
*/
internal
bool
matchParameters
(
KopiLua
.
Lua
.
lua_State
luaState
,
MethodBase
method
,
ref
MethodCache
methodCache
)
{
ExtractValue
extractValue
;
bool
isMethod
=
true
;
ParameterInfo
[]
paramInfo
=
method
.
GetParameters
();
int
currentLuaParam
=
1
;
int
nLuaParams
=
KopiLua
.
Lua
.
lua_gettop
(
luaState
);
ArrayList
paramList
=
new
ArrayList
();
List
<
int
>
outList
=
new
List
<
int
>();
List
<
MethodArgs
>
argTypes
=
new
List
<
MethodArgs
>();
foreach
(
ParameterInfo
currentNetParam
in
paramInfo
)
{
if
(!
currentNetParam
.
IsIn
&&
currentNetParam
.
IsOut
)
// Skips out params
{
outList
.
Add
(
paramList
.
Add
(
null
));
}
else
if
(
currentLuaParam
>
nLuaParams
)
// Adds optional parameters
{
if
(
currentNetParam
.
IsOptional
)
{
paramList
.
Add
(
currentNetParam
.
DefaultValue
);
}
else
{
isMethod
=
false
;
break
;
}
}
else
if
((
extractValue
=
translator
.
typeChecker
.
checkType
(
luaState
,
currentLuaParam
,
currentNetParam
.
ParameterType
))
!=
null
)
// Type checking
{
int
index
=
paramList
.
Add
(
extractValue
(
luaState
,
currentLuaParam
));
MethodArgs
methodArg
=
new
MethodArgs
();
methodArg
.
index
=
index
;
methodArg
.
extractValue
=
extractValue
;
argTypes
.
Add
(
methodArg
);
if
(
currentNetParam
.
ParameterType
.
IsByRef
)
outList
.
Add
(
index
);
currentLuaParam
++;
}
// Type does not match, ignore if the parameter is optional
else
if
(
currentNetParam
.
IsOptional
)
{
paramList
.
Add
(
currentNetParam
.
DefaultValue
);
}
else
// No match
{
isMethod
=
false
;
break
;
}
}
if
(
currentLuaParam
!=
nLuaParams
+
1
)
// Number of parameters does not match
using
System
;
using
System.IO
;
using
System.Collections
;
using
System.Reflection
;
using
System.Diagnostics
;
using
System.Collections.Generic
;
using
LuaWrap
;
using
System.Runtime.InteropServices
;
/*
* Functions used in the metatables of userdata representing
* CLR objects
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
class
MetaFunctions
{
/*
* __index metafunction for CLR objects. Implemented in Lua.
*/
internal
static
string
luaIndexFunction
=
"local function index(obj,name)\n"
+
" local meta=getmetatable(obj)\n"
+
" local cached=meta.cache[name]\n"
+
" if cached~=nil then\n"
+
" return cached\n"
+
" else\n"
+
" local value,isFunc=get_object_member(obj,name)\n"
+
" if isFunc then\n"
+
" meta.cache[name]=value\n"
+
" end\n"
+
" return value\n"
+
" end\n"
+
"end\n"
+
"return index"
;
private
ObjectTranslator
translator
;
private
Hashtable
memberCache
=
new
Hashtable
();
internal
KopiLua
.
Lua
.
lua_CFunction
gcFunction
,
indexFunction
,
newindexFunction
,
baseIndexFunction
,
classIndexFunction
,
classNewindexFunction
,
execDelegateFunction
,
callConstructorFunction
,
toStringFunction
;
public
MetaFunctions
(
ObjectTranslator
translator
)
{
this
.
translator
=
translator
;
gcFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
collectObject
);
toStringFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
toString
);
indexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
getMethod
);
newindexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
setFieldOrProperty
);
baseIndexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
getBaseMethod
);
callConstructorFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
callConstructor
);
classIndexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
getClassMethod
);
classNewindexFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
setClassFieldOrProperty
);
execDelegateFunction
=
new
KopiLua
.
Lua
.
lua_CFunction
(
this
.
runFunctionDelegate
);
}
/*
* __call metafunction of CLR delegates, retrieves and calls the delegate.
*/
private
int
runFunctionDelegate
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
KopiLua
.
Lua
.
lua_CFunction
func
=
(
KopiLua
.
Lua
.
lua_CFunction
)
translator
.
getRawNetObject
(
luaState
,
1
);
KopiLua
.
Lua
.
lua_remove
(
luaState
,
1
);
return
func
(
luaState
);
}
/*
* __gc metafunction of CLR objects.
*/
private
int
collectObject
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
int
udata
=
LuaLib
.
luanet_rawnetobj
(
luaState
,
1
);
if
(
udata
!=
-
1
)
{
translator
.
collectObject
(
udata
);
}
else
{
// Debug.WriteLine("not found: " + udata);
}
return
0
;
}
/*
* __tostring metafunction of CLR objects.
*/
private
int
toString
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
!=
null
)
{
translator
.
push
(
luaState
,
obj
.
ToString
()
+
": "
+
obj
.
GetHashCode
());
}
else
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
/// <summary>
/// Debug tool to dump the lua stack
/// </summary>
/// FIXME, move somewhere else
public
static
void
dumpStack
(
ObjectTranslator
translator
,
KopiLua
.
Lua
.
lua_State
luaState
)
{
int
depth
=
KopiLua
.
Lua
.
lua_gettop
(
luaState
);
Debug
.
WriteLine
(
"lua stack depth: "
+
depth
);
for
(
int
i
=
1
;
i
<=
depth
;
i
++)
{
LuaType
type
=
KopiLua
.
Lua
.
lua_type
(
luaState
,
i
).
ToLuaType
();
// we dump stacks when deep in calls, calling typename while the stack is in flux can fail sometimes, so manually check for key types
string
typestr
=
(
type
==
LuaType
.
Table
)
?
"table"
:
KopiLua
.
Lua
.
lua_typename
(
luaState
,
Convert
.
ToInt32
(
type
)).
ToString
();
string
strrep
=
KopiLua
.
Lua
.
lua_tostring
(
luaState
,
i
).
ToString
();
if
(
type
==
LuaType
.
UserData
)
{
object
obj
=
translator
.
getRawNetObject
(
luaState
,
i
);
strrep
=
obj
.
ToString
();
}
Debug
.
Print
(
"{0}: ({1}) {2}"
,
i
,
typestr
,
strrep
);
}
}
/*
* Called by the __index metafunction of CLR objects in case the
* method is not cached or it is a field/property/event.
* Receives the object and the member name as arguments and returns
* either the value of the member or a delegate to call it.
* If the member does not exist returns nil.
*/
private
int
getMethod
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
)
{
translator
.
throwError
(
luaState
,
"trying to index an invalid object reference"
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
object
index
=
translator
.
getObject
(
luaState
,
2
);
//Type indexType = index.GetType();
string
methodName
=
index
as
string
;
// will be null if not a string arg
Type
objType
=
obj
.
GetType
();
// Handle the most common case, looking up the method by name
if
(
methodName
!=
null
&&
isMemberPresent
(
objType
,
methodName
))
return
getMember
(
luaState
,
objType
,
obj
,
methodName
,
BindingFlags
.
Instance
);
// Try to access by array if the type is right and index is an int (lua numbers always come across as double)
if
(
objType
.
IsArray
&&
index
is
double
)
{
object
[]
arr
=
(
object
[])
obj
;
translator
.
push
(
luaState
,
arr
[(
int
)((
double
)
index
)]);
}
else
{
// Try to use get_Item to index into this .net object
MethodInfo
getter
=
objType
.
GetMethod
(
"get_Item"
);
ParameterInfo
[]
actualParms
=
(
getter
!=
null
)
?
getter
.
GetParameters
()
:
null
;
if
(
actualParms
==
null
||
actualParms
.
Length
!=
1
)
{
translator
.
throwError
(
luaState
,
"method not found (or no indexer): "
+
index
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
else
{
// Get the index in a form acceptable to the getter
index
=
translator
.
getAsType
(
luaState
,
2
,
actualParms
[
0
].
ParameterType
);
object
[]
args
=
new
object
[
1
];
// Just call the indexer - if out of bounds an exception will happen
args
[
0
]
=
index
;
try
{
object
result
=
getter
.
Invoke
(
obj
,
args
);
translator
.
push
(
luaState
,
result
);
}
catch
(
TargetInvocationException
e
)
{
// Provide a more readable description for the common case of key not found
if
(
e
.
InnerException
is
KeyNotFoundException
)
translator
.
throwError
(
luaState
,
"key '"
+
index
+
"' not found "
);
else
translator
.
throwError
(
luaState
,
"exception indexing '"
+
index
+
"' "
+
e
.
Message
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
}
}
KopiLua
.
Lua
.
lua_pushboolean
(
luaState
,
0
);
return
2
;
}
/*
* __index metafunction of base classes (the base field of Lua tables).
* Adds a prefix to the method name to call the base version of the method.
*/
private
int
getBaseMethod
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
)
{
translator
.
throwError
(
luaState
,
"trying to index an invalid object reference"
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
KopiLua
.
Lua
.
lua_pushboolean
(
luaState
,
0
);
return
2
;
}
string
methodName
=
KopiLua
.
Lua
.
lua_tostring
(
luaState
,
2
).
ToString
();
if
(
methodName
==
null
)
{
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
KopiLua
.
Lua
.
lua_pushboolean
(
luaState
,
0
);
return
2
;
}
getMember
(
luaState
,
obj
.
GetType
(),
obj
,
"__luaInterface_base_"
+
methodName
,
BindingFlags
.
Instance
);
KopiLua
.
Lua
.
lua_settop
(
luaState
,
-
2
);
if
(
KopiLua
.
Lua
.
lua_type
(
luaState
,
-
1
).
ToLuaType
()
==
LuaType
.
Nil
)
{
KopiLua
.
Lua
.
lua_settop
(
luaState
,
-
2
);
return
getMember
(
luaState
,
obj
.
GetType
(),
obj
,
methodName
,
BindingFlags
.
Instance
);
}
KopiLua
.
Lua
.
lua_pushboolean
(
luaState
,
0
);
return
2
;
}
/// <summary>
/// Does this method exist as either an instance or static?
/// </summary>
/// <param name="objType"></param>
/// <param name="methodName"></param>
/// <returns></returns>
bool
isMemberPresent
(
IReflect
objType
,
string
methodName
)
{
object
cachedMember
=
checkMemberCache
(
memberCache
,
objType
,
methodName
);
if
(
cachedMember
!=
null
)
return
true
;
MemberInfo
[]
members
=
objType
.
GetMember
(
methodName
,
BindingFlags
.
Static
|
BindingFlags
.
Instance
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
return
(
members
.
Length
>
0
);
}
/*
* Pushes the value of a member or a delegate to call it, depending on the type of
* the member. Works with static or instance members.
* Uses reflection to find members, and stores the reflected MemberInfo object in
* a cache (indexed by the type of the object and the name of the member).
*/
private
int
getMember
(
KopiLua
.
Lua
.
lua_State
luaState
,
IReflect
objType
,
object
obj
,
string
methodName
,
BindingFlags
bindingType
)
{
bool
implicitStatic
=
false
;
MemberInfo
member
=
null
;
object
cachedMember
=
checkMemberCache
(
memberCache
,
objType
,
methodName
);
//object cachedMember=null;
if
(
cachedMember
is
KopiLua
.
Lua
.
lua_CFunction
)
{
translator
.
pushFunction
(
luaState
,
(
KopiLua
.
Lua
.
lua_CFunction
)
cachedMember
);
translator
.
push
(
luaState
,
true
);
return
2
;
}
else
if
(
cachedMember
!=
null
)
{
member
=
(
MemberInfo
)
cachedMember
;
}
else
{
MemberInfo
[]
members
=
objType
.
GetMember
(
methodName
,
bindingType
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
if
(
members
.
Length
>
0
)
member
=
members
[
0
];
else
{
// If we can't find any suitable instance members, try to find them as statics - but we only want to allow implicit static
// lookups for fields/properties/events -kevinh
members
=
objType
.
GetMember
(
methodName
,
bindingType
|
BindingFlags
.
Static
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
if
(
members
.
Length
>
0
)
{
member
=
members
[
0
];
implicitStatic
=
true
;
}
}
}
if
(
member
!=
null
)
{
if
(
member
.
MemberType
==
MemberTypes
.
Field
)
{
FieldInfo
field
=
(
FieldInfo
)
member
;
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
member
);
try
{
translator
.
push
(
luaState
,
field
.
GetValue
(
obj
));
}
catch
{
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
}
else
if
(
member
.
MemberType
==
MemberTypes
.
Property
)
{
PropertyInfo
property
=
(
PropertyInfo
)
member
;
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
member
);
try
{
object
val
=
property
.
GetValue
(
obj
,
null
);
translator
.
push
(
luaState
,
val
);
}
catch
(
ArgumentException
)
{
// If we can't find the getter in our class, recurse up to the base class and see
// if they can help.
if
(
objType
is
Type
&&
!(((
Type
)
objType
)
==
typeof
(
object
)))
return
getMember
(
luaState
,
((
Type
)
objType
).
BaseType
,
obj
,
methodName
,
bindingType
);
else
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
catch
(
TargetInvocationException
e
)
// Convert this exception into a Lua error
{
ThrowError
(
luaState
,
e
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
}
else
if
(
member
.
MemberType
==
MemberTypes
.
Event
)
{
EventInfo
eventInfo
=
(
EventInfo
)
member
;
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
member
);
translator
.
push
(
luaState
,
new
RegisterEventHandler
(
translator
.
pendingEvents
,
obj
,
eventInfo
));
}
else
if
(!
implicitStatic
)
{
if
(
member
.
MemberType
==
MemberTypes
.
NestedType
)
{
// kevinh - added support for finding nested types
// cache us
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
member
);
// Find the name of our class
string
name
=
member
.
Name
;
Type
dectype
=
member
.
DeclaringType
;
// Build a new long name and try to find the type by name
string
longname
=
dectype
.
FullName
+
"+"
+
name
;
Type
nestedType
=
translator
.
FindType
(
longname
);
translator
.
pushType
(
luaState
,
nestedType
);
}
else
{
// Member type must be 'method'
KopiLua
.
Lua
.
lua_CFunction
wrapper
=
new
KopiLua
.
Lua
.
lua_CFunction
((
new
LuaMethodWrapper
(
translator
,
objType
,
methodName
,
bindingType
)).
call
);
if
(
cachedMember
==
null
)
setMemberCache
(
memberCache
,
objType
,
methodName
,
wrapper
);
translator
.
pushFunction
(
luaState
,
wrapper
);
translator
.
push
(
luaState
,
true
);
return
2
;
}
}
else
{
// If we reach this point we found a static method, but can't use it in this context because the user passed in an instance
translator
.
throwError
(
luaState
,
"can't pass instance to static method "
+
methodName
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
}
else
{
// kevinh - we want to throw an exception because meerly returning 'nil' in this case
// is not sufficient. valid data members may return nil and therefore there must be some
// way to know the member just doesn't exist.
translator
.
throwError
(
luaState
,
"unknown member name "
+
methodName
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
// push false because we are NOT returning a function (see luaIndexFunction)
translator
.
push
(
luaState
,
false
);
return
2
;
}
/*
* Checks if a MemberInfo object is cached, returning it or null.
*/
private
object
checkMemberCache
(
Hashtable
memberCache
,
IReflect
objType
,
string
memberName
)
{
Hashtable
members
=
(
Hashtable
)
memberCache
[
objType
];
if
(
members
!=
null
)
return
members
[
memberName
];
else
return
null
;
}
/*
* Stores a MemberInfo object in the member cache.
*/
private
void
setMemberCache
(
Hashtable
memberCache
,
IReflect
objType
,
string
memberName
,
object
member
)
{
Hashtable
members
=
(
Hashtable
)
memberCache
[
objType
];
if
(
members
==
null
)
{
members
=
new
Hashtable
();
memberCache
[
objType
]
=
members
;
}
members
[
memberName
]
=
member
;
}
/*
* __newindex metafunction of CLR objects. Receives the object,
* the member name and the value to be stored as arguments. Throws
* and error if the assignment is invalid.
*/
private
int
setFieldOrProperty
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
object
target
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
target
==
null
)
{
translator
.
throwError
(
luaState
,
"trying to index and invalid object reference"
);
return
0
;
}
Type
type
=
target
.
GetType
();
// First try to look up the parameter as a property name
string
detailMessage
;
bool
didMember
=
trySetMember
(
luaState
,
type
,
target
,
BindingFlags
.
Instance
,
out
detailMessage
);
if
(
didMember
)
return
0
;
// Must have found the property name
// We didn't find a property name, now see if we can use a [] style this accessor to set array contents
try
{
if
(
type
.
IsArray
&&
KopiLua
.
Lua
.
lua_isnumber
(
luaState
,
2
).
ToBoolean
())
{
int
index
=
(
int
)
KopiLua
.
Lua
.
lua_tonumber
(
luaState
,
2
);
Array
arr
=
(
Array
)
target
;
object
val
=
translator
.
getAsType
(
luaState
,
3
,
arr
.
GetType
().
GetElementType
());
arr
.
SetValue
(
val
,
index
);
}
else
{
// Try to see if we have a this[] accessor
MethodInfo
setter
=
type
.
GetMethod
(
"set_Item"
);
if
(
setter
!=
null
)
{
ParameterInfo
[]
args
=
setter
.
GetParameters
();
Type
valueType
=
args
[
1
].
ParameterType
;
// The new val ue the user specified
object
val
=
translator
.
getAsType
(
luaState
,
3
,
valueType
);
Type
indexType
=
args
[
0
].
ParameterType
;
object
index
=
translator
.
getAsType
(
luaState
,
2
,
indexType
);
object
[]
methodArgs
=
new
object
[
2
];
// Just call the indexer - if out of bounds an exception will happen
methodArgs
[
0
]
=
index
;
methodArgs
[
1
]
=
val
;
setter
.
Invoke
(
target
,
methodArgs
);
}
else
{
translator
.
throwError
(
luaState
,
detailMessage
);
// Pass the original message from trySetMember because it is probably best
}
}
}
catch
(
SEHException
)
{
// If we are seeing a C++ exception - this must actually be for Lua's private use. Let it handle it
throw
;
}
catch
(
Exception
e
)
{
ThrowError
(
luaState
,
e
);
}
return
0
;
}
/// <summary>
/// Tries to set a named property or field
/// </summary>
/// <param name="luaState"></param>
/// <param name="targetType"></param>
/// <param name="target"></param>
/// <param name="bindingType"></param>
/// <returns>false if unable to find the named member, true for success</returns>
private
bool
trySetMember
(
KopiLua
.
Lua
.
lua_State
luaState
,
IReflect
targetType
,
object
target
,
BindingFlags
bindingType
,
out
string
detailMessage
)
{
detailMessage
=
null
;
// No error yet
// If not already a string just return - we don't want to call tostring - which has the side effect of
// changing the lua typecode to string
// Note: We don't use isstring because the standard lua C isstring considers either strings or numbers to
// be true for isstring.
if
(
KopiLua
.
Lua
.
lua_type
(
luaState
,
2
).
ToLuaType
()
!=
LuaType
.
String
)
{
detailMessage
=
"property names must be strings"
;
return
false
;
}
// We only look up property names by string
string
fieldName
=
KopiLua
.
Lua
.
lua_tostring
(
luaState
,
2
).
ToString
();
if
(
fieldName
==
null
||
fieldName
.
Length
<
1
||
!(
char
.
IsLetter
(
fieldName
[
0
])
||
fieldName
[
0
]
==
'_'
))
{
detailMessage
=
"invalid property name"
;
return
false
;
}
// Find our member via reflection or the cache
MemberInfo
member
=
(
MemberInfo
)
checkMemberCache
(
memberCache
,
targetType
,
fieldName
);
if
(
member
==
null
)
{
MemberInfo
[]
members
=
targetType
.
GetMember
(
fieldName
,
bindingType
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
if
(
members
.
Length
>
0
)
{
member
=
members
[
0
];
setMemberCache
(
memberCache
,
targetType
,
fieldName
,
member
);
}
else
{
detailMessage
=
"field or property '"
+
fieldName
+
"' does not exist"
;
return
false
;
}
}
if
(
member
.
MemberType
==
MemberTypes
.
Field
)
{
FieldInfo
field
=
(
FieldInfo
)
member
;
object
val
=
translator
.
getAsType
(
luaState
,
3
,
field
.
FieldType
);
try
{
field
.
SetValue
(
target
,
val
);
}
catch
(
Exception
e
)
{
ThrowError
(
luaState
,
e
);
}
// We did a call
return
true
;
}
else
if
(
member
.
MemberType
==
MemberTypes
.
Property
)
{
PropertyInfo
property
=
(
PropertyInfo
)
member
;
object
val
=
translator
.
getAsType
(
luaState
,
3
,
property
.
PropertyType
);
try
{
property
.
SetValue
(
target
,
val
,
null
);
}
catch
(
Exception
e
)
{
ThrowError
(
luaState
,
e
);
}
// We did a call
return
true
;
}
detailMessage
=
"'"
+
fieldName
+
"' is not a .net field or property"
;
return
false
;
}
/*
* Writes to fields or properties, either static or instance. Throws an error
* if the operation is invalid.
*/
private
int
setMember
(
KopiLua
.
Lua
.
lua_State
luaState
,
IReflect
targetType
,
object
target
,
BindingFlags
bindingType
)
{
string
detail
;
bool
success
=
trySetMember
(
luaState
,
targetType
,
target
,
bindingType
,
out
detail
);
if
(!
success
)
translator
.
throwError
(
luaState
,
detail
);
return
0
;
}
/// <summary>
/// Convert a C# exception into a Lua error
/// </summary>
/// <param name="e"></param>
/// We try to look into the exception to give the most meaningful description
void
ThrowError
(
KopiLua
.
Lua
.
lua_State
luaState
,
Exception
e
)
{
// If we got inside a reflection show what really happened
TargetInvocationException
te
=
e
as
TargetInvocationException
;
if
(
te
!=
null
)
e
=
te
.
InnerException
;
translator
.
throwError
(
luaState
,
e
);
}
/*
* __index metafunction of type references, works on static members.
*/
private
int
getClassMethod
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
IReflect
klass
;
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
||
!(
obj
is
IReflect
))
{
translator
.
throwError
(
luaState
,
"trying to index an invalid type reference"
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
else
klass
=
(
IReflect
)
obj
;
if
(
KopiLua
.
Lua
.
lua_isnumber
(
luaState
,
2
).
ToBoolean
())
{
int
size
=
(
int
)
KopiLua
.
Lua
.
lua_tonumber
(
luaState
,
2
);
translator
.
push
(
luaState
,
Array
.
CreateInstance
(
klass
.
UnderlyingSystemType
,
size
));
return
1
;
}
else
{
string
methodName
=
KopiLua
.
Lua
.
lua_tostring
(
luaState
,
2
).
ToString
();
if
(
methodName
==
null
)
{
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
else
return
getMember
(
luaState
,
klass
,
null
,
methodName
,
BindingFlags
.
FlattenHierarchy
|
BindingFlags
.
Static
);
}
}
/*
* __newindex function of type references, works on static members.
*/
private
int
setClassFieldOrProperty
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
IReflect
target
;
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
||
!(
obj
is
IReflect
))
{
translator
.
throwError
(
luaState
,
"trying to index an invalid type reference"
);
return
0
;
}
else
target
=
(
IReflect
)
obj
;
return
setMember
(
luaState
,
target
,
null
,
BindingFlags
.
FlattenHierarchy
|
BindingFlags
.
Static
);
}
/*
* __call metafunction of type references. Searches for and calls
* a constructor for the type. Returns nil if the constructor is not
* found or if the arguments are invalid. Throws an error if the constructor
* generates an exception.
*/
private
int
callConstructor
(
KopiLua
.
Lua
.
lua_State
luaState
)
{
MethodCache
validConstructor
=
new
MethodCache
();
IReflect
klass
;
object
obj
=
translator
.
getRawNetObject
(
luaState
,
1
);
if
(
obj
==
null
||
!(
obj
is
IReflect
))
{
translator
.
throwError
(
luaState
,
"trying to call constructor on an invalid type reference"
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
else
klass
=
(
IReflect
)
obj
;
KopiLua
.
Lua
.
lua_remove
(
luaState
,
1
);
ConstructorInfo
[]
constructors
=
klass
.
UnderlyingSystemType
.
GetConstructors
();
foreach
(
ConstructorInfo
constructor
in
constructors
)
{
bool
isConstructor
=
matchParameters
(
luaState
,
constructor
,
ref
validConstructor
);
if
(
isConstructor
)
{
try
{
translator
.
push
(
luaState
,
constructor
.
Invoke
(
validConstructor
.
args
));
}
catch
(
TargetInvocationException
e
)
{
ThrowError
(
luaState
,
e
);
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
catch
{
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
}
return
1
;
}
}
string
constructorName
=
(
constructors
.
Length
==
0
)
?
"unknown"
:
constructors
[
0
].
Name
;
translator
.
throwError
(
luaState
,
String
.
Format
(
"{0} does not contain constructor({1}) argument match"
,
klass
.
UnderlyingSystemType
,
constructorName
));
KopiLua
.
Lua
.
lua_pushnil
(
luaState
);
return
1
;
}
/*
* Matches a method against its arguments in the Lua stack. Returns
* if the match was succesful. It it was also returns the information
* necessary to invoke the method.
*/
internal
bool
matchParameters
(
KopiLua
.
Lua
.
lua_State
luaState
,
MethodBase
method
,
ref
MethodCache
methodCache
)
{
ExtractValue
extractValue
;
bool
isMethod
=
true
;
ParameterInfo
[]
paramInfo
=
method
.
GetParameters
();
int
currentLuaParam
=
1
;
int
nLuaParams
=
KopiLua
.
Lua
.
lua_gettop
(
luaState
);
ArrayList
paramList
=
new
ArrayList
();
List
<
int
>
outList
=
new
List
<
int
>();
List
<
MethodArgs
>
argTypes
=
new
List
<
MethodArgs
>();
foreach
(
ParameterInfo
currentNetParam
in
paramInfo
)
{
if
(!
currentNetParam
.
IsIn
&&
currentNetParam
.
IsOut
)
// Skips out params
{
outList
.
Add
(
paramList
.
Add
(
null
));
}
else
if
(
currentLuaParam
>
nLuaParams
)
// Adds optional parameters
{
if
(
currentNetParam
.
IsOptional
)
{
paramList
.
Add
(
currentNetParam
.
DefaultValue
);
}
else
{
isMethod
=
false
;
break
;
}
}
else
if
((
extractValue
=
translator
.
typeChecker
.
checkType
(
luaState
,
currentLuaParam
,
currentNetParam
.
ParameterType
))
!=
null
)
// Type checking
{
int
index
=
paramList
.
Add
(
extractValue
(
luaState
,
currentLuaParam
));
MethodArgs
methodArg
=
new
MethodArgs
();
methodArg
.
index
=
index
;
methodArg
.
extractValue
=
extractValue
;
argTypes
.
Add
(
methodArg
);
if
(
currentNetParam
.
ParameterType
.
IsByRef
)
outList
.
Add
(
index
);
currentLuaParam
++;
}
// Type does not match, ignore if the parameter is optional
else
if
(
currentNetParam
.
IsOptional
)
{
paramList
.
Add
(
currentNetParam
.
DefaultValue
);
}
else
// No match
{
isMethod
=
false
;
break
;
}
}
if
(
currentLuaParam
!=
nLuaParams
+
1
)
// Number of parameters does not match
isMethod
=
false
;
if
(
isMethod
)
{
methodCache
.
args
=
paramList
.
ToArray
();
methodCache
.
cachedMethod
=
method
;
methodCache
.
outList
=
outList
.
ToArray
();
methodCache
.
argTypes
=
argTypes
.
ToArray
();
}
return
isMethod
;
}
}
if
(
isMethod
)
{
methodCache
.
args
=
paramList
.
ToArray
();
methodCache
.
cachedMethod
=
method
;
methodCache
.
outList
=
outList
.
ToArray
();
methodCache
.
argTypes
=
argTypes
.
ToArray
();
}
return
isMethod
;
}
}
}
\ No newline at end of file
LuaInterface/MethodWrapper.cs
View file @
00eddcac
...
...
@@ -106,13 +106,15 @@ namespace LuaInterface
object
targetObject
=
target
;
bool
failedCall
=
true
;
int
nReturnValues
=
0
;
Console
.
WriteLine
(
"asyyyyyyyyyyyyyy"
);
if
(!
KopiLua
.
Lua
.
lua_checkstack
(
luaState
,
5
).
ToBoolean
())
throw
new
LuaException
(
"Lua stack overflow"
);
Console
.
WriteLine
(
"asyyyyyyyyyyyyyy1"
);
bool
isStatic
=
(
bindingType
&
BindingFlags
.
Static
)
==
BindingFlags
.
Static
;
Console
.
WriteLine
(
"asyyyyyyyyyyyyyy2"
);
SetPendingException
(
null
);
Console
.
WriteLine
(
"asyyyyyyyyyyyyyy3"
);
Console
.
WriteLine
(
methodToCall
==
null
);
if
(
methodToCall
==
null
)
// Method from name
{
if
(
isStatic
)
...
...
@@ -120,6 +122,9 @@ namespace LuaInterface
else
targetObject
=
extractTarget
(
luaState
,
1
);
//KopiLua.Lua.lua_remove(luaState,1); // Pops the receiver
Console
.
WriteLine
(
"asdxxxxxx"
);
Console
.
WriteLine
(
lastCalledMethod
.
cachedMethod
);
Console
.
WriteLine
(
lastCalledMethod
.
cachedMethod
!=
null
);
if
(
lastCalledMethod
.
cachedMethod
!=
null
)
// Cached?
{
int
numStackToSkip
=
isStatic
?
0
:
1
;
// If this is an instance invoe we will have an extra arg on the stack for the targetObject
...
...
@@ -131,6 +136,8 @@ namespace LuaInterface
throw
new
LuaException
(
"Lua stack overflow"
);
try
{
Console
.
WriteLine
(
"0"
);
Console
.
WriteLine
(
lastCalledMethod
.
argTypes
.
Length
);
for
(
int
i
=
0
;
i
<
lastCalledMethod
.
argTypes
.
Length
;
i
++)
{
lastCalledMethod
.
args
[
lastCalledMethod
.
argTypes
[
i
].
index
]=
...
...
@@ -142,12 +149,14 @@ namespace LuaInterface
throw
new
LuaException
(
"argument number "
+(
i
+
1
)+
" is invalid"
);
}
}
Console
.
WriteLine
(
"asd"
);
if
((
bindingType
&
BindingFlags
.
Static
)==
BindingFlags
.
Static
)
{
translator
.
push
(
luaState
,
lastCalledMethod
.
cachedMethod
.
Invoke
(
null
,
lastCalledMethod
.
args
));
}
else
{
Console
.
WriteLine
(
"asd2"
);
if
(
lastCalledMethod
.
cachedMethod
.
IsConstructor
)
translator
.
push
(
luaState
,((
ConstructorInfo
)
lastCalledMethod
.
cachedMethod
).
Invoke
(
lastCalledMethod
.
args
));
else
...
...
LuaInterface/ObjectTranslator.cs
View file @
00eddcac
...
...
@@ -618,6 +618,7 @@ namespace LuaInterface
if
(
udata
!=-
1
)
return
objects
[
udata
];
else
//return null;
return
getUserData
(
luaState
,
index
);
}
default
:
...
...
@@ -749,7 +750,6 @@ namespace LuaInterface
{
// Make sure we are _really_ ILuaGenerated
Type
typ
=
o
.
GetType
();
return
(
typ
.
GetInterface
(
"ILuaGeneratedType"
)
!=
null
);
}
else
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment