/* * Tencent is pleased to support the open source community by making xLua available. * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at * http://opensource.org/licenses/MIT * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #if USE_UNI_LUA using LuaAPI = UniLua.Lua; using RealStatePtr = UniLua.ILuaState; using LuaCSFunction = UniLua.CSharpFunctionDelegate; #else using LuaAPI = XLua.LuaDLL.Lua; using RealStatePtr = System.IntPtr; using LuaCSFunction = XLua.LuaDLL.lua_CSFunction; #endif namespace XLua { using System; using System.Collections; using System.Reflection; using System.Collections.Generic; using System.Diagnostics; using System.Linq; class ReferenceEqualsComparer : IEqualityComparer { public new bool Equals(object o1, object o2) { return object.ReferenceEquals(o1, o2); } public int GetHashCode(object obj) { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); } } #pragma warning disable 414 public class MonoPInvokeCallbackAttribute : System.Attribute { private Type type; public MonoPInvokeCallbackAttribute(Type t) { type = t; } } #pragma warning restore 414 public enum LuaTypes { LUA_TNONE = -1, LUA_TNIL = 0, LUA_TNUMBER = 3, LUA_TSTRING = 4, LUA_TBOOLEAN = 1, LUA_TTABLE = 5, LUA_TFUNCTION = 6, LUA_TUSERDATA = 7, LUA_TTHREAD = 8, LUA_TLIGHTUSERDATA = 2 } public enum LuaGCOptions { LUA_GCSTOP = 0, LUA_GCRESTART = 1, LUA_GCCOLLECT = 2, LUA_GCCOUNT = 3, LUA_GCCOUNTB = 4, LUA_GCSTEP = 5, LUA_GCSETPAUSE = 6, LUA_GCSETSTEPMUL = 7, } public enum LuaThreadStatus { LUA_RESUME_ERROR = -1, LUA_OK = 0, LUA_YIELD = 1, LUA_ERRRUN = 2, LUA_ERRSYNTAX = 3, LUA_ERRMEM = 4, LUA_ERRERR = 5, } sealed class LuaIndexes { public static int LUA_REGISTRYINDEX { get { return InternalGlobals.LUA_REGISTRYINDEX; } set { InternalGlobals.LUA_REGISTRYINDEX = value; } } } #if GEN_CODE_MINIMIZE public delegate int CSharpWrapper(IntPtr L, int top); #endif public partial class ObjectTranslator { internal MethodWrapsCache methodWrapsCache; internal ObjectCheckers objectCheckers; internal ObjectCasters objectCasters; internal readonly ObjectPool objects = new ObjectPool(); internal readonly Dictionary reverseMap = new Dictionary(new ReferenceEqualsComparer()); internal LuaEnv luaEnv; internal StaticLuaCallbacks metaFunctions; internal List assemblies; private LuaCSFunction importTypeFunction,loadAssemblyFunction, castFunction; //延迟加载 private readonly Dictionary> delayWrap = new Dictionary>(); private readonly Dictionary> interfaceBridgeCreators = new Dictionary>(); //无法访问的类,比如声明成internal,可以用其接口、基类的生成代码来访问 private readonly Dictionary aliasCfg = new Dictionary(); public void DelayWrapLoader(Type type, Action loader) { delayWrap[type] = loader; } public void AddInterfaceBridgeCreator(Type type, Func creator) { interfaceBridgeCreators.Add(type, creator); } Dictionary loaded_types = new Dictionary(); public bool TryDelayWrapLoader(RealStatePtr L, Type type) { if (loaded_types.ContainsKey(type)) return true; loaded_types.Add(type, true); LuaAPI.luaL_newmetatable(L, type.FullName); //先建一个metatable,因为加载过程可能会需要用到 LuaAPI.lua_pop(L, 1); Action loader; int top = LuaAPI.lua_gettop(L); if (delayWrap.TryGetValue(type, out loader)) { delayWrap.Remove(type); loader(L); } else { #if !GEN_CODE_MINIMIZE && !ENABLE_IL2CPP && (UNITY_EDITOR || XLUA_GENERAL) && !FORCE_REFLECTION && !NET_STANDARD_2_0 if (!DelegateBridge.Gen_Flag && !type.IsEnum() && !typeof(Delegate).IsAssignableFrom(type) && Utils.IsPublic(type)) { Type wrap = ce.EmitTypeWrap(type); MethodInfo method = wrap.GetMethod("__Register", BindingFlags.Static | BindingFlags.Public); method.Invoke(null, new object[] { L }); } else { Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type)); } #else Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type)); #endif #if NOT_GEN_WARNING if (!typeof(Delegate).IsAssignableFrom(type)) { #if !XLUA_GENERAL UnityEngine.Debug.LogWarning(string.Format("{0} not gen, using reflection instead", type)); #else System.Console.WriteLine(string.Format("Warning: {0} not gen, using reflection instead", type)); #endif } #endif } if (top != LuaAPI.lua_gettop(L)) { throw new Exception("top change, before:" + top + ", after:" + LuaAPI.lua_gettop(L)); } foreach (var nested_type in type.GetNestedTypes(BindingFlags.Public)) { if (nested_type.IsGenericTypeDefinition()) { continue; } GetTypeId(L, nested_type); } return true; } public void Alias(Type type, string alias) { Type alias_type = FindType(alias); if (alias_type == null) { throw new ArgumentException("Can not find " + alias); } aliasCfg[alias_type] = type; } public int cacheRef; void addAssemblieByName(IEnumerable assemblies_usorted, string name) { foreach(var assemblie in assemblies_usorted) { if (assemblie.FullName.StartsWith(name) && !assemblies.Contains(assemblie)) { assemblies.Add(assemblie); break; } } } public ObjectTranslator(LuaEnv luaenv,RealStatePtr L) { #if XLUA_GENERAL || (UNITY_WSA && !UNITY_EDITOR) var dumb_field = typeof(ObjectTranslator).GetField("s_gen_reg_dumb_obj", BindingFlags.Static| BindingFlags.DeclaredOnly | BindingFlags.NonPublic); if (dumb_field != null) { dumb_field.GetValue(null); } #endif assemblies = new List(); #if (UNITY_WSA && !ENABLE_IL2CPP) && !UNITY_EDITOR var assemblies_usorted = Utils.GetAssemblies(); #else assemblies.Add(Assembly.GetExecutingAssembly()); var assemblies_usorted = AppDomain.CurrentDomain.GetAssemblies(); #endif addAssemblieByName(assemblies_usorted, "mscorlib,"); addAssemblieByName(assemblies_usorted, "System,"); addAssemblieByName(assemblies_usorted, "System.Core,"); foreach (Assembly assembly in assemblies_usorted) { if (!assemblies.Contains(assembly)) { assemblies.Add(assembly); } } this.luaEnv=luaenv; objectCasters = new ObjectCasters(this); objectCheckers = new ObjectCheckers(this); methodWrapsCache = new MethodWrapsCache(this, objectCheckers, objectCasters); metaFunctions=new StaticLuaCallbacks(); importTypeFunction = new LuaCSFunction(StaticLuaCallbacks.ImportType); loadAssemblyFunction = new LuaCSFunction(StaticLuaCallbacks.LoadAssembly); castFunction = new LuaCSFunction(StaticLuaCallbacks.Cast); LuaAPI.lua_newtable(L); LuaAPI.lua_newtable(L); LuaAPI.xlua_pushasciistring(L, "__mode"); LuaAPI.xlua_pushasciistring(L, "v"); LuaAPI.lua_rawset(L, -3); LuaAPI.lua_setmetatable(L, -2); cacheRef = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); initCSharpCallLua(); } internal enum LOGLEVEL{ NO, INFO, WARN, ERROR } #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0 Type delegate_birdge_type; class CompareByArgRet : IEqualityComparer { public bool Equals(MethodInfo x, MethodInfo y) { return Utils.IsParamsMatch(x, y); } public int GetHashCode(MethodInfo method) { int hc = 0; hc += method.ReturnType.GetHashCode(); foreach (var pi in method.GetParameters()) { hc += pi.ParameterType.GetHashCode(); } return hc; } } #endif void initCSharpCallLua() { #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0 delegate_birdge_type = typeof(DelegateBridge); if (!DelegateBridge.Gen_Flag) { List cs_call_lua = new List(); foreach (var type in Utils.GetAllTypes()) { if(type.IsDefined(typeof(CSharpCallLuaAttribute), false)) { cs_call_lua.Add(type); } if (!type.IsAbstract || !type.IsSealed) continue; var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); for (int i = 0; i < fields.Length; i++) { var field = fields[i]; if (field.IsDefined(typeof(CSharpCallLuaAttribute), false) && (typeof(IEnumerable)).IsAssignableFrom(field.FieldType)) { cs_call_lua.AddRange(field.GetValue(null) as IEnumerable); } } var props = type.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); for (int i = 0; i < props.Length; i++) { var prop = props[i]; if (prop.IsDefined(typeof(CSharpCallLuaAttribute), false) && (typeof(IEnumerable)).IsAssignableFrom(prop.PropertyType)) { cs_call_lua.AddRange(prop.GetValue(null, null) as IEnumerable); } } } IEnumerable> groups = (from type in cs_call_lua where typeof(Delegate).IsAssignableFrom(type) && type != typeof(Delegate) && type != typeof(MulticastDelegate) where !type.GetMethod("Invoke").GetParameters().Any(paramInfo => paramInfo.ParameterType.IsGenericParameter) select type).GroupBy(t => t.GetMethod("Invoke"), new CompareByArgRet()); ce.SetGenInterfaces(cs_call_lua.Where(type=>type.IsInterface()).ToList()); delegate_birdge_type = ce.EmitDelegateImpl(groups); } #endif } #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0 CodeEmit ce = new CodeEmit(); #endif MethodInfo[] genericAction = null; MethodInfo[] genericFunc = null; Dictionary> delegateCreatorCache = new Dictionary>(); Func getCreatorUsingGeneric(DelegateBridgeBase bridge, Type delegateType, MethodInfo delegateMethod) { Func genericDelegateCreator = null; if (genericAction == null) { var methods = typeof(DelegateBridge).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); genericAction = methods.Where(m => m.Name == "Action").OrderBy(m => m.GetParameters().Length).ToArray(); genericFunc = methods.Where(m => m.Name == "Func").OrderBy(m => m.GetParameters().Length).ToArray(); } if (genericAction.Length != 5 || genericFunc.Length != 5) { return null; } var parameters = delegateMethod.GetParameters(); #if !XLUA_GENERAL if ((delegateMethod.ReturnType.IsValueType() && delegateMethod.ReturnType != typeof(void)) || parameters.Length > 4) { genericDelegateCreator = (x) => null; } else #endif { foreach (var pinfo in parameters) { if (pinfo.ParameterType.IsValueType() || pinfo.IsOut || pinfo.ParameterType.IsByRef) { genericDelegateCreator = (x) => null; break; } } if (genericDelegateCreator == null) { var typeArgs = parameters.Select(pinfo => pinfo.ParameterType); MethodInfo genericMethodInfo = null; if (delegateMethod.ReturnType == typeof(void)) { genericMethodInfo = genericAction[parameters.Length]; } else { genericMethodInfo = genericFunc[parameters.Length]; typeArgs = typeArgs.Concat(new Type[] { delegateMethod.ReturnType }); } if (genericMethodInfo.IsGenericMethodDefinition) { var methodInfo = genericMethodInfo.MakeGenericMethod(typeArgs.ToArray()); genericDelegateCreator = (o) => #if !UNITY_WSA || UNITY_EDITOR Delegate.CreateDelegate(delegateType, o, methodInfo); #else methodInfo.CreateDelegate(delegateType, bridge); #endif } else { genericDelegateCreator = (o) => #if !UNITY_WSA || UNITY_EDITOR Delegate.CreateDelegate(delegateType, o, genericMethodInfo); #else genericMethodInfo.CreateDelegate(delegateType, o); #endif } } } return genericDelegateCreator; } Delegate getDelegate(DelegateBridgeBase bridge, Type delegateType) { Delegate ret = bridge.GetDelegateByType(delegateType); if (ret != null) { return ret; } if (delegateType == typeof(Delegate) || delegateType == typeof(MulticastDelegate)) { return null; } Func delegateCreator; if (!delegateCreatorCache.TryGetValue(delegateType, out delegateCreator)) { // get by parameters MethodInfo delegateMethod = delegateType.GetMethod("Invoke"); var methods = bridge.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(m => !m.IsGenericMethodDefinition && (m.Name.StartsWith("__Gen_Delegate_Imp") || m.Name == "Action")).ToArray(); for (int i = 0; i < methods.Length; i++) { if (!methods[i].IsConstructor && Utils.IsParamsMatch(delegateMethod, methods[i])) { var foundMethod = methods[i]; delegateCreator = (o) => #if !UNITY_WSA || UNITY_EDITOR Delegate.CreateDelegate(delegateType, o, foundMethod); #else foundMethod.CreateDelegate(delegateType, o); #endif break; } } if (delegateCreator == null) { delegateCreator = getCreatorUsingGeneric(bridge, delegateType, delegateMethod); } delegateCreatorCache.Add(delegateType, delegateCreator); } ret = delegateCreator(bridge); if (ret != null) { return ret; } throw new InvalidCastException("This type must add to CSharpCallLua: " + delegateType.GetFriendlyName()); } Dictionary delegate_bridges = new Dictionary(); public object CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx) { LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); if (!LuaAPI.lua_isnil(L, -1)) { int referenced = LuaAPI.xlua_tointeger(L, -1); LuaAPI.lua_pop(L, 1); if (delegate_bridges[referenced].IsAlive) { if (delegateType == null) { return delegate_bridges[referenced].Target; } DelegateBridgeBase exist_bridge = delegate_bridges[referenced].Target as DelegateBridgeBase; Delegate exist_delegate; if (exist_bridge.TryGetDelegate(delegateType, out exist_delegate)) { return exist_delegate; } else { exist_delegate = getDelegate(exist_bridge, delegateType); exist_bridge.AddDelegate(delegateType, exist_delegate); return exist_delegate; } } } else { LuaAPI.lua_pop(L, 1); } LuaAPI.lua_pushvalue(L, idx); int reference = LuaAPI.luaL_ref(L); LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_pushnumber(L, reference); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); DelegateBridgeBase bridge; try { #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0 if (!DelegateBridge.Gen_Flag) { bridge = Activator.CreateInstance(delegate_birdge_type, new object[] { reference, luaEnv }) as DelegateBridgeBase; } else #endif { bridge = new DelegateBridge(reference, luaEnv); } } catch(Exception e) { LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_pushnil(L); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.lua_pushnil(L); LuaAPI.xlua_rawseti(L, LuaIndexes.LUA_REGISTRYINDEX, reference); throw e; } if (delegateType == null) { delegate_bridges[reference] = new WeakReference(bridge); return bridge; } try { var ret = getDelegate(bridge, delegateType); bridge.AddDelegate(delegateType, ret); delegate_bridges[reference] = new WeakReference(bridge); return ret; } catch(Exception e) { bridge.Dispose(); throw e; } } public bool AllDelegateBridgeReleased() { foreach (var kv in delegate_bridges) { if (kv.Value.IsAlive) { return false; } } return true; } public void ReleaseLuaBase(RealStatePtr L, int reference, bool is_delegate) { if(is_delegate) { LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, reference); if (LuaAPI.lua_isnil(L, -1)) { LuaAPI.lua_pop(L, 1); } else { LuaAPI.lua_pushvalue(L, -1); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); if (LuaAPI.lua_type(L, -1) == LuaTypes.LUA_TNUMBER && LuaAPI.xlua_tointeger(L, -1) == reference) // { //UnityEngine.Debug.LogWarning("release delegate ref = " + luaReference); LuaAPI.lua_pop(L, 1);// pop LUA_REGISTRYINDEX[func] LuaAPI.lua_pushnil(L); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); // LUA_REGISTRYINDEX[func] = nil } else //another Delegate ref the function before the GC tick { LuaAPI.lua_pop(L, 2); // pop LUA_REGISTRYINDEX[func] & func } } LuaAPI.lua_unref(L, reference); delegate_bridges.Remove(reference); } else { LuaAPI.lua_unref(L, reference); } } public object CreateInterfaceBridge(RealStatePtr L, Type interfaceType, int idx) { Func creator; if (!interfaceBridgeCreators.TryGetValue(interfaceType, out creator)) { #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0 var bridgeType = ce.EmitInterfaceImpl(interfaceType); creator = (int reference, LuaEnv luaenv) => { return Activator.CreateInstance(bridgeType, new object[] { reference, luaEnv }) as LuaBase; }; interfaceBridgeCreators.Add(interfaceType, creator); #else throw new InvalidCastException("This type must add to CSharpCallLua: " + interfaceType); #endif } LuaAPI.lua_pushvalue(L, idx); return creator(LuaAPI.luaL_ref(L), luaEnv); } int common_array_meta = -1; public void CreateArrayMetatable(RealStatePtr L) { Utils.BeginObjectRegister(null, L, this, 0, 0, 1, 0, common_array_meta); Utils.RegisterFunc(L, Utils.GETTER_IDX, "Length", StaticLuaCallbacks.ArrayLength); Utils.EndObjectRegister(null, L, this, null, null, typeof(System.Array), StaticLuaCallbacks.ArrayIndexer, StaticLuaCallbacks.ArrayNewIndexer); } int common_delegate_meta = -1; public void CreateDelegateMetatable(RealStatePtr L) { Utils.BeginObjectRegister(null, L, this, 3, 0, 0, 0, common_delegate_meta); Utils.RegisterFunc(L, Utils.OBJ_META_IDX, "__call", StaticLuaCallbacks.DelegateCall); Utils.RegisterFunc(L, Utils.OBJ_META_IDX, "__add", StaticLuaCallbacks.DelegateCombine); Utils.RegisterFunc(L, Utils.OBJ_META_IDX, "__sub", StaticLuaCallbacks.DelegateRemove); Utils.EndObjectRegister(null, L, this, null, null, typeof(System.MulticastDelegate), null, null); } int enumerable_pairs_func = -1; internal void CreateEnumerablePairs(RealStatePtr L) { LuaFunction func = luaEnv.DoString(@" return function(obj) local isKeyValuePair local function lua_iter(cs_iter, k) if cs_iter:MoveNext() then local current = cs_iter.Current if isKeyValuePair == nil then if type(current) == 'userdata' then local t = current:GetType() isKeyValuePair = t.Name == 'KeyValuePair`2' and t.Namespace == 'System.Collections.Generic' else isKeyValuePair = false end --print(current, isKeyValuePair) end if isKeyValuePair then return current.Key, current.Value else return k + 1, current end end end return lua_iter, obj:GetEnumerator(), -1 end ")[0] as LuaFunction; func.push(L); enumerable_pairs_func = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); func.Dispose(); } public void OpenLib(RealStatePtr L) { if (0 != LuaAPI.xlua_getglobal(L, "xlua")) { throw new Exception("call xlua_getglobal fail!" + LuaAPI.lua_tostring(L, -1)); } LuaAPI.xlua_pushasciistring(L, "import_type"); LuaAPI.lua_pushstdcallcfunction(L,importTypeFunction); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "import_generic_type"); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.ImportGenericType); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "cast"); LuaAPI.lua_pushstdcallcfunction(L, castFunction); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "load_assembly"); LuaAPI.lua_pushstdcallcfunction(L,loadAssemblyFunction); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "access"); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.XLuaAccess); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "private_accessible"); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.XLuaPrivateAccessible); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "metatable_operation"); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.XLuaMetatableOperation); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "tofunction"); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.ToFunction); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "get_generic_method"); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.GetGenericMethod); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "release"); LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.ReleaseCsObject); LuaAPI.lua_rawset(L, -3); LuaAPI.lua_pop(L, 1); LuaAPI.lua_createtable(L, 1, 4); // 4 for __gc, __tostring, __index, __newindex common_array_meta = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.lua_createtable(L, 1, 4); // 4 for __gc, __tostring, __index, __newindex common_delegate_meta = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); } internal void createFunctionMetatable(RealStatePtr L) { LuaAPI.lua_newtable(L); LuaAPI.xlua_pushasciistring(L,"__gc"); LuaAPI.lua_pushstdcallcfunction(L,metaFunctions.GcMeta); LuaAPI.lua_rawset(L,-3); LuaAPI.lua_pushlightuserdata(L, LuaAPI.xlua_tag()); LuaAPI.lua_pushnumber(L, 1); LuaAPI.lua_rawset(L, -3); LuaAPI.lua_pushvalue(L, -1); int type_id = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.lua_pushnumber(L, type_id); LuaAPI.xlua_rawseti(L, -2, 1); LuaAPI.lua_pop(L, 1); typeIdMap.Add(typeof(LuaCSFunction), type_id); } internal Type FindType(string className, bool isQualifiedName = false) { foreach (Assembly assembly in assemblies) { Type klass = assembly.GetType(className); if (klass!=null) { return klass; } } int p1 = className.IndexOf('['); if (p1 > 0 && !isQualifiedName) { string qualified_name = className.Substring(0, p1 + 1); string[] generic_params = className.Substring(p1 + 1, className.Length - qualified_name.Length - 1).Split(','); for(int i = 0; i < generic_params.Length; i++) { Type generic_param = FindType(generic_params[i].Trim()); if (generic_param == null) { return null; } if (i != 0 ) { qualified_name += ", "; } qualified_name = qualified_name + "[" + generic_param.AssemblyQualifiedName + "]"; } qualified_name += "]"; return FindType(qualified_name, true); } return null; } bool hasMethod(Type type, string methodName) { foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { if (method.Name == methodName) { return true; } } return false; } internal void collectObject(int obj_index_to_collect) { object o; if (objects.TryGetValue(obj_index_to_collect, out o)) { objects.Remove(obj_index_to_collect); if (o != null) { int obj_index; //lua gc是先把weak table移除后再调用__gc,这期间同一个对象可能再次push到lua,关联到新的index bool is_enum = o.GetType().IsEnum(); if ((is_enum ? enumMap.TryGetValue(o, out obj_index) : reverseMap.TryGetValue(o, out obj_index)) && obj_index == obj_index_to_collect) { if (is_enum) { enumMap.Remove(o); } else { reverseMap.Remove(o); } } } } } int addObject(object obj, bool is_valuetype, bool is_enum) { int index = objects.Add(obj); if (is_enum) { enumMap[obj] = index; } else if (!is_valuetype) { reverseMap[obj] = index; } return index; } internal object GetObject(RealStatePtr L,int index) { return (objectCasters.GetCaster(typeof(object))(L, index, null)); } public Type GetTypeOf(RealStatePtr L, int idx) { Type type = null; int type_id = LuaAPI.xlua_gettypeid(L, idx); if (type_id != -1) { typeMap.TryGetValue(type_id, out type); } return type; } public bool Assignable(RealStatePtr L, int index) { return Assignable(L, index, typeof(T)); } public bool Assignable(RealStatePtr L, int index, Type type) { if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA) // 快路径 { int udata = LuaAPI.xlua_tocsobj_safe(L, index); object obj; if (udata != -1 && objects.TryGetValue(udata, out obj)) { RawObject rawObject = obj as RawObject; if (rawObject != null) { obj = rawObject.Target; } if (obj == null) { return !type.IsValueType(); } return type.IsAssignableFrom(obj.GetType()); } int type_id = LuaAPI.xlua_gettypeid(L, index); Type type_of_struct; if (type_id != -1 && typeMap.TryGetValue(type_id, out type_of_struct)) // is struct { return type.IsAssignableFrom(type_of_struct); } } return objectCheckers.GetChecker(type)(L, index); } public object GetObject(RealStatePtr L, int index, Type type) { int udata = LuaAPI.xlua_tocsobj_safe(L, index); if (udata != -1) { object obj = objects.Get(udata); RawObject rawObject = obj as RawObject; return rawObject == null ? obj : rawObject.Target; } else { if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA) { GetCSObject get; int type_id = LuaAPI.xlua_gettypeid(L, index); if (type_id != -1 && type_id == decimal_type_id) { decimal d; Get(L, index, out d); return d; } Type type_of_struct; if (type_id != -1 && typeMap.TryGetValue(type_id, out type_of_struct) && type.IsAssignableFrom(type_of_struct) && custom_get_funcs.TryGetValue(type, out get)) { return get(L, index); } } return (objectCasters.GetCaster(type)(L, index, null)); } } public void Get(RealStatePtr L, int index, out T v) { Func get_func; if (tryGetGetFuncByType(typeof(T), out get_func)) { v = get_func(L, index); } else { v = (T)GetObject(L, index, typeof(T)); } } public void PushByType(RealStatePtr L, T v) { Action push_func; if (tryGetPushFuncByType(typeof(T), out push_func)) { push_func(L, v); } else { PushAny(L, v); } } #if GENERIC_SHARING public T GetByType(RealStatePtr L, int index) { Func get_func; if (tryGetGetFuncByType(typeof(T), out get_func)) { return get_func(L, index); } else { return (T)GetObject(L, index, typeof(T)); } } #endif public T[] GetParams(RealStatePtr L, int index) { T[] ret = new T[Math.Max(LuaAPI.lua_gettop(L) - index + 1, 0)]; for(int i = 0; i < ret.Length; i++) { Get(L, index + i, out ret[i]); } return ret; } public Array GetParams(RealStatePtr L, int index, Type type) //反射版本 { Array ret = Array.CreateInstance(type, Math.Max(LuaAPI.lua_gettop(L) - index + 1, 0)); //这个函数,长度为0的话,返回null for (int i = 0; i < ret.Length; i++) { ret.SetValue(GetObject(L, index + i, type), i); } return ret; } #if UNITY_EDITOR || XLUA_GENERAL public void PushParams(RealStatePtr L, Array ary) { if (ary != null) { for (int i = 0; i < ary.Length; i++) { PushAny(L, ary.GetValue(i)); } } } #endif public T GetDelegate(RealStatePtr L, int index) where T :class { if (LuaAPI.lua_isfunction(L, index)) { return CreateDelegateBridge(L, typeof(T), index) as T; } else if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA) { return (T)SafeGetCSObj(L, index); } else { return null; } } Dictionary typeIdMap = new Dictionary(); //only store the type id to type map for struct Dictionary typeMap = new Dictionary(); public int GetTypeId(RealStatePtr L, Type type) { bool isFirst; return getTypeId(L, type, out isFirst); } HashSet privateAccessibleFlags = new HashSet(); public void PrivateAccessible(RealStatePtr L, Type type) { if (!privateAccessibleFlags.Contains(type)) //未处理 { privateAccessibleFlags.Add(type); if (typeIdMap.ContainsKey(type)) //loaded { Utils.MakePrivateAccessible(L, type); } } } internal int getTypeId(RealStatePtr L, Type type, out bool is_first, LOGLEVEL log_level = LOGLEVEL.WARN) { int type_id; is_first = false; if (!typeIdMap.TryGetValue(type, out type_id)) // no reference { if (type.IsArray) { if (common_array_meta == -1) throw new Exception("Fatal Exception! Array Metatable not inited!"); return common_array_meta; } if (typeof(MulticastDelegate).IsAssignableFrom(type)) { if (common_delegate_meta == -1) throw new Exception("Fatal Exception! Delegate Metatable not inited!"); TryDelayWrapLoader(L, type); return common_delegate_meta; } is_first = true; Type alias_type = null; aliasCfg.TryGetValue(type, out alias_type); LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName); if (LuaAPI.lua_isnil(L, -1)) //no meta yet, try to use reflection meta { LuaAPI.lua_pop(L, 1); if (TryDelayWrapLoader(L, alias_type == null ? type : alias_type)) { LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName); } else { throw new Exception("Fatal: can not load metatable of type:" + type); } } //循环依赖,自身依赖自己的class,比如有个自身类型的静态readonly对象。 if (typeIdMap.TryGetValue(type, out type_id)) { LuaAPI.lua_pop(L, 1); } else { if (type.IsEnum()) { LuaAPI.xlua_pushasciistring(L, "__band"); LuaAPI.lua_pushstdcallcfunction(L, metaFunctions.EnumAndMeta); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "__bor"); LuaAPI.lua_pushstdcallcfunction(L, metaFunctions.EnumOrMeta); LuaAPI.lua_rawset(L, -3); } if (typeof(IEnumerable).IsAssignableFrom(type)) { LuaAPI.xlua_pushasciistring(L, "__pairs"); LuaAPI.lua_getref(L, enumerable_pairs_func); LuaAPI.lua_rawset(L, -3); } LuaAPI.lua_pushvalue(L, -1); type_id = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.lua_pushnumber(L, type_id); LuaAPI.xlua_rawseti(L, -2, 1); LuaAPI.lua_pop(L, 1); if (type.IsValueType()) { typeMap.Add(type_id, type); } typeIdMap.Add(type, type_id); } } return type_id; } void pushPrimitive(RealStatePtr L, object o) { if (o is sbyte || o is byte || o is short || o is ushort || o is int) { int i = Convert.ToInt32(o); LuaAPI.xlua_pushinteger(L, i); } else if (o is uint) { LuaAPI.xlua_pushuint(L, (uint)o); } else if (o is float || o is double) { double d = Convert.ToDouble(o); LuaAPI.lua_pushnumber(L, d); } else if (o is IntPtr) { LuaAPI.lua_pushlightuserdata(L, (IntPtr)o); } else if (o is char) { LuaAPI.xlua_pushinteger(L, (char)o); } else if (o is long) { LuaAPI.lua_pushint64(L, Convert.ToInt64(o)); } else if (o is ulong) { LuaAPI.lua_pushuint64(L, Convert.ToUInt64(o)); } else if (o is bool) { bool b = (bool)o; LuaAPI.lua_pushboolean(L, b); } else { throw new Exception("No support type " + o.GetType()); } } public void PushAny(RealStatePtr L, object o) { if (o == null) { LuaAPI.lua_pushnil(L); return; } Type type = o.GetType(); if (type.IsPrimitive()) { pushPrimitive(L, o); } else if (o is string) { LuaAPI.lua_pushstring(L, o as string); } else if (type == typeof(byte[])) { LuaAPI.lua_pushstring(L, o as byte[]); } else if (o is decimal) { PushDecimal(L, (decimal)o); } else if (o is LuaBase) { ((LuaBase)o).push(L); } else if (o is LuaCSFunction) { Push(L, o as LuaCSFunction); } else if (o is ValueType) { PushCSObject push; if (custom_push_funcs.TryGetValue(o.GetType(), out push)) { push(L, o); } else { Push(L, o); } } else { Push(L, o); } } Dictionary enumMap = new Dictionary(); public int TranslateToEnumToTop(RealStatePtr L, Type type, int idx) { object res = null; LuaTypes lt = (LuaTypes)LuaAPI.lua_type(L, idx); if (lt == LuaTypes.LUA_TNUMBER) { int ival = (int)LuaAPI.lua_tonumber(L, idx); res = Enum.ToObject(type, ival); } else if (lt == LuaTypes.LUA_TSTRING) { string sflags = LuaAPI.lua_tostring(L, idx); res = Enum.Parse(type, sflags); } else { return LuaAPI.luaL_error(L, "#1 argument must be a integer or a string"); } PushAny(L, res); return 1; } public void Push(RealStatePtr L, LuaCSFunction o) { if (Utils.IsStaticPInvokeCSFunction(o)) { LuaAPI.lua_pushstdcallcfunction(L, o); } else { Push(L, (object)o); LuaAPI.lua_pushstdcallcfunction(L, metaFunctions.StaticCSFunctionWraper, 1); } } public void Push(RealStatePtr L, LuaBase o) { if (o == null) { LuaAPI.lua_pushnil(L); } else { o.push(L); } } public void Push(RealStatePtr L, object o) { if (o == null) { LuaAPI.lua_pushnil(L); return; } int index = -1; Type type = o.GetType(); #if !UNITY_WSA || UNITY_EDITOR bool is_enum = type.IsEnum; bool is_valuetype = type.IsValueType; #else bool is_enum = type.GetTypeInfo().IsEnum; bool is_valuetype = type.GetTypeInfo().IsValueType; #endif bool needcache = !is_valuetype || is_enum; if (needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index))) { if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1) { return; } //这里实在太经典了,weaktable先删除,然后GC会延迟调用,当index会循环利用的时候,不注释这行将会导致重复释放 //collectObject(index); } bool is_first; int type_id = getTypeId(L, type, out is_first); //如果一个type的定义含本身静态readonly实例时,getTypeId会push一个实例,这时候应该用这个实例 if (is_first && needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index))) { if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1) { return; } } index = addObject(o, is_valuetype, is_enum); LuaAPI.xlua_pushcsobj(L, index, type_id, needcache, cacheRef); } public void PushObject(RealStatePtr L, object o, int type_id) { if (o == null) { LuaAPI.lua_pushnil(L); return; } int index = -1; if (reverseMap.TryGetValue(o, out index)) { if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1) { return; } } index = addObject(o, false, false); LuaAPI.xlua_pushcsobj(L, index, type_id, true, cacheRef); } public void Update(RealStatePtr L, int index, object obj) { int udata = LuaAPI.xlua_tocsobj_fast(L, index); if (udata != -1) { objects.Replace(udata, obj); } else { UpdateCSObject update; if (custom_update_funcs.TryGetValue(obj.GetType(), out update)) { update(L, index, obj); } else { throw new Exception("can not update [" + obj + "]"); } } } private object getCsObj(RealStatePtr L, int index, int udata) { object obj; if (udata == -1) { if (LuaAPI.lua_type(L, index) != LuaTypes.LUA_TUSERDATA) return null; Type type = GetTypeOf(L, index); if (type == typeof(decimal)) { decimal v; Get(L, index, out v); return v; } GetCSObject get; if (type != null && custom_get_funcs.TryGetValue(type, out get)) { return get(L, index); } else { return null; } } else if (objects.TryGetValue(udata, out obj)) { #if !UNITY_5 && !XLUA_GENERAL && !UNITY_2017 && !UNITY_2017_1_OR_NEWER && !UNITY_2018 if (obj != null && obj is UnityEngine.Object && ((obj as UnityEngine.Object) == null)) { //throw new UnityEngine.MissingReferenceException("The object of type '"+ obj.GetType().Name +"' has been destroyed but you are still trying to access it."); return null; } #endif return obj; } return null; } internal object SafeGetCSObj(RealStatePtr L, int index) { return getCsObj(L, index, LuaAPI.xlua_tocsobj_safe(L, index)); } internal object FastGetCSObj(RealStatePtr L,int index) { return getCsObj(L, index, LuaAPI.xlua_tocsobj_fast(L,index)); } internal void ReleaseCSObj(RealStatePtr L, int index) { int udata = LuaAPI.xlua_tocsobj_safe(L, index); if (udata != -1) { object o = objects.Replace(udata, null); if (o != null && reverseMap.ContainsKey(o)) { reverseMap.Remove(o); } } } List fix_cs_functions = new List(); internal LuaCSFunction GetFixCSFunction(int index) { return fix_cs_functions[index]; } internal void PushFixCSFunction(RealStatePtr L, LuaCSFunction func) { if (func == null) { LuaAPI.lua_pushnil(L); } else { LuaAPI.xlua_pushinteger(L, fix_cs_functions.Count); fix_cs_functions.Add(func); LuaAPI.lua_pushstdcallcfunction(L, metaFunctions.FixCSFunctionWraper, 1); } } #if GEN_CODE_MINIMIZE CSharpWrapper[] csharpWrapper = new CSharpWrapper[0]; int csharpWrapperSize = 0; internal int CallCSharpWrapper(RealStatePtr L, int funcidx, int top) { return csharpWrapper[funcidx](L, top); } void ensureCSharpWrapperCapacity(int min) { if (csharpWrapper.Length < min) { int num = (csharpWrapper.Length == 0) ? 4 : (csharpWrapper.Length * 2); if (num > 2146435071) { num = 2146435071; } if (num < min) { num = min; } var array = new CSharpWrapper[num]; Array.Copy(csharpWrapper, 0, array, 0, csharpWrapper.Length); csharpWrapper = array; } } internal void PushCSharpWrapper(RealStatePtr L, CSharpWrapper func) { if (func == null) { LuaAPI.lua_pushnil(L); } else { LuaAPI.xlua_push_csharp_wrapper(L, csharpWrapperSize); ensureCSharpWrapperCapacity(csharpWrapperSize + 1); csharpWrapper[csharpWrapperSize++] = func; } } #endif internal object[] popValues(RealStatePtr L,int oldTop) { int newTop=LuaAPI.lua_gettop(L); if(oldTop==newTop) { return null; } else { ArrayList returnValues=new ArrayList(); for(int i=oldTop+1;i<=newTop;i++) { returnValues.Add(GetObject(L,i)); } LuaAPI.lua_settop(L,oldTop); return returnValues.ToArray(); } } internal object[] popValues(RealStatePtr L,int oldTop,Type[] popTypes) { int newTop=LuaAPI.lua_gettop(L); if(oldTop==newTop) { return null; } else { int iTypes; ArrayList returnValues=new ArrayList(); if(popTypes[0] == typeof(void)) iTypes=1; else iTypes=0; for(int i=oldTop+1;i<=newTop;i++) { returnValues.Add(GetObject(L,i,popTypes[iTypes])); iTypes++; } LuaAPI.lua_settop(L,oldTop); return returnValues.ToArray(); } } public delegate void PushCSObject(RealStatePtr L, object obj); public delegate object GetCSObject(RealStatePtr L, int idx); public delegate void UpdateCSObject(RealStatePtr L, int idx, object obj); private Dictionary custom_push_funcs = new Dictionary(); private Dictionary custom_get_funcs = new Dictionary(); private Dictionary custom_update_funcs = new Dictionary(); void registerCustomOp(Type type, PushCSObject push, GetCSObject get, UpdateCSObject update) { if (push != null) custom_push_funcs.Add(type, push); if (get != null) custom_get_funcs.Add(type, get); if (update != null) custom_update_funcs.Add(type, update); } public bool HasCustomOp(Type type) { return custom_push_funcs.ContainsKey(type); } private Dictionary push_func_with_type = null; bool tryGetPushFuncByType(Type type, out T func) where T : class { if (push_func_with_type == null) { push_func_with_type = new Dictionary() { {typeof(int), new Action(LuaAPI.xlua_pushinteger) }, {typeof(double), new Action(LuaAPI.lua_pushnumber) }, {typeof(string), new Action(LuaAPI.lua_pushstring) }, {typeof(byte[]), new Action(LuaAPI.lua_pushstring) }, {typeof(bool), new Action(LuaAPI.lua_pushboolean) }, {typeof(long), new Action(LuaAPI.lua_pushint64) }, {typeof(ulong), new Action(LuaAPI.lua_pushuint64) }, {typeof(IntPtr), new Action(LuaAPI.lua_pushlightuserdata) }, {typeof(decimal), new Action(PushDecimal) }, {typeof(byte), new Action((L, v) => LuaAPI.xlua_pushinteger(L, v)) }, {typeof(sbyte), new Action((L, v) => LuaAPI.xlua_pushinteger(L, v)) }, {typeof(char), new Action((L, v) => LuaAPI.xlua_pushinteger(L, v)) }, {typeof(short), new Action((L, v) => LuaAPI.xlua_pushinteger(L, v)) }, {typeof(ushort), new Action((L, v) => LuaAPI.xlua_pushinteger(L, v)) }, {typeof(uint), new Action(LuaAPI.xlua_pushuint) }, {typeof(float), new Action((L, v) => LuaAPI.lua_pushnumber(L, v)) }, }; } Delegate obj; if (push_func_with_type.TryGetValue(type, out obj)) { func = obj as T; return true; } else { func = null; return false; } } private Dictionary get_func_with_type = null; bool tryGetGetFuncByType(Type type, out T func) where T : class { if (get_func_with_type == null) { get_func_with_type = new Dictionary() { {typeof(int), new Func(LuaAPI.xlua_tointeger) }, {typeof(double), new Func(LuaAPI.lua_tonumber) }, {typeof(string), new Func(LuaAPI.lua_tostring) }, {typeof(byte[]), new Func(LuaAPI.lua_tobytes) }, {typeof(bool), new Func(LuaAPI.lua_toboolean) }, {typeof(long), new Func(LuaAPI.lua_toint64) }, {typeof(ulong), new Func(LuaAPI.lua_touint64) }, {typeof(IntPtr), new Func(LuaAPI.lua_touserdata) }, {typeof(decimal), new Func((L, idx) => { decimal ret; Get(L, idx, out ret); return ret; }) }, {typeof(byte), new Func((L, idx) => (byte)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(sbyte), new Func((L, idx) => (sbyte)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(char), new Func((L, idx) => (char)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(short), new Func((L, idx) => (short)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(ushort), new Func((L, idx) => (ushort)LuaAPI.xlua_tointeger(L, idx) ) }, {typeof(uint), new Func(LuaAPI.xlua_touint) }, {typeof(float), new Func((L, idx) => (float)LuaAPI.lua_tonumber(L, idx) ) }, }; } Delegate obj; if (get_func_with_type.TryGetValue(type, out obj)) { func = obj as T; return true; } else { func = null; return false; } } public delegate bool CheckFunc(RealStatePtr L, int idx); public delegate void GetFunc(RealStatePtr L, int idx, out T val); public void RegisterPushAndGetAndUpdate(Action push, GetFunc get, Action update) { Type type = typeof(T); Action org_push; Func org_get; if (tryGetPushFuncByType(type, out org_push) || tryGetGetFuncByType(type, out org_get)) { throw new InvalidOperationException("push or get of " + type + " has register!"); } push_func_with_type.Add(type, push); get_func_with_type.Add(type, new Func((L, idx) => { T ret; get(L, idx, out ret); return ret; })); registerCustomOp(type, (RealStatePtr L, object obj) => { push(L, (T)obj); }, (RealStatePtr L, int idx) => { T val; get(L, idx, out val); return val; }, (RealStatePtr L, int idx, object obj) => { update(L, idx, (T)obj); } ); } public void RegisterChecker(CheckFunc check) { objectCheckers.AddChecker(typeof(T), (L, idx) => { return check(L, idx); }); } public void RegisterCaster(GetFunc get) { objectCasters.AddCaster(typeof(T), (L, idx, o) => { T obj; get(L, idx, out obj); return obj; }); } int decimal_type_id = -1; public void PushDecimal(RealStatePtr L, decimal val) { if (decimal_type_id == -1) { bool is_first; decimal_type_id = getTypeId(L, typeof(decimal), out is_first); } IntPtr buff = LuaAPI.xlua_pushstruct(L, 16, decimal_type_id); if (!CopyByValue.Pack(buff, 0, val)) { throw new Exception("pack fail for decimal ,value=" + val); } } public bool IsDecimal(RealStatePtr L, int index) { if (decimal_type_id == -1) return false; return LuaAPI.xlua_gettypeid(L, index) == decimal_type_id; } public decimal GetDecimal(RealStatePtr L, int index) { decimal ret; Get(L, index, out ret); return ret; } public void Get(RealStatePtr L, int index, out decimal val) { LuaTypes lua_type = LuaAPI.lua_type(L, index); if (lua_type == LuaTypes.LUA_TUSERDATA) { if (LuaAPI.xlua_gettypeid(L, index) != decimal_type_id) { throw new Exception("invalid userdata for decimal!"); } IntPtr buff = LuaAPI.lua_touserdata(L, index); if (!CopyByValue.UnPack(buff, 0, out val)) { throw new Exception("unpack decimal fail!"); } } else if(lua_type == LuaTypes.LUA_TNUMBER) { if (LuaAPI.lua_isint64(L, index)) { val = (decimal)LuaAPI.lua_toint64(L, index); } else { val = (decimal)LuaAPI.lua_tonumber(L, index); // has gc } } else { throw new Exception("invalid lua value for decimal, LuaType=" + lua_type); } } } }