MMRScriptTypeCast.cs 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
  28. using OpenSim.Region.ScriptEngine.Yengine;
  29. using System;
  30. using System.Collections.Generic;
  31. using System.Reflection;
  32. using System.Reflection.Emit;
  33. using System.Globalization;
  34. using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
  35. using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
  36. using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
  37. using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
  38. using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
  39. using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
  40. using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
  41. /**
  42. * @brief Generate script object code to perform type casting
  43. */
  44. namespace OpenSim.Region.ScriptEngine.Yengine
  45. {
  46. public class TypeCast
  47. {
  48. private delegate void CastDelegate(IScriptCodeGen scg, Token errorAt);
  49. private static ConstructorInfo floatConstructorStringInfo = typeof(LSL_Float).GetConstructor(new Type[] { typeof(string) });
  50. private static ConstructorInfo integerConstructorStringInfo = typeof(LSL_Integer).GetConstructor(new Type[] { typeof(string) });
  51. private static ConstructorInfo lslFloatConstructorInfo = typeof(LSL_Float).GetConstructor(new Type[] { typeof(double) });
  52. private static ConstructorInfo lslIntegerConstructorInfo = typeof(LSL_Integer).GetConstructor(new Type[] { typeof(int) });
  53. private static ConstructorInfo lslStringConstructorInfo = typeof(LSL_String).GetConstructor(new Type[] { typeof(string) });
  54. private static ConstructorInfo rotationConstrucorStringInfo = typeof(LSL_Rotation).GetConstructor(new Type[] { typeof(string) });
  55. private static ConstructorInfo vectorConstrucorStringInfo = typeof(LSL_Vector).GetConstructor(new Type[] { typeof(string) });
  56. private static FieldInfo lslFloatValueFieldInfo = typeof(LSL_Float).GetField("value");
  57. private static FieldInfo lslIntegerValueFieldInfo = typeof(LSL_Integer).GetField("value");
  58. private static FieldInfo lslStringValueFieldInfo = typeof(LSL_String).GetField("m_string");
  59. private static FieldInfo sdtcITableFieldInfo = typeof(XMRSDTypeClObj).GetField("sdtcITable");
  60. private static MethodInfo boolToListMethodInfo = typeof(TypeCast).GetMethod("BoolToList", new Type[] { typeof(bool) });
  61. private static MethodInfo boolToStringMethodInfo = typeof(TypeCast).GetMethod("BoolToString", new Type[] { typeof(bool) });
  62. private static MethodInfo charToStringMethodInfo = typeof(TypeCast).GetMethod("CharToString", new Type[] { typeof(char) });
  63. private static MethodInfo excToStringMethodInfo = typeof(TypeCast).GetMethod("ExceptionToString", new Type[] { typeof(Exception), typeof(XMRInstAbstract) });
  64. private static MethodInfo floatToStringMethodInfo = typeof(TypeCast).GetMethod("FloatToString", new Type[] { typeof(double) });
  65. private static MethodInfo intToStringMethodInfo = typeof(TypeCast).GetMethod("IntegerToString", new Type[] { typeof(int) });
  66. private static MethodInfo keyToBoolMethodInfo = typeof(TypeCast).GetMethod("KeyToBool", new Type[] { typeof(string) });
  67. private static MethodInfo listToBoolMethodInfo = typeof(TypeCast).GetMethod("ListToBool", new Type[] { typeof(LSL_List) });
  68. private static MethodInfo listToStringMethodInfo = typeof(TypeCast).GetMethod("ListToString", new Type[] { typeof(LSL_List) });
  69. private static MethodInfo objectToFloatMethodInfo = typeof(TypeCast).GetMethod("ObjectToFloat", new Type[] { typeof(object) });
  70. private static MethodInfo objectToIntegerMethodInfo = typeof(TypeCast).GetMethod("ObjectToInteger", new Type[] { typeof(object) });
  71. private static MethodInfo objectToListMethodInfo = typeof(TypeCast).GetMethod("ObjectToList", new Type[] { typeof(object) });
  72. private static MethodInfo objectToRotationMethodInfo = typeof(TypeCast).GetMethod("ObjectToRotation", new Type[] { typeof(object) });
  73. private static MethodInfo objectToStringMethodInfo = typeof(TypeCast).GetMethod("ObjectToString", new Type[] { typeof(object) });
  74. private static MethodInfo objectToVectorMethodInfo = typeof(TypeCast).GetMethod("ObjectToVector", new Type[] { typeof(object) });
  75. private static MethodInfo rotationToBoolMethodInfo = typeof(TypeCast).GetMethod("RotationToBool", new Type[] { typeof(LSL_Rotation) });
  76. private static MethodInfo rotationToStringMethodInfo = typeof(TypeCast).GetMethod("RotationToString", new Type[] { typeof(LSL_Rotation) });
  77. private static MethodInfo stringToBoolMethodInfo = typeof(TypeCast).GetMethod("StringToBool", new Type[] { typeof(string) });
  78. private static MethodInfo vectorToBoolMethodInfo = typeof(TypeCast).GetMethod("VectorToBool", new Type[] { typeof(LSL_Vector) });
  79. private static MethodInfo vectorToStringMethodInfo = typeof(TypeCast).GetMethod("VectorToString", new Type[] { typeof(LSL_Vector) });
  80. private static MethodInfo sdTypeClassCastClass2ClassMethodInfo = typeof(XMRSDTypeClObj).GetMethod("CastClass2Class", new Type[] { typeof(object), typeof(int) });
  81. private static MethodInfo sdTypeClassCastIFace2ClassMethodInfo = typeof(XMRSDTypeClObj).GetMethod("CastIFace2Class", new Type[] { typeof(Delegate[]), typeof(int) });
  82. private static MethodInfo sdTypeClassCastObj2IFaceMethodInfo = typeof(XMRSDTypeClObj).GetMethod("CastObj2IFace", new Type[] { typeof(object), typeof(string) });
  83. private static MethodInfo charToListMethodInfo = typeof(TypeCast).GetMethod("CharToList", new Type[] { typeof(char) });
  84. private static MethodInfo excToListMethodInfo = typeof(TypeCast).GetMethod("ExcToList", new Type[] { typeof(Exception) });
  85. private static MethodInfo vectorToListMethodInfo = typeof(TypeCast).GetMethod("VectorToList", new Type[] { typeof(LSL_Vector) });
  86. private static MethodInfo floatToListMethodInfo = typeof(TypeCast).GetMethod("FloatToList", new Type[] { typeof(double) });
  87. private static MethodInfo integerToListMethodInfo = typeof(TypeCast).GetMethod("IntegerToList", new Type[] { typeof(int) });
  88. private static MethodInfo rotationToListMethodInfo = typeof(TypeCast).GetMethod("RotationToList", new Type[] { typeof(LSL_Rotation) });
  89. private static MethodInfo stringToListMethodInfo = typeof(TypeCast).GetMethod("StringToList", new Type[] { typeof(string) });
  90. /*
  91. * List of all allowed type casts and how to perform the casting.
  92. */
  93. private static Dictionary<string, CastDelegate> legalTypeCasts = CreateLegalTypeCasts();
  94. /**
  95. * @brief create a dictionary of legal type casts.
  96. * Defines what EXPLICIT type casts are allowed in addition to the IMPLICIT ones.
  97. * Key is of the form <oldtype> <newtype> for IMPLICIT casting.
  98. * Key is of the form <oldtype>*<newtype> for EXPLICIT casting.
  99. * Value is a delegate that generates code to perform the type cast.
  100. */
  101. private static Dictionary<string, CastDelegate> CreateLegalTypeCasts()
  102. {
  103. Dictionary<string, CastDelegate> ltc = new Dictionary<string, CastDelegate>();
  104. // IMPLICIT type casts (a space is in middle of the key)
  105. // EXPLICIT type casts (an * is in middle of the key)
  106. // In general, only mark explicit if it might throw an exception
  107. ltc.Add("array object", TypeCastArray2Object);
  108. ltc.Add("bool float", TypeCastBool2Float);
  109. ltc.Add("bool integer", TypeCastBool2Integer);
  110. ltc.Add("bool list", TypeCastBool2List);
  111. ltc.Add("bool object", TypeCastBool2Object);
  112. ltc.Add("bool string", TypeCastBool2String);
  113. ltc.Add("char integer", TypeCastChar2Integer);
  114. ltc.Add("char list", TypeCastChar2List);
  115. ltc.Add("char object", TypeCastChar2Object);
  116. ltc.Add("char string", TypeCastChar2String);
  117. ltc.Add("exception list", TypeCastExc2List);
  118. ltc.Add("exception object", TypeCastExc2Object);
  119. ltc.Add("exception string", TypeCastExc2String);
  120. ltc.Add("float bool", TypeCastFloat2Bool);
  121. ltc.Add("float integer", TypeCastFloat2Integer);
  122. ltc.Add("float list", TypeCastFloat2List);
  123. ltc.Add("float object", TypeCastFloat2Object);
  124. ltc.Add("float string", TypeCastFloat2String);
  125. ltc.Add("integer bool", TypeCastInteger2Bool);
  126. ltc.Add("integer char", TypeCastInteger2Char);
  127. ltc.Add("integer float", TypeCastInteger2Float);
  128. ltc.Add("integer list", TypeCastInteger2List);
  129. ltc.Add("integer object", TypeCastInteger2Object);
  130. ltc.Add("integer string", TypeCastInteger2String);
  131. ltc.Add("list bool", TypeCastList2Bool);
  132. ltc.Add("list object", TypeCastList2Object);
  133. ltc.Add("list string", TypeCastList2String);
  134. ltc.Add("object*array", TypeCastObject2Array);
  135. ltc.Add("object*bool", TypeCastObject2Bool);
  136. ltc.Add("object*char", TypeCastObject2Char);
  137. ltc.Add("object*exception", TypeCastObject2Exc);
  138. ltc.Add("object*float", TypeCastObject2Float);
  139. ltc.Add("object*integer", TypeCastObject2Integer);
  140. ltc.Add("object*list", TypeCastObject2List);
  141. ltc.Add("object*rotation", TypeCastObject2Rotation);
  142. ltc.Add("object string", TypeCastObject2String);
  143. ltc.Add("object*vector", TypeCastObject2Vector);
  144. ltc.Add("rotation bool", TypeCastRotation2Bool);
  145. ltc.Add("rotation list", TypeCastRotation2List);
  146. ltc.Add("rotation object", TypeCastRotation2Object);
  147. ltc.Add("rotation string", TypeCastRotation2String);
  148. ltc.Add("string bool", TypeCastString2Bool);
  149. ltc.Add("string float", TypeCastString2Float);
  150. ltc.Add("string integer", TypeCastString2Integer);
  151. ltc.Add("string list", TypeCastString2List);
  152. ltc.Add("string object", TypeCastString2Object);
  153. ltc.Add("string rotation", TypeCastString2Rotation);
  154. ltc.Add("string vector", TypeCastString2Vector);
  155. ltc.Add("vector bool", TypeCastVector2Bool);
  156. ltc.Add("vector list", TypeCastVector2List);
  157. ltc.Add("vector object", TypeCastVector2Object);
  158. ltc.Add("vector string", TypeCastVector2String);
  159. return ltc;
  160. }
  161. /**
  162. * @brief See if the given type can be cast to the other implicitly.
  163. * @param dstType = type being cast to
  164. * @param srcType = type being cast from
  165. * @returns false: implicit cast not allowed
  166. * true: implicit cast allowed
  167. */
  168. public static bool IsAssignableFrom(TokenType dstType, TokenType srcType)
  169. {
  170. // Do a 'dry run' of the casting operation, discarding any emits and not printing any errors.
  171. // But if the casting tries to print error(s), return false.
  172. // Otherwise assume the cast is allowed and return true.
  173. SCGIAF scg = new SCGIAF();
  174. scg.ok = true;
  175. scg._ilGen = migiaf;
  176. CastTopOfStack(scg, null, srcType, dstType, false);
  177. return scg.ok;
  178. }
  179. private struct SCGIAF: IScriptCodeGen
  180. {
  181. public bool ok;
  182. public ScriptMyILGen _ilGen;
  183. // IScriptCodeGen
  184. public ScriptMyILGen ilGen
  185. {
  186. get
  187. {
  188. return _ilGen;
  189. }
  190. }
  191. public void ErrorMsg(Token token, string message)
  192. {
  193. ok = false;
  194. }
  195. public void PushDefaultValue(TokenType type)
  196. {
  197. }
  198. public void PushXMRInst()
  199. {
  200. }
  201. }
  202. private static readonly MIGIAF migiaf = new MIGIAF();
  203. private struct MIGIAF: ScriptMyILGen
  204. {
  205. // ScriptMyILGen
  206. public string methName
  207. {
  208. get
  209. {
  210. return null;
  211. }
  212. }
  213. public ScriptMyLocal DeclareLocal(Type type, string name)
  214. {
  215. return null;
  216. }
  217. public ScriptMyLabel DefineLabel(string name)
  218. {
  219. return null;
  220. }
  221. public void BeginExceptionBlock()
  222. {
  223. }
  224. public void BeginCatchBlock(Type excType)
  225. {
  226. }
  227. public void BeginFinallyBlock()
  228. {
  229. }
  230. public void EndExceptionBlock()
  231. {
  232. }
  233. public void Emit(Token errorAt, OpCode opcode)
  234. {
  235. }
  236. public void Emit(Token errorAt, OpCode opcode, FieldInfo field)
  237. {
  238. }
  239. public void Emit(Token errorAt, OpCode opcode, ScriptMyLocal myLocal)
  240. {
  241. }
  242. public void Emit(Token errorAt, OpCode opcode, Type type)
  243. {
  244. }
  245. public void Emit(Token errorAt, OpCode opcode, ScriptMyLabel myLabel)
  246. {
  247. }
  248. public void Emit(Token errorAt, OpCode opcode, ScriptMyLabel[] myLabels)
  249. {
  250. }
  251. public void Emit(Token errorAt, OpCode opcode, ScriptObjWriter method)
  252. {
  253. }
  254. public void Emit(Token errorAt, OpCode opcode, MethodInfo method)
  255. {
  256. }
  257. public void Emit(Token errorAt, OpCode opcode, ConstructorInfo ctor)
  258. {
  259. }
  260. public void Emit(Token errorAt, OpCode opcode, double value)
  261. {
  262. }
  263. public void Emit(Token errorAt, OpCode opcode, float value)
  264. {
  265. }
  266. public void Emit(Token errorAt, OpCode opcode, int value)
  267. {
  268. }
  269. public void Emit(Token errorAt, OpCode opcode, string value)
  270. {
  271. }
  272. public void MarkLabel(ScriptMyLabel myLabel)
  273. {
  274. }
  275. }
  276. /**
  277. * @brief Emit code that converts the top stack item from 'oldType' to 'newType'
  278. * @param scg = what script we are compiling
  279. * @param errorAt = token used for source location for error messages
  280. * @param oldType = type of item currently on the stack
  281. * @param newType = type to convert it to
  282. * @param explicitAllowed = false: only consider implicit casts
  283. * true: consider both implicit and explicit casts
  284. * @returns with code emitted for conversion (or error message output if not allowed, and stack left unchanged)
  285. */
  286. public static void CastTopOfStack(IScriptCodeGen scg, Token errorAt, TokenType oldType, TokenType newType, bool explicitAllowed)
  287. {
  288. CastDelegate castDelegate;
  289. string oldString = oldType.ToString();
  290. string newString = newType.ToString();
  291. // 'key' -> 'bool' is the only time we care about key being different than string.
  292. if((oldString == "key") && (newString == "bool"))
  293. {
  294. LSLUnwrap(scg, errorAt, oldType);
  295. scg.ilGen.Emit(errorAt, OpCodes.Call, keyToBoolMethodInfo);
  296. LSLWrap(scg, errorAt, newType);
  297. return;
  298. }
  299. // Treat key and string as same type for all other type casts.
  300. if(oldString == "key")
  301. oldString = "string";
  302. if(newString == "key")
  303. newString = "string";
  304. // If the types are the same, there is no conceptual casting needed.
  305. // However, there may be wraping/unwraping to/from the LSL wrappers.
  306. if(oldString == newString)
  307. {
  308. if(oldType.ToLSLWrapType() != newType.ToLSLWrapType())
  309. {
  310. LSLUnwrap(scg, errorAt, oldType);
  311. LSLWrap(scg, errorAt, newType);
  312. }
  313. return;
  314. }
  315. // Script-defined classes can be cast up and down the tree.
  316. if((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeClass))
  317. {
  318. TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
  319. TokenDeclSDTypeClass newSDTC = ((TokenTypeSDTypeClass)newType).decl;
  320. // implicit cast allowed from leaf toward root
  321. for(TokenDeclSDTypeClass sdtc = oldSDTC; sdtc != null; sdtc = sdtc.extends)
  322. {
  323. if(sdtc == newSDTC)
  324. return;
  325. }
  326. // explicit cast allowed from root toward leaf
  327. for(TokenDeclSDTypeClass sdtc = newSDTC; sdtc != null; sdtc = sdtc.extends)
  328. {
  329. if(sdtc == oldSDTC)
  330. {
  331. ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
  332. scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, newSDTC.sdTypeIndex);
  333. scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
  334. return;
  335. }
  336. }
  337. // not on same branch
  338. goto illcast;
  339. }
  340. // One script-defined interface type cannot be cast to another script-defined interface type,
  341. // unless the old interface declares that it implements the new interface. That proves that
  342. // the underlying object, no matter what type, implements the new interface.
  343. if((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeInterface))
  344. {
  345. TokenDeclSDTypeInterface oldDecl = ((TokenTypeSDTypeInterface)oldType).decl;
  346. TokenDeclSDTypeInterface newDecl = ((TokenTypeSDTypeInterface)newType).decl;
  347. if(!oldDecl.Implements(newDecl))
  348. goto illcast;
  349. scg.ilGen.Emit(errorAt, OpCodes.Ldstr, newType.ToString());
  350. scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
  351. return;
  352. }
  353. // A script-defined class type can be implicitly cast to a script-defined interface type that it
  354. // implements. The result is an array of delegates that give the class's implementation of the
  355. // various methods defined by the interface.
  356. if((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeInterface))
  357. {
  358. TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
  359. int intfIndex;
  360. if(!oldSDTC.intfIndices.TryGetValue(newType.ToString(), out intfIndex))
  361. goto illcast;
  362. scg.ilGen.Emit(errorAt, OpCodes.Ldfld, sdtcITableFieldInfo);
  363. scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, intfIndex);
  364. scg.ilGen.Emit(errorAt, OpCodes.Ldelem, typeof(Delegate[]));
  365. return;
  366. }
  367. // A script-defined interface type can be explicitly cast to a script-defined class type by
  368. // extracting the Target property from element 0 of the delegate array that is the interface
  369. // object and making sure it casts to the correct script-defined class type.
  370. //
  371. // But then only if the class type implements the interface type.
  372. if((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeClass))
  373. {
  374. TokenTypeSDTypeInterface oldSDTI = (TokenTypeSDTypeInterface)oldType;
  375. TokenTypeSDTypeClass newSDTC = (TokenTypeSDTypeClass)newType;
  376. if(!newSDTC.decl.CanCastToIntf(oldSDTI.decl))
  377. goto illcast;
  378. ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
  379. scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, newSDTC.decl.sdTypeIndex);
  380. scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastIFace2ClassMethodInfo);
  381. return;
  382. }
  383. // A script-defined interface type can be implicitly cast to object.
  384. if((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeObject))
  385. {
  386. return;
  387. }
  388. // An object can be explicitly cast to a script-defined interface.
  389. if((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeInterface))
  390. {
  391. ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
  392. scg.ilGen.Emit(errorAt, OpCodes.Ldstr, newString);
  393. scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
  394. return;
  395. }
  396. // Cast to void is always allowed, such as discarding value from 'i++' or function return value.
  397. if(newType is TokenTypeVoid)
  398. {
  399. scg.ilGen.Emit(errorAt, OpCodes.Pop);
  400. return;
  401. }
  402. // Cast from undef to object or script-defined type is always allowed.
  403. if((oldType is TokenTypeUndef) &&
  404. ((newType is TokenTypeObject) ||
  405. (newType is TokenTypeSDTypeClass) ||
  406. (newType is TokenTypeSDTypeInterface)))
  407. {
  408. return;
  409. }
  410. // Script-defined classes can be implicitly cast to objects.
  411. if((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeObject))
  412. {
  413. return;
  414. }
  415. // Script-defined classes can be explicitly cast from objects and other script-defined classes.
  416. // Note that we must manually check that it is the correct SDTypeClass however because as far as
  417. // mono is concerned, all SDTypeClass's are the same.
  418. if((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeClass))
  419. {
  420. ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
  421. scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, ((TokenTypeSDTypeClass)newType).decl.sdTypeIndex);
  422. scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
  423. return;
  424. }
  425. // Delegates can be implicitly cast to/from objects.
  426. if((oldType is TokenTypeSDTypeDelegate) && (newType is TokenTypeObject))
  427. {
  428. return;
  429. }
  430. if((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeDelegate))
  431. {
  432. scg.ilGen.Emit(errorAt, OpCodes.Castclass, newType.ToSysType());
  433. return;
  434. }
  435. // Some actual conversion is needed, see if it is in table of legal casts.
  436. string key = oldString + " " + newString;
  437. if(!legalTypeCasts.TryGetValue(key, out castDelegate))
  438. {
  439. key = oldString + "*" + newString;
  440. if(!legalTypeCasts.TryGetValue(key, out castDelegate))
  441. goto illcast;
  442. ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
  443. }
  444. // Ok, output cast. But make sure it is in native form without any LSL wrapping
  445. // before passing to our casting routine. Then if caller is expecting an LSL-
  446. // wrapped value on the stack upon return, wrap it up after our casting.
  447. LSLUnwrap(scg, errorAt, oldType);
  448. castDelegate(scg, errorAt);
  449. LSLWrap(scg, errorAt, newType);
  450. return;
  451. illcast:
  452. scg.ErrorMsg(errorAt, "illegal to cast from " + oldString + " to " + newString);
  453. if(!(oldType is TokenTypeVoid))
  454. scg.ilGen.Emit(errorAt, OpCodes.Pop);
  455. scg.PushDefaultValue(newType);
  456. }
  457. private static void ExplCheck(IScriptCodeGen scg, Token errorAt, bool explicitAllowed, string oldString, string newString)
  458. {
  459. if(!explicitAllowed)
  460. {
  461. scg.ErrorMsg(errorAt, "must explicitly cast from " + oldString + " to " + newString);
  462. }
  463. }
  464. /**
  465. * @brief If value on the stack is an LSL-style wrapped value, unwrap it.
  466. */
  467. public static void LSLUnwrap(IScriptCodeGen scg, Token errorAt, TokenType type)
  468. {
  469. if(type.ToLSLWrapType() == typeof(LSL_Float))
  470. {
  471. scg.ilGen.Emit(errorAt, OpCodes.Ldfld, lslFloatValueFieldInfo);
  472. }
  473. if(type.ToLSLWrapType() == typeof(LSL_Integer))
  474. {
  475. scg.ilGen.Emit(errorAt, OpCodes.Ldfld, lslIntegerValueFieldInfo);
  476. }
  477. if(type.ToLSLWrapType() == typeof(LSL_String))
  478. {
  479. scg.ilGen.Emit(errorAt, OpCodes.Ldfld, lslStringValueFieldInfo);
  480. }
  481. }
  482. /**
  483. * @brief If caller wants the unwrapped value on stack wrapped LSL-style, wrap it.
  484. */
  485. private static void LSLWrap(IScriptCodeGen scg, Token errorAt, TokenType type)
  486. {
  487. if(type.ToLSLWrapType() == typeof(LSL_Float))
  488. {
  489. scg.ilGen.Emit(errorAt, OpCodes.Newobj, lslFloatConstructorInfo);
  490. }
  491. if(type.ToLSLWrapType() == typeof(LSL_Integer))
  492. {
  493. scg.ilGen.Emit(errorAt, OpCodes.Newobj, lslIntegerConstructorInfo);
  494. }
  495. if(type.ToLSLWrapType() == typeof(LSL_String))
  496. {
  497. scg.ilGen.Emit(errorAt, OpCodes.Newobj, lslStringConstructorInfo);
  498. }
  499. }
  500. /**
  501. * @brief These routines output code to perform casting.
  502. * They can assume there are no LSL wrapped values on input
  503. * and they should not output an LSL wrapped value.
  504. */
  505. private static void TypeCastArray2Object(IScriptCodeGen scg, Token errorAt)
  506. {
  507. }
  508. private static void TypeCastBool2Float(IScriptCodeGen scg, Token errorAt)
  509. {
  510. if(typeof(double) == typeof(float))
  511. {
  512. scg.ilGen.Emit(errorAt, OpCodes.Conv_R4);
  513. }
  514. else if(typeof(double) == typeof(double))
  515. {
  516. scg.ilGen.Emit(errorAt, OpCodes.Conv_R8);
  517. }
  518. else
  519. {
  520. throw new Exception("unknown type");
  521. }
  522. }
  523. private static void TypeCastBool2Integer(IScriptCodeGen scg, Token errorAt)
  524. {
  525. }
  526. private static void TypeCastBool2Object(IScriptCodeGen scg, Token errorAt)
  527. {
  528. scg.ilGen.Emit(errorAt, OpCodes.Box, typeof(bool));
  529. }
  530. private static void TypeCastChar2Integer(IScriptCodeGen scg, Token errorAt)
  531. {
  532. }
  533. private static void TypeCastChar2List(IScriptCodeGen scg, Token errorAt)
  534. {
  535. scg.ilGen.Emit(errorAt, OpCodes.Call, charToListMethodInfo);
  536. }
  537. private static void TypeCastChar2Object(IScriptCodeGen scg, Token errorAt)
  538. {
  539. scg.ilGen.Emit(errorAt, OpCodes.Box, typeof(char));
  540. }
  541. private static void TypeCastChar2String(IScriptCodeGen scg, Token errorAt)
  542. {
  543. scg.ilGen.Emit(errorAt, OpCodes.Call, charToStringMethodInfo);
  544. }
  545. private static void TypeCastExc2List(IScriptCodeGen scg, Token errorAt)
  546. {
  547. scg.ilGen.Emit(errorAt, OpCodes.Call, excToListMethodInfo);
  548. }
  549. private static void TypeCastExc2Object(IScriptCodeGen scg, Token errorAt)
  550. {
  551. }
  552. private static void TypeCastExc2String(IScriptCodeGen scg, Token errorAt)
  553. {
  554. scg.PushXMRInst();
  555. scg.ilGen.Emit(errorAt, OpCodes.Call, excToStringMethodInfo);
  556. }
  557. private static void TypeCastFloat2Bool(IScriptCodeGen scg, Token errorAt)
  558. {
  559. scg.ilGen.Emit(errorAt, OpCodes.Ldc_R4, 0.0f);
  560. scg.ilGen.Emit(errorAt, OpCodes.Ceq);
  561. scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4_1);
  562. scg.ilGen.Emit(errorAt, OpCodes.Xor);
  563. }
  564. private static void TypeCastFloat2Integer(IScriptCodeGen scg, Token errorAt)
  565. {
  566. scg.ilGen.Emit(errorAt, OpCodes.Conv_I4);
  567. }
  568. private static void TypeCastFloat2Object(IScriptCodeGen scg, Token errorAt)
  569. {
  570. scg.ilGen.Emit(errorAt, OpCodes.Box, typeof(double));
  571. }
  572. private static void TypeCastInteger2Bool(IScriptCodeGen scg, Token errorAt)
  573. {
  574. scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4_0);
  575. scg.ilGen.Emit(errorAt, OpCodes.Ceq);
  576. scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4_1);
  577. scg.ilGen.Emit(errorAt, OpCodes.Xor);
  578. }
  579. private static void TypeCastInteger2Char(IScriptCodeGen scg, Token errorAt)
  580. {
  581. }
  582. private static void TypeCastInteger2Float(IScriptCodeGen scg, Token errorAt)
  583. {
  584. if(typeof(double) == typeof(float))
  585. {
  586. scg.ilGen.Emit(errorAt, OpCodes.Conv_R4);
  587. }
  588. else if(typeof(double) == typeof(double))
  589. {
  590. scg.ilGen.Emit(errorAt, OpCodes.Conv_R8);
  591. }
  592. else
  593. {
  594. throw new Exception("unknown type");
  595. }
  596. }
  597. private static void TypeCastInteger2Object(IScriptCodeGen scg, Token errorAt)
  598. {
  599. scg.ilGen.Emit(errorAt, OpCodes.Box, typeof(int));
  600. }
  601. private static void TypeCastList2Bool(IScriptCodeGen scg, Token errorAt)
  602. {
  603. scg.ilGen.Emit(errorAt, OpCodes.Call, listToBoolMethodInfo);
  604. }
  605. private static void TypeCastList2Object(IScriptCodeGen scg, Token errorAt)
  606. {
  607. if(typeof(LSL_List).IsValueType)
  608. {
  609. scg.ilGen.Emit(errorAt, OpCodes.Box, typeof(LSL_List));
  610. }
  611. }
  612. private static void TypeCastObject2Array(IScriptCodeGen scg, Token errorAt)
  613. {
  614. scg.ilGen.Emit(errorAt, OpCodes.Castclass, typeof(XMR_Array));
  615. }
  616. private static void TypeCastObject2Bool(IScriptCodeGen scg, Token errorAt)
  617. {
  618. scg.ilGen.Emit(errorAt, OpCodes.Unbox_Any, typeof(bool));
  619. }
  620. private static void TypeCastObject2Char(IScriptCodeGen scg, Token errorAt)
  621. {
  622. scg.ilGen.Emit(errorAt, OpCodes.Unbox_Any, typeof(char));
  623. }
  624. private static void TypeCastObject2Exc(IScriptCodeGen scg, Token errorAt)
  625. {
  626. scg.ilGen.Emit(errorAt, OpCodes.Castclass, typeof(Exception));
  627. }
  628. private static void TypeCastObject2Float(IScriptCodeGen scg, Token errorAt)
  629. {
  630. scg.ilGen.Emit(errorAt, OpCodes.Call, objectToFloatMethodInfo);
  631. }
  632. private static void TypeCastObject2Integer(IScriptCodeGen scg, Token errorAt)
  633. {
  634. scg.ilGen.Emit(errorAt, OpCodes.Call, objectToIntegerMethodInfo);
  635. }
  636. private static void TypeCastObject2List(IScriptCodeGen scg, Token errorAt)
  637. {
  638. if(typeof(LSL_List).IsValueType)
  639. {
  640. scg.ilGen.Emit(errorAt, OpCodes.Call, objectToListMethodInfo);
  641. }
  642. else
  643. {
  644. scg.ilGen.Emit(errorAt, OpCodes.Castclass, typeof(LSL_List));
  645. }
  646. }
  647. private static void TypeCastObject2Rotation(IScriptCodeGen scg, Token errorAt)
  648. {
  649. scg.ilGen.Emit(errorAt, OpCodes.Call, objectToRotationMethodInfo);
  650. }
  651. private static void TypeCastObject2Vector(IScriptCodeGen scg, Token errorAt)
  652. {
  653. scg.ilGen.Emit(errorAt, OpCodes.Call, objectToVectorMethodInfo);
  654. }
  655. private static void TypeCastRotation2Bool(IScriptCodeGen scg, Token errorAt)
  656. {
  657. scg.ilGen.Emit(errorAt, OpCodes.Call, rotationToBoolMethodInfo);
  658. }
  659. private static void TypeCastRotation2Object(IScriptCodeGen scg, Token errorAt)
  660. {
  661. scg.ilGen.Emit(errorAt, OpCodes.Box, typeof(LSL_Rotation));
  662. }
  663. private static void TypeCastString2Bool(IScriptCodeGen scg, Token errorAt)
  664. {
  665. scg.ilGen.Emit(errorAt, OpCodes.Call, stringToBoolMethodInfo);
  666. }
  667. private static void TypeCastString2Object(IScriptCodeGen scg, Token errorAt)
  668. {
  669. }
  670. private static void TypeCastString2Rotation(IScriptCodeGen scg, Token errorAt)
  671. {
  672. scg.ilGen.Emit(errorAt, OpCodes.Newobj, rotationConstrucorStringInfo);
  673. }
  674. private static void TypeCastString2Vector(IScriptCodeGen scg, Token errorAt)
  675. {
  676. scg.ilGen.Emit(errorAt, OpCodes.Newobj, vectorConstrucorStringInfo);
  677. }
  678. private static void TypeCastVector2Bool(IScriptCodeGen scg, Token errorAt)
  679. {
  680. scg.ilGen.Emit(errorAt, OpCodes.Call, vectorToBoolMethodInfo);
  681. }
  682. private static void TypeCastVector2List(IScriptCodeGen scg, Token errorAt)
  683. {
  684. scg.ilGen.Emit(errorAt, OpCodes.Call, vectorToListMethodInfo);
  685. }
  686. private static void TypeCastVector2Object(IScriptCodeGen scg, Token errorAt)
  687. {
  688. scg.ilGen.Emit(errorAt, OpCodes.Box, typeof(LSL_Vector));
  689. }
  690. private static void TypeCastBool2List(IScriptCodeGen scg, Token errorAt)
  691. {
  692. scg.ilGen.Emit(errorAt, OpCodes.Call, boolToListMethodInfo);
  693. }
  694. private static void TypeCastBool2String(IScriptCodeGen scg, Token errorAt)
  695. {
  696. scg.ilGen.Emit(errorAt, OpCodes.Call, boolToStringMethodInfo);
  697. }
  698. private static void TypeCastFloat2List(IScriptCodeGen scg, Token errorAt)
  699. {
  700. scg.ilGen.Emit(errorAt, OpCodes.Call, floatToListMethodInfo);
  701. }
  702. private static void TypeCastFloat2String(IScriptCodeGen scg, Token errorAt)
  703. {
  704. scg.ilGen.Emit(errorAt, OpCodes.Call, floatToStringMethodInfo);
  705. }
  706. private static void TypeCastInteger2List(IScriptCodeGen scg, Token errorAt)
  707. {
  708. scg.ilGen.Emit(errorAt, OpCodes.Call, integerToListMethodInfo);
  709. }
  710. private static void TypeCastInteger2String(IScriptCodeGen scg, Token errorAt)
  711. {
  712. scg.ilGen.Emit(errorAt, OpCodes.Call, intToStringMethodInfo);
  713. }
  714. private static void TypeCastList2String(IScriptCodeGen scg, Token errorAt)
  715. {
  716. scg.ilGen.Emit(errorAt, OpCodes.Call, listToStringMethodInfo);
  717. }
  718. private static void TypeCastObject2String(IScriptCodeGen scg, Token errorAt)
  719. {
  720. scg.ilGen.Emit(errorAt, OpCodes.Call, objectToStringMethodInfo);
  721. }
  722. private static void TypeCastRotation2List(IScriptCodeGen scg, Token errorAt)
  723. {
  724. scg.ilGen.Emit(errorAt, OpCodes.Call, rotationToListMethodInfo);
  725. }
  726. private static void TypeCastRotation2String(IScriptCodeGen scg, Token errorAt)
  727. {
  728. scg.ilGen.Emit(errorAt, OpCodes.Call, rotationToStringMethodInfo);
  729. }
  730. private static void TypeCastString2Float(IScriptCodeGen scg, Token errorAt)
  731. {
  732. scg.ilGen.Emit(errorAt, OpCodes.Newobj, floatConstructorStringInfo);
  733. scg.ilGen.Emit(errorAt, OpCodes.Ldfld, lslFloatValueFieldInfo);
  734. }
  735. private static void TypeCastString2Integer(IScriptCodeGen scg, Token errorAt)
  736. {
  737. scg.ilGen.Emit(errorAt, OpCodes.Newobj, integerConstructorStringInfo);
  738. scg.ilGen.Emit(errorAt, OpCodes.Ldfld, lslIntegerValueFieldInfo);
  739. }
  740. private static void TypeCastString2List(IScriptCodeGen scg, Token errorAt)
  741. {
  742. scg.ilGen.Emit(errorAt, OpCodes.Call, stringToListMethodInfo);
  743. }
  744. private static void TypeCastVector2String(IScriptCodeGen scg, Token errorAt)
  745. {
  746. scg.ilGen.Emit(errorAt, OpCodes.Call, vectorToStringMethodInfo);
  747. }
  748. /*
  749. * Because the calls are funky, let the compiler handle them.
  750. */
  751. public static bool RotationToBool(LSL_Rotation x)
  752. {
  753. return !x.Equals(ScriptBaseClass.ZERO_ROTATION);
  754. }
  755. public static bool StringToBool(string x)
  756. {
  757. return x.Length > 0;
  758. }
  759. public static bool VectorToBool(LSL_Vector x)
  760. {
  761. return !x.Equals(ScriptBaseClass.ZERO_VECTOR);
  762. }
  763. public static string BoolToString(bool x)
  764. {
  765. return x ? "1" : "0";
  766. }
  767. public static string CharToString(char x)
  768. {
  769. return x.ToString();
  770. }
  771. public static string FloatToString(double x)
  772. {
  773. return x.ToString("0.000000",CultureInfo.InvariantCulture);
  774. }
  775. public static string IntegerToString(int x)
  776. {
  777. return x.ToString();
  778. }
  779. public static bool KeyToBool(string x)
  780. {
  781. return (x != "") && (x != ScriptBaseClass.NULL_KEY);
  782. }
  783. public static bool ListToBool(LSL_List x)
  784. {
  785. return x.Length != 0;
  786. }
  787. public static string ListToString(LSL_List x)
  788. {
  789. return x.ToString();
  790. }
  791. public static string ObjectToString(object x)
  792. {
  793. return (x == null) ? null : x.ToString();
  794. }
  795. public static string RotationToString(LSL_Rotation x)
  796. {
  797. return x.ToString();
  798. }
  799. public static string VectorToString(LSL_Vector x)
  800. {
  801. return x.ToString();
  802. }
  803. public static LSL_List BoolToList(bool b)
  804. {
  805. return new LSL_List(new object[] { new LSL_Integer(b ? 1 : 0) });
  806. }
  807. public static LSL_List CharToList(char c)
  808. {
  809. return new LSL_List(new object[] { new LSL_Integer(c) });
  810. }
  811. public static LSL_List ExcToList(Exception e)
  812. {
  813. return new LSL_List(new object[] { e });
  814. }
  815. public static LSL_List VectorToList(LSL_Vector v)
  816. {
  817. return new LSL_List(new object[] { v });
  818. }
  819. public static LSL_List FloatToList(double f)
  820. {
  821. return new LSL_List(new object[] { new LSL_Float(f) });
  822. }
  823. public static LSL_List IntegerToList(int i)
  824. {
  825. return new LSL_List(new object[] { new LSL_Integer(i) });
  826. }
  827. public static LSL_List RotationToList(LSL_Rotation r)
  828. {
  829. return new LSL_List(new object[] { r });
  830. }
  831. public static LSL_List StringToList(string s)
  832. {
  833. return new LSL_List(new object[] { new LSL_String(s) });
  834. }
  835. public static double ObjectToFloat(object x)
  836. {
  837. if(x is LSL_String)
  838. return double.Parse(((LSL_String)x).m_string);
  839. if(x is string)
  840. return double.Parse((string)x);
  841. if(x is LSL_Float)
  842. return (double)(LSL_Float)x;
  843. if(x is LSL_Integer)
  844. return (double)(int)(LSL_Integer)x;
  845. if(x is int)
  846. return (double)(int)x;
  847. return (double)x;
  848. }
  849. public static int ObjectToInteger(object x)
  850. {
  851. if(x is LSL_String)
  852. return int.Parse(((LSL_String)x).m_string);
  853. if(x is string)
  854. return int.Parse((string)x);
  855. if(x is LSL_Integer)
  856. return (int)(LSL_Integer)x;
  857. return (int)x;
  858. }
  859. public static LSL_List ObjectToList(object x)
  860. {
  861. return (LSL_List)x;
  862. }
  863. public static LSL_Rotation ObjectToRotation(object x)
  864. {
  865. if(x is LSL_String)
  866. return new LSL_Rotation(((LSL_String)x).m_string);
  867. if(x is string)
  868. return new LSL_Rotation((string)x);
  869. return (LSL_Rotation)x;
  870. }
  871. public static LSL_Vector ObjectToVector(object x)
  872. {
  873. if(x is LSL_String)
  874. return new LSL_Vector(((LSL_String)x).m_string);
  875. if(x is string)
  876. return new LSL_Vector((string)x);
  877. return (LSL_Vector)x;
  878. }
  879. public static string ExceptionToString(Exception x, XMRInstAbstract inst)
  880. {
  881. return XMRInstAbstract.xmrExceptionTypeName(x) + ": " + XMRInstAbstract.xmrExceptionMessage(x) +
  882. "\n" + inst.xmrExceptionStackTrace(x);
  883. }
  884. /*
  885. * These are used by event handler entrypoints to remove any LSL wrapping
  886. * from the argument list and return the unboxed/unwrapped value.
  887. */
  888. public static double EHArgUnwrapFloat(object x)
  889. {
  890. if(x is LSL_Float)
  891. return (double)(LSL_Float)x;
  892. return (double)x;
  893. }
  894. public static int EHArgUnwrapInteger(object x)
  895. {
  896. if(x is LSL_Integer)
  897. return (int)(LSL_Integer)x;
  898. return (int)x;
  899. }
  900. public static LSL_Rotation EHArgUnwrapRotation(object x)
  901. {
  902. if(x is OpenMetaverse.Quaternion)
  903. {
  904. OpenMetaverse.Quaternion q = (OpenMetaverse.Quaternion)x;
  905. return new LSL_Rotation(q.X, q.Y, q.Z, q.W);
  906. }
  907. return (LSL_Rotation)x;
  908. }
  909. public static string EHArgUnwrapString(object x)
  910. {
  911. if(x is LSL_Key)
  912. return (string)(LSL_Key)x;
  913. if(x is LSL_String)
  914. return (string)(LSL_String)x;
  915. return (string)x;
  916. }
  917. public static LSL_Vector EHArgUnwrapVector(object x)
  918. {
  919. if(x is OpenMetaverse.Vector3)
  920. {
  921. OpenMetaverse.Vector3 v = (OpenMetaverse.Vector3)x;
  922. return new LSL_Vector(v.X, v.Y, v.Z);
  923. }
  924. return (LSL_Vector)x;
  925. }
  926. }
  927. }