PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/IronPython/IronPython.Modules/_ctypes/StructType.cs

http://github.com/IronLanguages/main
C# | 478 lines | 367 code | 75 blank | 36 comment | 78 complexity | d24105c962e7e70b1c0d16091e63be29 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if FEATURE_NATIVE
  16. using System;
  17. using System.Collections;
  18. using System.Collections.Generic;
  19. using System.Diagnostics;
  20. using System.Reflection.Emit;
  21. using System.Runtime.InteropServices;
  22. using Microsoft.Scripting;
  23. using Microsoft.Scripting.Runtime;
  24. using IronPython.Runtime;
  25. using IronPython.Runtime.Operations;
  26. using IronPython.Runtime.Types;
  27. using System.Text;
  28. #if CLR2
  29. using Microsoft.Scripting.Math;
  30. #else
  31. using System.Numerics;
  32. using Microsoft.Scripting.Utils;
  33. #endif
  34. namespace IronPython.Modules {
  35. /// <summary>
  36. /// Provides support for interop with native code from Python code.
  37. /// </summary>
  38. public static partial class CTypes {
  39. /// <summary>
  40. /// Meta class for structures. Validates _fields_ on creation, provides factory
  41. /// methods for creating instances from addresses and translating to parameters.
  42. /// </summary>
  43. [PythonType, PythonHidden]
  44. public class StructType : PythonType, INativeType {
  45. internal Field[] _fields;
  46. private int? _size, _alignment, _pack;
  47. private static readonly Field[] _emptyFields = new Field[0]; // fields were never initialized before a type was created
  48. public StructType(CodeContext/*!*/ context, string name, PythonTuple bases, PythonDictionary members)
  49. : base(context, name, bases, members) {
  50. foreach (PythonType pt in ResolutionOrder) {
  51. StructType st = pt as StructType;
  52. if (st != this && st != null) {
  53. st.EnsureFinal();
  54. }
  55. UnionType ut = pt as UnionType;
  56. if (ut != null) {
  57. ut.EnsureFinal();
  58. }
  59. }
  60. object pack;
  61. if (members.TryGetValue("_pack_", out pack)) {
  62. if (!(pack is int) || ((int)pack < 0)) {
  63. throw PythonOps.ValueError("pack must be a non-negative integer");
  64. }
  65. _pack = (int)pack;
  66. }
  67. object fields;
  68. if (members.TryGetValue("_fields_", out fields)) {
  69. // When we support alternate endianness this should change to:
  70. //__setattr__(context, "_fields_", fields);
  71. SetFields(fields);
  72. }
  73. // TODO: _anonymous_
  74. }
  75. private StructType(Type underlyingSystemType)
  76. : base(underlyingSystemType) {
  77. }
  78. public static ArrayType/*!*/ operator *(StructType type, int count) {
  79. return MakeArrayType(type, count);
  80. }
  81. public static ArrayType/*!*/ operator *(int count, StructType type) {
  82. return MakeArrayType(type, count);
  83. }
  84. public _Structure from_address(CodeContext/*!*/ context, int address) {
  85. return from_address(context, new IntPtr(address));
  86. }
  87. public _Structure from_address(CodeContext/*!*/ context, BigInteger address) {
  88. return from_address(context, new IntPtr((long)address));
  89. }
  90. public _Structure from_address(CodeContext/*!*/ context, IntPtr ptr) {
  91. _Structure res = (_Structure)CreateInstance(context);
  92. res.SetAddress(ptr);
  93. return res;
  94. }
  95. public _Structure from_buffer(ArrayModule.array array, [DefaultParameterValue(0)]int offset) {
  96. ValidateArraySizes(array, offset, ((INativeType)this).Size);
  97. _Structure res = (_Structure)CreateInstance(Context.SharedContext);
  98. IntPtr addr = array.GetArrayAddress();
  99. res._memHolder = new MemoryHolder(addr.Add(offset), ((INativeType)this).Size);
  100. res._memHolder.AddObject("ffffffff", array);
  101. return res;
  102. }
  103. public _Structure from_buffer_copy(ArrayModule.array array, [DefaultParameterValue(0)]int offset) {
  104. ValidateArraySizes(array, offset, ((INativeType)this).Size);
  105. _Structure res = (_Structure)CreateInstance(Context.SharedContext);
  106. res._memHolder = new MemoryHolder(((INativeType)this).Size);
  107. res._memHolder.CopyFrom(array.GetArrayAddress().Add(offset), new IntPtr(((INativeType)this).Size));
  108. GC.KeepAlive(array);
  109. return res;
  110. }
  111. /// <summary>
  112. /// Converts an object into a function call parameter.
  113. ///
  114. /// Structures just return themselves.
  115. /// </summary>
  116. public object from_param(object obj) {
  117. if (!Builtin.isinstance(obj, this)) {
  118. throw PythonOps.TypeError("expected {0} instance got {1}", Name, PythonTypeOps.GetName(obj));
  119. }
  120. return obj;
  121. }
  122. public object in_dll(object library, string name) {
  123. throw new NotImplementedException("in dll");
  124. }
  125. public new virtual void __setattr__(CodeContext/*!*/ context, string name, object value) {
  126. if (name == "_fields_") {
  127. lock (this) {
  128. if (_fields != null) {
  129. throw PythonOps.AttributeError("_fields_ is final");
  130. }
  131. SetFields(value);
  132. }
  133. }
  134. base.__setattr__(context, name, value);
  135. }
  136. #region INativeType Members
  137. int INativeType.Size {
  138. get {
  139. EnsureSizeAndAlignment();
  140. return _size.Value;
  141. }
  142. }
  143. int INativeType.Alignment {
  144. get {
  145. EnsureSizeAndAlignment();
  146. return _alignment.Value;
  147. }
  148. }
  149. object INativeType.GetValue(MemoryHolder/*!*/ owner, object readingFrom, int offset, bool raw) {
  150. _Structure res = (_Structure)CreateInstance(this.Context.SharedContext);
  151. res._memHolder = owner.GetSubBlock(offset);
  152. return res;
  153. }
  154. object INativeType.SetValue(MemoryHolder/*!*/ address, int offset, object value) {
  155. try {
  156. return SetValueInternal(address, offset, value);
  157. } catch (ArgumentTypeException e) {
  158. throw PythonOps.RuntimeError("({0}) <type 'exceptions.TypeError'>: {1}",
  159. Name,
  160. e.Message);
  161. } catch (ArgumentException e) {
  162. throw PythonOps.RuntimeError("({0}) <type 'exceptions.ValueError'>: {1}",
  163. Name,
  164. e.Message);
  165. }
  166. }
  167. internal object SetValueInternal(MemoryHolder address, int offset, object value) {
  168. IList<object> init = value as IList<object>;
  169. if (init != null) {
  170. if (init.Count > _fields.Length) {
  171. throw PythonOps.TypeError("too many initializers");
  172. }
  173. for (int i = 0; i < init.Count; i++) {
  174. _fields[i].SetValue(address, offset, init[i]);
  175. }
  176. } else {
  177. CData data = value as CData;
  178. if (data != null) {
  179. data._memHolder.CopyTo(address, offset, data.Size);
  180. return data._memHolder.EnsureObjects();
  181. } else {
  182. throw new NotImplementedException("set value");
  183. }
  184. }
  185. return null;
  186. }
  187. Type/*!*/ INativeType.GetNativeType() {
  188. EnsureFinal();
  189. return GetMarshalTypeFromSize(_size.Value);
  190. }
  191. MarshalCleanup INativeType.EmitMarshalling(ILGenerator/*!*/ method, LocalOrArg argIndex, List<object>/*!*/ constantPool, int constantPoolArgument) {
  192. Type argumentType = argIndex.Type;
  193. argIndex.Emit(method);
  194. if (argumentType.IsValueType) {
  195. method.Emit(OpCodes.Box, argumentType);
  196. }
  197. constantPool.Add(this);
  198. method.Emit(OpCodes.Ldarg, constantPoolArgument);
  199. method.Emit(OpCodes.Ldc_I4, constantPool.Count - 1);
  200. method.Emit(OpCodes.Ldelem_Ref);
  201. method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod("CheckCDataType"));
  202. method.Emit(OpCodes.Call, typeof(CData).GetMethod("get_UnsafeAddress"));
  203. method.Emit(OpCodes.Ldobj, ((INativeType)this).GetNativeType());
  204. return null;
  205. }
  206. Type/*!*/ INativeType.GetPythonType() {
  207. return typeof(object);
  208. }
  209. void INativeType.EmitReverseMarshalling(ILGenerator method, LocalOrArg value, List<object> constantPool, int constantPoolArgument) {
  210. value.Emit(method);
  211. EmitCDataCreation(this, method, constantPool, constantPoolArgument);
  212. }
  213. string INativeType.TypeFormat {
  214. get {
  215. if (_pack != null || _fields == _emptyFields || _fields == null) {
  216. return "B";
  217. }
  218. StringBuilder res = new StringBuilder();
  219. res.Append("T{");
  220. foreach (Field f in _fields) {
  221. res.Append(f.NativeType.TypeFormat);
  222. res.Append(':');
  223. res.Append(f.FieldName);
  224. res.Append(':');
  225. }
  226. res.Append('}');
  227. return res.ToString();
  228. }
  229. }
  230. #endregion
  231. internal static PythonType MakeSystemType(Type underlyingSystemType) {
  232. return PythonType.SetPythonType(underlyingSystemType, new StructType(underlyingSystemType));
  233. }
  234. private void SetFields(object fields) {
  235. lock (this) {
  236. IList<object> list = GetFieldsList(fields);
  237. int size;
  238. int alignment;
  239. int? bitCount = null;
  240. int? curBitCount = null;
  241. INativeType lastType = null;
  242. List<Field> allFields = GetBaseSizeAlignmentAndFields(out size, out alignment);
  243. IList<object> anonFields = GetAnonymousFields(this);
  244. for (int fieldIndex = 0; fieldIndex < list.Count; fieldIndex++) {
  245. object o = list[fieldIndex];
  246. string fieldName;
  247. INativeType cdata;
  248. GetFieldInfo(this, o, out fieldName, out cdata, out bitCount);
  249. int prevSize = UpdateSizeAndAlignment(cdata, bitCount, lastType, ref size, ref alignment, ref curBitCount);
  250. Field newField = new Field(fieldName, cdata, prevSize, allFields.Count, bitCount, curBitCount - bitCount);
  251. allFields.Add(newField);
  252. AddSlot(fieldName, newField);
  253. if (anonFields != null && anonFields.Contains(fieldName)) {
  254. AddAnonymousFields(this, allFields, cdata, newField);
  255. }
  256. lastType = cdata;
  257. }
  258. CheckAnonymousFields(allFields, anonFields);
  259. if (bitCount != null) {
  260. size += lastType.Size;
  261. }
  262. _fields = allFields.ToArray();
  263. _size = PythonStruct.Align(size, alignment);
  264. _alignment = alignment;
  265. }
  266. }
  267. internal static void CheckAnonymousFields(List<Field> allFields, IList<object> anonFields) {
  268. if (anonFields != null) {
  269. foreach (string s in anonFields) {
  270. bool found = false;
  271. foreach (Field f in allFields) {
  272. if (f.FieldName == s) {
  273. found = true;
  274. break;
  275. }
  276. }
  277. if (!found) {
  278. throw PythonOps.AttributeError("anonymous field {0} is not defined in this structure", s);
  279. }
  280. }
  281. }
  282. }
  283. internal static IList<object> GetAnonymousFields(PythonType type) {
  284. object anonymous;
  285. IList<object> anonFields = null;
  286. if (type.TryGetBoundAttr(type.Context.SharedContext, type, "_anonymous_", out anonymous)) {
  287. anonFields = anonymous as IList<object>;
  288. if (anonFields == null) {
  289. throw PythonOps.TypeError("_anonymous_ must be a sequence");
  290. }
  291. }
  292. return anonFields;
  293. }
  294. internal static void AddAnonymousFields(PythonType type, List<Field> allFields, INativeType cdata, Field newField) {
  295. Field[] childFields;
  296. if (cdata is StructType) {
  297. childFields = ((StructType)cdata)._fields;
  298. } else if (cdata is UnionType) {
  299. childFields = ((UnionType)cdata)._fields;
  300. } else {
  301. throw PythonOps.TypeError("anonymous field must be struct or union");
  302. }
  303. foreach (Field existingField in childFields) {
  304. Field anonField = new Field(
  305. existingField.FieldName,
  306. existingField.NativeType,
  307. checked(existingField.offset + newField.offset),
  308. allFields.Count
  309. );
  310. type.AddSlot(existingField.FieldName, anonField);
  311. allFields.Add(anonField);
  312. }
  313. }
  314. private List<Field> GetBaseSizeAlignmentAndFields(out int size, out int alignment) {
  315. size = 0;
  316. alignment = 1;
  317. List<Field> allFields = new List<Field>();
  318. INativeType lastType = null;
  319. int? totalBitCount = null;
  320. foreach (PythonType pt in BaseTypes) {
  321. StructType st = pt as StructType;
  322. if (st != null) {
  323. foreach (Field f in st._fields) {
  324. allFields.Add(f);
  325. UpdateSizeAndAlignment(f.NativeType, f.BitCount, lastType, ref size, ref alignment, ref totalBitCount);
  326. if (f.NativeType == this) {
  327. throw StructureCannotContainSelf();
  328. }
  329. lastType = f.NativeType;
  330. }
  331. }
  332. }
  333. return allFields;
  334. }
  335. private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, INativeType lastType, ref int size, ref int alignment, ref int? totalBitCount) {
  336. int prevSize = size;
  337. if (bitCount != null) {
  338. if (lastType != null && lastType.Size != cdata.Size) {
  339. totalBitCount = null;
  340. prevSize = size += lastType.Size;
  341. }
  342. size = PythonStruct.Align(size, cdata.Alignment);
  343. if (totalBitCount != null) {
  344. if ((bitCount + totalBitCount + 7) / 8 <= cdata.Size) {
  345. totalBitCount = bitCount + totalBitCount;
  346. } else {
  347. size += lastType.Size;
  348. prevSize = size;
  349. totalBitCount = bitCount;
  350. }
  351. } else {
  352. totalBitCount = bitCount;
  353. }
  354. } else {
  355. if (totalBitCount != null) {
  356. size += lastType.Size;
  357. prevSize = size;
  358. totalBitCount = null;
  359. }
  360. if (_pack != null) {
  361. alignment = _pack.Value;
  362. prevSize = size = PythonStruct.Align(size, _pack.Value);
  363. size += cdata.Size;
  364. } else {
  365. alignment = Math.Max(alignment, cdata.Alignment);
  366. prevSize = size = PythonStruct.Align(size, cdata.Alignment);
  367. size += cdata.Size;
  368. }
  369. }
  370. return prevSize;
  371. }
  372. internal void EnsureFinal() {
  373. if (_fields == null) {
  374. SetFields(PythonTuple.EMPTY);
  375. if (_fields.Length == 0) {
  376. // track that we were initialized w/o fields.
  377. _fields = _emptyFields;
  378. }
  379. }
  380. }
  381. /// <summary>
  382. /// If our size/alignment hasn't been initialized then grabs the size/alignment
  383. /// from all of our base classes. If later new _fields_ are added we'll be
  384. /// initialized and these values will be replaced.
  385. /// </summary>
  386. private void EnsureSizeAndAlignment() {
  387. Debug.Assert(_size.HasValue == _alignment.HasValue);
  388. // these are always iniitalized together
  389. if (_size == null) {
  390. lock (this) {
  391. if (_size == null) {
  392. int size, alignment;
  393. GetBaseSizeAlignmentAndFields(out size, out alignment);
  394. _size = size;
  395. _alignment = alignment;
  396. }
  397. }
  398. }
  399. }
  400. }
  401. }
  402. }
  403. #endif