MethodWarpsCache.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /*
  2. * Tencent is pleased to support the open source community by making xLua available.
  3. * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
  4. * 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
  5. * http://opensource.org/licenses/MIT
  6. * 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.
  7. */
  8. #if USE_UNI_LUA
  9. using LuaAPI = UniLua.Lua;
  10. using RealStatePtr = UniLua.ILuaState;
  11. using LuaCSFunction = UniLua.CSharpFunctionDelegate;
  12. #else
  13. using LuaAPI = XLua.LuaDLL.Lua;
  14. using RealStatePtr = System.IntPtr;
  15. using LuaCSFunction = XLua.LuaDLL.lua_CSFunction;
  16. #endif
  17. using System.Collections.Generic;
  18. using System;
  19. using System.Reflection;
  20. namespace XLua
  21. {
  22. public class OverloadMethodWrap
  23. {
  24. ObjectTranslator translator;
  25. Type targetType;
  26. MethodBase method;
  27. ObjectCheck[] checkArray;
  28. ObjectCast[] castArray;
  29. int[] inPosArray;
  30. int[] outPosArray;
  31. bool[] isOptionalArray;
  32. object[] defaultValueArray;
  33. bool isVoid = true;
  34. int luaStackPosStart = 1;
  35. bool targetNeeded = false;
  36. object[] args;
  37. int[] refPos;
  38. Type paramsType = null;
  39. public bool HasDefalutValue{ get; private set; }
  40. public OverloadMethodWrap(ObjectTranslator translator, Type targetType, MethodBase method)
  41. {
  42. this.translator = translator;
  43. this.targetType = targetType;
  44. this.method = method;
  45. HasDefalutValue = false;
  46. }
  47. public void Init(ObjectCheckers objCheckers, ObjectCasters objCasters)
  48. {
  49. if ((typeof(Delegate) != targetType && typeof(Delegate).IsAssignableFrom(targetType)) ||
  50. !method.IsStatic || method.IsConstructor)
  51. {
  52. luaStackPosStart = 2;
  53. if (!method.IsConstructor)
  54. {
  55. targetNeeded = true;
  56. }
  57. }
  58. var paramInfos = method.GetParameters();
  59. refPos = new int[paramInfos.Length];
  60. List<int> inPosList = new List<int>();
  61. List<int> outPosList = new List<int>();
  62. List<ObjectCheck> paramsChecks = new List<ObjectCheck>();
  63. List<ObjectCast> paramsCasts = new List<ObjectCast>();
  64. List<bool> isOptionalList = new List<bool>();
  65. List<object> defaultValueList = new List<object>();
  66. for(int i = 0; i < paramInfos.Length; i++)
  67. {
  68. refPos[i] = -1;
  69. if (!paramInfos[i].IsIn && paramInfos[i].IsOut) // out parameter
  70. {
  71. outPosList.Add(i);
  72. }
  73. else
  74. {
  75. if(paramInfos[i].ParameterType.IsByRef)
  76. {
  77. var ttype = paramInfos[i].ParameterType.GetElementType();
  78. if(CopyByValue.IsStruct(ttype) && ttype != typeof(decimal))
  79. {
  80. refPos[i] = inPosList.Count;
  81. }
  82. outPosList.Add(i);
  83. }
  84. inPosList.Add(i);
  85. var paramType = paramInfos[i].IsDefined(typeof(ParamArrayAttribute), false) || (!paramInfos[i].ParameterType.IsArray && paramInfos[i].ParameterType.IsByRef ) ?
  86. paramInfos[i].ParameterType.GetElementType() : paramInfos[i].ParameterType;
  87. paramsChecks.Add (objCheckers.GetChecker(paramType));
  88. paramsCasts.Add (objCasters.GetCaster(paramType));
  89. isOptionalList.Add(paramInfos[i].IsOptional);
  90. var defalutValue = paramInfos[i].DefaultValue;
  91. if (paramInfos[i].IsOptional)
  92. {
  93. if (defalutValue != null && defalutValue.GetType() != paramInfos[i].ParameterType)
  94. {
  95. defalutValue = defalutValue.GetType() == typeof(Missing) ? (paramInfos[i].ParameterType.IsValueType() ? Activator.CreateInstance(paramInfos[i].ParameterType) : Missing.Value)
  96. : Convert.ChangeType(defalutValue, paramInfos[i].ParameterType);
  97. }
  98. HasDefalutValue = true;
  99. }
  100. defaultValueList.Add(paramInfos[i].IsOptional ? defalutValue : null);
  101. }
  102. }
  103. checkArray = paramsChecks.ToArray();
  104. castArray = paramsCasts.ToArray();
  105. inPosArray = inPosList.ToArray();
  106. outPosArray = outPosList.ToArray();
  107. isOptionalArray = isOptionalList.ToArray();
  108. defaultValueArray = defaultValueList.ToArray();
  109. if (paramInfos.Length > 0 && paramInfos[paramInfos.Length - 1].IsDefined(typeof(ParamArrayAttribute), false))
  110. {
  111. paramsType = paramInfos[paramInfos.Length - 1].ParameterType.GetElementType();
  112. }
  113. args = new object[paramInfos.Length];
  114. if (method is MethodInfo) //constructor is not MethodInfo?
  115. {
  116. isVoid = (method as MethodInfo).ReturnType == typeof(void);
  117. }
  118. else if(method is ConstructorInfo)
  119. {
  120. isVoid = false;
  121. }
  122. }
  123. public bool Check(RealStatePtr L)
  124. {
  125. int luaTop = LuaAPI.lua_gettop(L);
  126. int luaStackPos = luaStackPosStart;
  127. for(int i = 0; i < checkArray.Length; i++)
  128. {
  129. if ((i == (checkArray.Length - 1)) && (paramsType != null))
  130. {
  131. break;
  132. }
  133. if(luaStackPos > luaTop && !isOptionalArray[i])
  134. {
  135. return false;
  136. }
  137. else if(luaStackPos <= luaTop && !checkArray[i](L, luaStackPos))
  138. {
  139. return false;
  140. }
  141. if (luaStackPos <= luaTop || !isOptionalArray[i])
  142. {
  143. luaStackPos++;
  144. }
  145. }
  146. return paramsType != null ? (luaStackPos < luaTop + 1 ?
  147. checkArray[checkArray.Length - 1](L, luaStackPos) : true) : luaStackPos == luaTop + 1;
  148. }
  149. public int Call(RealStatePtr L)
  150. {
  151. try
  152. {
  153. #if UNITY_EDITOR && !DISABLE_OBSOLETE_WARNING
  154. if (method.IsDefined(typeof(ObsoleteAttribute), true))
  155. {
  156. ObsoleteAttribute info = Attribute.GetCustomAttribute(method, typeof(ObsoleteAttribute)) as ObsoleteAttribute;
  157. UnityEngine.Debug.LogWarning("Obsolete Method [" + method.DeclaringType.ToString() + "." + method.Name + "]: " + info.Message);
  158. }
  159. #endif
  160. object target = null;
  161. MethodBase toInvoke = method;
  162. if (luaStackPosStart > 1)
  163. {
  164. target = translator.FastGetCSObj(L, 1);
  165. if (target is Delegate)
  166. {
  167. Delegate delegateInvoke = (Delegate)target;
  168. #if UNITY_WSA && !UNITY_EDITOR
  169. toInvoke = delegateInvoke.GetMethodInfo();
  170. #else
  171. toInvoke = delegateInvoke.Method;
  172. #endif
  173. }
  174. }
  175. int luaTop = LuaAPI.lua_gettop(L);
  176. int luaStackPos = luaStackPosStart;
  177. for (int i = 0; i < castArray.Length; i++)
  178. {
  179. //UnityEngine.Debug.Log("inPos:" + inPosArray[i]);
  180. if (luaStackPos > luaTop) //after check
  181. {
  182. if (paramsType != null && i == castArray.Length - 1)
  183. {
  184. args[inPosArray[i]] = Array.CreateInstance(paramsType, 0);
  185. }
  186. else
  187. {
  188. args[inPosArray[i]] = defaultValueArray[i];
  189. }
  190. }
  191. else
  192. {
  193. if (paramsType != null && i == castArray.Length - 1)
  194. {
  195. args[inPosArray[i]] = translator.GetParams(L, luaStackPos, paramsType);
  196. }
  197. else
  198. {
  199. args[inPosArray[i]] = castArray[i](L, luaStackPos, null);
  200. }
  201. luaStackPos++;
  202. }
  203. //UnityEngine.Debug.Log("value:" + args[inPosArray[i]]);
  204. }
  205. object ret = null;
  206. ret = toInvoke.IsConstructor ? ((ConstructorInfo)method).Invoke(args) : method.Invoke(targetNeeded ? target : null, args);
  207. if (targetNeeded && targetType.IsValueType())
  208. {
  209. translator.Update(L, 1, target);
  210. }
  211. int nRet = 0;
  212. if (!isVoid)
  213. {
  214. //UnityEngine.Debug.Log(toInvoke.ToString() + " ret:" + ret);
  215. translator.PushAny(L, ret);
  216. nRet++;
  217. }
  218. for (int i = 0; i < outPosArray.Length; i++)
  219. {
  220. if (refPos[outPosArray[i]] != -1)
  221. {
  222. translator.Update(L, luaStackPosStart + refPos[outPosArray[i]], args[outPosArray[i]]);
  223. }
  224. translator.PushAny(L, args[outPosArray[i]]);
  225. nRet++;
  226. }
  227. return nRet;
  228. }
  229. finally
  230. {
  231. for(int i = 0; i < args.Length; i++)
  232. {
  233. args[i] = null;
  234. }
  235. }
  236. }
  237. }
  238. public class MethodWrap
  239. {
  240. private string methodName;
  241. private List<OverloadMethodWrap> overloads = new List<OverloadMethodWrap>();
  242. private bool forceCheck;
  243. public MethodWrap(string methodName, List<OverloadMethodWrap> overloads, bool forceCheck)
  244. {
  245. this.methodName = methodName;
  246. this.overloads = overloads;
  247. this.forceCheck = forceCheck;
  248. }
  249. public int Call(RealStatePtr L)
  250. {
  251. try
  252. {
  253. if (overloads.Count == 1 && !overloads[0].HasDefalutValue && !forceCheck) return overloads[0].Call(L);
  254. for (int i = 0; i < overloads.Count; ++i)
  255. {
  256. var overload = overloads[i];
  257. if (overload.Check(L))
  258. {
  259. return overload.Call(L);
  260. }
  261. }
  262. return LuaAPI.luaL_error(L, "invalid arguments to " + methodName);
  263. }
  264. catch (System.Reflection.TargetInvocationException e)
  265. {
  266. return LuaAPI.luaL_error(L, "c# exception:" + e.InnerException.Message + ",stack:" + e.InnerException.StackTrace);
  267. }
  268. catch (System.Exception e)
  269. {
  270. return LuaAPI.luaL_error(L, "c# exception:" + e.Message + ",stack:" + e.StackTrace);
  271. }
  272. }
  273. }
  274. public class MethodWrapsCache
  275. {
  276. ObjectTranslator translator;
  277. ObjectCheckers objCheckers;
  278. ObjectCasters objCasters;
  279. Dictionary<Type, LuaCSFunction> constructorCache = new Dictionary<Type, LuaCSFunction>();
  280. Dictionary<Type, Dictionary<string, LuaCSFunction>> methodsCache = new Dictionary<Type, Dictionary<string, LuaCSFunction>>();
  281. Dictionary<Type, LuaCSFunction> delegateCache = new Dictionary<Type, LuaCSFunction>();
  282. public MethodWrapsCache(ObjectTranslator translator, ObjectCheckers objCheckers, ObjectCasters objCasters)
  283. {
  284. this.translator = translator;
  285. this.objCheckers = objCheckers;
  286. this.objCasters = objCasters;
  287. }
  288. public LuaCSFunction GetConstructorWrap(Type type)
  289. {
  290. //UnityEngine.Debug.LogWarning("GetConstructor:" + type);
  291. if (!constructorCache.ContainsKey(type))
  292. {
  293. var constructors = type.GetConstructors();
  294. if (type.IsAbstract() || constructors == null || constructors.Length == 0)
  295. {
  296. if (type.IsValueType())
  297. {
  298. constructorCache[type] = (L) =>
  299. {
  300. translator.PushAny(L, Activator.CreateInstance(type));
  301. return 1;
  302. };
  303. }
  304. else
  305. {
  306. return null;
  307. }
  308. }
  309. else
  310. {
  311. LuaCSFunction ctor = _GenMethodWrap(type, ".ctor", constructors, true).Call;
  312. if (type.IsValueType())
  313. {
  314. bool hasZeroParamsCtor = false;
  315. for (int i = 0; i < constructors.Length; i++)
  316. {
  317. if (constructors[i].GetParameters().Length == 0)
  318. {
  319. hasZeroParamsCtor = true;
  320. break;
  321. }
  322. }
  323. if (hasZeroParamsCtor)
  324. {
  325. constructorCache[type] = ctor;
  326. }
  327. else
  328. {
  329. constructorCache[type] = (L) =>
  330. {
  331. if (LuaAPI.lua_gettop(L) == 1)
  332. {
  333. translator.PushAny(L, Activator.CreateInstance(type));
  334. return 1;
  335. }
  336. else
  337. {
  338. return ctor(L);
  339. }
  340. };
  341. }
  342. }
  343. else
  344. {
  345. constructorCache[type] = ctor;
  346. }
  347. }
  348. }
  349. return constructorCache[type];
  350. }
  351. public LuaCSFunction GetMethodWrap(Type type, string methodName)
  352. {
  353. //UnityEngine.Debug.LogWarning("GetMethodWrap:" + type + " " + methodName);
  354. if (!methodsCache.ContainsKey(type))
  355. {
  356. methodsCache[type] = new Dictionary<string, LuaCSFunction>();
  357. }
  358. var methodsOfType = methodsCache[type];
  359. if (!methodsOfType.ContainsKey(methodName))
  360. {
  361. MemberInfo[] methods = type.GetMember(methodName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
  362. if (methods == null || methods.Length == 0 ||
  363. #if UNITY_WSA && !UNITY_EDITOR
  364. methods[0] is MethodBase
  365. #else
  366. methods[0].MemberType != MemberTypes.Method
  367. #endif
  368. )
  369. {
  370. return null;
  371. }
  372. methodsOfType[methodName] = _GenMethodWrap(type, methodName, methods).Call;
  373. }
  374. return methodsOfType[methodName];
  375. }
  376. public LuaCSFunction GetMethodWrapInCache(Type type, string methodName)
  377. {
  378. //string retriKey = type.ToString() + "." + methodName;
  379. //return methodsCache.ContainsKey(retriKey) ? methodsCache[retriKey] : null;
  380. if (!methodsCache.ContainsKey(type))
  381. {
  382. methodsCache[type] = new Dictionary<string, LuaCSFunction>();
  383. }
  384. var methodsOfType = methodsCache[type];
  385. return methodsOfType.ContainsKey(methodName) ? methodsOfType[methodName] : null;
  386. }
  387. public LuaCSFunction GetDelegateWrap(Type type)
  388. {
  389. //UnityEngine.Debug.LogWarning("GetDelegateWrap:" + type );
  390. if (!typeof(Delegate).IsAssignableFrom(type))
  391. {
  392. return null;
  393. }
  394. if (!delegateCache.ContainsKey(type))
  395. {
  396. delegateCache[type] = _GenMethodWrap(type, type.ToString(), new MethodBase[] { type.GetMethod("Invoke") }).Call;
  397. }
  398. return delegateCache[type];
  399. }
  400. public LuaCSFunction GetEventWrap(Type type, string eventName)
  401. {
  402. if (!methodsCache.ContainsKey(type))
  403. {
  404. methodsCache[type] = new Dictionary<string, LuaCSFunction>();
  405. }
  406. var methodsOfType = methodsCache[type];
  407. if (!methodsOfType.ContainsKey(eventName))
  408. {
  409. {
  410. EventInfo eventInfo = type.GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic);
  411. if (eventInfo == null)
  412. {
  413. throw new Exception(type.Name + " has no event named: " + eventName);
  414. }
  415. int start_idx = 0;
  416. MethodInfo add = eventInfo.GetAddMethod(true);
  417. MethodInfo remove = eventInfo.GetRemoveMethod(true);
  418. if (add == null && remove == null)
  419. {
  420. throw new Exception(type.Name + "'s " + eventName + " has either add nor remove");
  421. }
  422. bool is_static = add != null ? add.IsStatic : remove.IsStatic;
  423. if (!is_static) start_idx = 1;
  424. methodsOfType[eventName] = (L) =>
  425. {
  426. object obj = null;
  427. if (!is_static)
  428. {
  429. obj = translator.GetObject(L, 1, type);
  430. if (obj == null)
  431. {
  432. return LuaAPI.luaL_error(L, "invalid #1, needed:" + type);
  433. }
  434. }
  435. try
  436. {
  437. object handlerDelegate = translator.CreateDelegateBridge(L, eventInfo.EventHandlerType, start_idx + 2);
  438. if (handlerDelegate == null)
  439. {
  440. return LuaAPI.luaL_error(L, "invalid #" + (start_idx + 2) + ", needed:" + eventInfo.EventHandlerType);
  441. }
  442. switch (LuaAPI.lua_tostring(L, start_idx + 1))
  443. {
  444. case "+":
  445. if (add == null)
  446. {
  447. return LuaAPI.luaL_error(L, "no add for event " + eventName);
  448. }
  449. add.Invoke(obj, new object[] { handlerDelegate });
  450. break;
  451. case "-":
  452. if (remove == null)
  453. {
  454. return LuaAPI.luaL_error(L, "no remove for event " + eventName);
  455. }
  456. remove.Invoke(obj, new object[] { handlerDelegate });
  457. break;
  458. default:
  459. return LuaAPI.luaL_error(L, "invalid #" + (start_idx + 1) + ", needed: '+' or '-'" + eventInfo.EventHandlerType);
  460. }
  461. }
  462. catch (System.Exception e)
  463. {
  464. return LuaAPI.luaL_error(L, "c# exception:" + e + ",stack:" + e.StackTrace);
  465. }
  466. return 0;
  467. };
  468. }
  469. }
  470. return methodsOfType[eventName];
  471. }
  472. public MethodWrap _GenMethodWrap(Type type, string methodName, IEnumerable<MemberInfo> methodBases, bool forceCheck = false)
  473. {
  474. List<OverloadMethodWrap> overloads = new List<OverloadMethodWrap>();
  475. foreach(var methodBase in methodBases)
  476. {
  477. var mb = methodBase as MethodBase;
  478. if (mb == null)
  479. continue;
  480. if (mb.IsGenericMethodDefinition
  481. #if !ENABLE_IL2CPP
  482. && !tryMakeGenericMethod(ref mb)
  483. #endif
  484. )
  485. continue;
  486. var overload = new OverloadMethodWrap(translator, type, mb);
  487. overload.Init(objCheckers, objCasters);
  488. overloads.Add(overload);
  489. }
  490. return new MethodWrap(methodName, overloads, forceCheck);
  491. }
  492. private static bool tryMakeGenericMethod(ref MethodBase method)
  493. {
  494. try
  495. {
  496. if (!(method is MethodInfo) || !Utils.IsSupportedMethod(method as MethodInfo) )
  497. {
  498. return false;
  499. }
  500. var genericArguments = method.GetGenericArguments();
  501. var constraintedArgumentTypes = new Type[genericArguments.Length];
  502. for (var i = 0; i < genericArguments.Length; i++)
  503. {
  504. var argumentType = genericArguments[i];
  505. var parameterConstraints = argumentType.GetGenericParameterConstraints();
  506. constraintedArgumentTypes[i] = parameterConstraints[0];
  507. }
  508. method = ((MethodInfo)method).MakeGenericMethod(constraintedArgumentTypes);
  509. return true;
  510. }
  511. catch (Exception)
  512. {
  513. return false;
  514. }
  515. }
  516. }
  517. }