PageRenderTime 65ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/DICK.B1/IronPython.Modules/cPickle.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 1919 lines | 1733 code | 115 blank | 71 comment | 157 complexity | f67e1e744406b8aea75d1cef552d26ef MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, please send an email to
  8. * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.IO;
  20. using System.Runtime.InteropServices;
  21. using System.Text;
  22. using IronPython.Runtime;
  23. using IronPython.Runtime.Exceptions;
  24. using IronPython.Runtime.Operations;
  25. using IronPython.Runtime.Types;
  26. using Microsoft.Scripting;
  27. using Microsoft.Scripting.Runtime;
  28. using Microsoft.Scripting.Utils;
  29. #if CLR2
  30. using Microsoft.Scripting.Math;
  31. #else
  32. using System.Numerics;
  33. #endif
  34. [assembly: PythonModule("cPickle", typeof(IronPython.Modules.PythonPickle))]
  35. namespace IronPython.Modules {
  36. public static class PythonPickle {
  37. public const string __doc__ = "Fast object serialization/deserialization.\n\n"
  38. + "Differences from CPython:\n"
  39. + " - does not implement the undocumented fast mode\n";
  40. [System.Runtime.CompilerServices.SpecialName]
  41. public static void PerformModuleReload(PythonContext/*!*/ context, PythonDictionary/*!*/ dict) {
  42. context.EnsureModuleException("PickleError", dict, "PickleError", "cPickle");
  43. context.EnsureModuleException("PicklingError", dict, "PicklingError", "cPickle");
  44. context.EnsureModuleException("UnpicklingError", dict, "UnpicklingError", "cPickle");
  45. context.EnsureModuleException("UnpickleableError", dict, "UnpickleableError", "cPickle");
  46. context.EnsureModuleException("BadPickleGet", dict, "BadPickleGet", "cPickle");
  47. dict["__builtins__"] = context.BuiltinModuleInstance;
  48. dict["compatible_formats"] = PythonOps.MakeList("1.0", "1.1", "1.2", "1.3", "2.0");
  49. }
  50. private static readonly PythonStruct.Struct _float64 = PythonStruct.Struct.Create(">d");
  51. private static readonly PythonStruct.Struct _uint8 = PythonStruct.Struct.Create("B");
  52. private static readonly PythonStruct.Struct _uint16 = PythonStruct.Struct.Create("<H");
  53. private static readonly PythonStruct.Struct _uint32 = PythonStruct.Struct.Create("<i");
  54. private const int highestProtocol = 2;
  55. public const string __version__ = "1.71";
  56. public const string format_version = "2.0";
  57. public static int HIGHEST_PROTOCOL {
  58. get { return highestProtocol; }
  59. }
  60. private const string Newline = "\n";
  61. #region Public module-level functions
  62. [Documentation("dump(obj, file, protocol=0) -> None\n\n"
  63. + "Pickle obj and write the result to file.\n"
  64. + "\n"
  65. + "See documentation for Pickler() for a description the file, protocol, and\n"
  66. + "(deprecated) bin parameters."
  67. )]
  68. public static void dump(CodeContext/*!*/ context, object obj, object file, [DefaultParameterValue(null)] object protocol, [DefaultParameterValue(null)] object bin) {
  69. PicklerObject pickler = new PicklerObject(context, file, protocol, bin);
  70. pickler.dump(context, obj);
  71. }
  72. [Documentation("dumps(obj, protocol=0) -> pickle string\n\n"
  73. + "Pickle obj and return the result as a string.\n"
  74. + "\n"
  75. + "See the documentation for Pickler() for a description of the protocol and\n"
  76. + "(deprecated) bin parameters."
  77. )]
  78. public static string dumps(CodeContext/*!*/ context, object obj, [DefaultParameterValue(null)] object protocol, [DefaultParameterValue(null)] object bin) {
  79. //??? possible perf enhancement: use a C# TextWriter-backed IFileOutput and
  80. // thus avoid Python call overhead. Also do similar thing for LoadFromString.
  81. object stringIO = PythonOps.Invoke(context, DynamicHelpers.GetPythonTypeFromType(typeof(PythonStringIO)), "StringIO");
  82. PicklerObject pickler = new PicklerObject(context, stringIO, protocol, bin);
  83. pickler.dump(context, obj);
  84. return Converter.ConvertToString(PythonOps.Invoke(context, stringIO, "getvalue"));
  85. }
  86. [Documentation("load(file) -> unpickled object\n\n"
  87. + "Read pickle data from the open file object and return the corresponding\n"
  88. + "unpickled object. Data after the first pickle found is ignored, but the file\n"
  89. + "cursor is not reset, so if a file objects contains multiple pickles, then\n"
  90. + "load() may be called multiple times to unpickle them.\n"
  91. + "\n"
  92. + "file: an object (such as an open file or a StringIO) with read(num_chars) and\n"
  93. + " readline() methods that return strings\n"
  94. + "\n"
  95. + "load() automatically determines if the pickle data was written in binary or\n"
  96. + "text mode."
  97. )]
  98. public static object load(CodeContext/*!*/ context, object file) {
  99. return new UnpicklerObject(context, file).load(context);
  100. }
  101. [Documentation("loads(string) -> unpickled object\n\n"
  102. + "Read a pickle object from a string, unpickle it, and return the resulting\n"
  103. + "reconstructed object. Characters in the string beyond the end of the first\n"
  104. + "pickle are ignored."
  105. )]
  106. public static object loads(CodeContext/*!*/ context, string @string) {
  107. PythonFile pf = PythonFile.Create(
  108. context,
  109. new MemoryStream(@string.MakeByteArray()),
  110. "loads",
  111. "b"
  112. );
  113. return new UnpicklerObject(context, pf).load(context);
  114. }
  115. #endregion
  116. #region File I/O wrappers
  117. /// <summary>
  118. /// Interface for "file-like objects" that implement the protocol needed by load() and friends.
  119. /// This enables the creation of thin wrappers that make fast .NET types and slow Python types look the same.
  120. /// </summary>
  121. internal interface IFileInput {
  122. string Read(CodeContext/*!*/ context, int size);
  123. string ReadLine(CodeContext/*!*/ context);
  124. }
  125. /// <summary>
  126. /// Interface for "file-like objects" that implement the protocol needed by dump() and friends.
  127. /// This enables the creation of thin wrappers that make fast .NET types and slow Python types look the same.
  128. /// </summary>
  129. internal interface IFileOutput {
  130. void Write(CodeContext/*!*/ context, string data);
  131. }
  132. private class PythonFileInput : IFileInput {
  133. private object _readMethod;
  134. private object _readLineMethod;
  135. public PythonFileInput(CodeContext/*!*/ context, object file) {
  136. if (!PythonOps.TryGetBoundAttr(context, file, "read", out _readMethod) ||
  137. !PythonOps.IsCallable(context, _readMethod) ||
  138. !PythonOps.TryGetBoundAttr(context, file, "readline", out _readLineMethod) ||
  139. !PythonOps.IsCallable(context, _readLineMethod)
  140. ) {
  141. throw PythonOps.TypeError("argument must have callable 'read' and 'readline' attributes");
  142. }
  143. }
  144. public string Read(CodeContext/*!*/ context, int size) {
  145. return Converter.ConvertToString(PythonCalls.Call(context, _readMethod, size));
  146. }
  147. public string ReadLine(CodeContext/*!*/ context) {
  148. return Converter.ConvertToString(PythonCalls.Call(context, _readLineMethod));
  149. }
  150. }
  151. private class PythonFileOutput : IFileOutput {
  152. private object _writeMethod;
  153. public PythonFileOutput(CodeContext/*!*/ context, object file) {
  154. if (!PythonOps.TryGetBoundAttr(context, file, "write", out _writeMethod) ||
  155. !PythonOps.IsCallable(context, this._writeMethod)
  156. ) {
  157. throw PythonOps.TypeError("argument must have callable 'write' attribute");
  158. }
  159. }
  160. public void Write(CodeContext/*!*/ context, string data) {
  161. PythonCalls.Call(context, _writeMethod, data);
  162. }
  163. }
  164. private class PythonReadableFileOutput : PythonFileOutput {
  165. private object _getValueMethod;
  166. public PythonReadableFileOutput(CodeContext/*!*/ context, object file)
  167. : base(context, file) {
  168. if (!PythonOps.TryGetBoundAttr(context, file, "getvalue", out _getValueMethod) ||
  169. !PythonOps.IsCallable(context, _getValueMethod)
  170. ) {
  171. throw PythonOps.TypeError("argument must have callable 'getvalue' attribute");
  172. }
  173. }
  174. public object GetValue(CodeContext/*!*/ context) {
  175. return PythonCalls.Call(context, _getValueMethod);
  176. }
  177. }
  178. #endregion
  179. #region Opcode constants
  180. internal static class Opcode {
  181. public const string Append = "a";
  182. public const string Appends = "e";
  183. public const string BinFloat = "G";
  184. public const string BinGet = "h";
  185. public const string BinInt = "J";
  186. public const string BinInt1 = "K";
  187. public const string BinInt2 = "M";
  188. public const string BinPersid = "Q";
  189. public const string BinPut = "q";
  190. public const string BinString = "T";
  191. public const string BinUnicode = "X";
  192. public const string Build = "b";
  193. public const string Dict = "d";
  194. public const string Dup = "2";
  195. public const string EmptyDict = "}";
  196. public const string EmptyList = "]";
  197. public const string EmptyTuple = ")";
  198. public const string Ext1 = "\x82";
  199. public const string Ext2 = "\x83";
  200. public const string Ext4 = "\x84";
  201. public const string Float = "F";
  202. public const string Get = "g";
  203. public const string Global = "c";
  204. public const string Inst = "i";
  205. public const string Int = "I";
  206. public const string List = "l";
  207. public const string Long = "L";
  208. public const string Long1 = "\x8a";
  209. public const string Long4 = "\x8b";
  210. public const string LongBinGet = "j";
  211. public const string LongBinPut = "r";
  212. public const string Mark = "(";
  213. public const string NewFalse = "\x89";
  214. public const string NewObj = "\x81";
  215. public const string NewTrue = "\x88";
  216. public const string NoneValue = "N";
  217. public const string Obj = "o";
  218. public const string PersId = "P";
  219. public const string Pop = "0";
  220. public const string PopMark = "1";
  221. public const string Proto = "\x80";
  222. public const string Put = "p";
  223. public const string Reduce = "R";
  224. public const string SetItem = "s";
  225. public const string SetItems = "u";
  226. public const string ShortBinstring = "U";
  227. public const string Stop = ".";
  228. public const string String = "S";
  229. public const string Tuple = "t";
  230. public const string Tuple1 = "\x85";
  231. public const string Tuple2 = "\x86";
  232. public const string Tuple3 = "\x87";
  233. public const string Unicode = "V";
  234. }
  235. #endregion
  236. #region Pickler object
  237. public static PicklerObject Pickler(CodeContext/*!*/ context, [DefaultParameterValue(null)]object file, [DefaultParameterValue(null)]object protocol, [DefaultParameterValue(null)]object bin) {
  238. return new PicklerObject(context, file, protocol, bin);
  239. }
  240. [Documentation("Pickler(file, protocol=0) -> Pickler object\n\n"
  241. + "A Pickler object serializes Python objects to a pickle bytecode stream, which\n"
  242. + "can then be converted back into equivalent objects using an Unpickler.\n"
  243. + "\n"
  244. + "file: an object (such as an open file) that has a write(string) method.\n"
  245. + "protocol: if omitted, protocol 0 is used. If HIGHEST_PROTOCOL or a negative\n"
  246. + " number, the highest available protocol is used.\n"
  247. + "bin: (deprecated; use protocol instead) for backwards compability, a 'bin'\n"
  248. + " keyword parameter is supported. When protocol is specified it is ignored.\n"
  249. + " If protocol is not specified, then protocol 0 is used if bin is false, and\n"
  250. + " protocol 1 is used if bin is true."
  251. )]
  252. [PythonType("Pickler"), PythonHidden]
  253. public class PicklerObject {
  254. private const char LowestPrintableChar = (char)32;
  255. private const char HighestPrintableChar = (char)126;
  256. // max elements that can be set/appended at a time using SETITEMS/APPENDS
  257. private delegate void PickleFunction(CodeContext/*!*/ context, object value);
  258. private readonly Dictionary<PythonType, PickleFunction> dispatchTable;
  259. private int _batchSize = 1000;
  260. private IFileOutput _file;
  261. private int _protocol;
  262. private IDictionary _memo;
  263. private object _persist_id;
  264. #region Public API
  265. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  266. public IDictionary memo {
  267. get { return _memo; }
  268. set { _memo = value; }
  269. }
  270. public int proto {
  271. get { return _protocol; }
  272. set { _protocol = value; }
  273. }
  274. public int _BATCHSIZE {
  275. get { return _batchSize; }
  276. set { _batchSize = value; }
  277. }
  278. public object persistent_id {
  279. get {
  280. return _persist_id;
  281. }
  282. set {
  283. _persist_id = value;
  284. }
  285. }
  286. public int binary {
  287. get { return _protocol == 0 ? 1 : 0; }
  288. set { _protocol = value; }
  289. }
  290. public int fast {
  291. // We don't implement fast, but we silently ignore it when it's set so that test_cpickle works.
  292. // For a description of fast, see http://mail.python.org/pipermail/python-bugs-list/2001-October/007695.html
  293. get { return 0; }
  294. set { /* ignore */ }
  295. }
  296. public PicklerObject(CodeContext/*!*/ context, object file, object protocol, object bin) {
  297. dispatchTable = new Dictionary<PythonType, PickleFunction>();
  298. dispatchTable[TypeCache.Boolean] = SaveBoolean;
  299. dispatchTable[TypeCache.Int32] = SaveInteger;
  300. dispatchTable[TypeCache.Null] = SaveNone;
  301. dispatchTable[TypeCache.Dict] = SaveDict;
  302. dispatchTable[TypeCache.BigInteger] = SaveLong;
  303. dispatchTable[TypeCache.Double] = SaveFloat;
  304. dispatchTable[TypeCache.String] = SaveUnicode;
  305. dispatchTable[TypeCache.PythonTuple] = SaveTuple;
  306. dispatchTable[TypeCache.List] = SaveList;
  307. dispatchTable[TypeCache.OldClass] = SaveGlobal;
  308. dispatchTable[TypeCache.Function] = SaveGlobal;
  309. dispatchTable[TypeCache.BuiltinFunction] = SaveGlobal;
  310. dispatchTable[TypeCache.PythonType] = SaveGlobal;
  311. dispatchTable[TypeCache.OldInstance] = SaveInstance;
  312. int intProtocol;
  313. if (file == null) {
  314. _file = new PythonReadableFileOutput(context, new PythonStringIO.StringO());
  315. } else if (Converter.TryConvertToInt32(file, out intProtocol)) {
  316. // For undocumented (yet tested in official CPython tests) list-based pickler, the
  317. // user could do something like Pickler(1), which would create a protocol-1 pickler
  318. // with an internal string output buffer (retrievable using GetValue()). For a little
  319. // more info, see
  320. // https://sourceforge.net/tracker/?func=detail&atid=105470&aid=939395&group_id=5470
  321. _file = new PythonReadableFileOutput(context, new PythonStringIO.StringO());
  322. protocol = file;
  323. } else if (file is IFileOutput) {
  324. _file = (IFileOutput)file;
  325. } else {
  326. _file = new PythonFileOutput(context, file);
  327. }
  328. this._memo = new PythonDictionary();
  329. if (protocol == null) protocol = PythonOps.IsTrue(bin) ? 1 : 0;
  330. intProtocol = PythonContext.GetContext(context).ConvertToInt32(protocol);
  331. if (intProtocol > highestProtocol) {
  332. throw PythonOps.ValueError("pickle protocol {0} asked for; the highest available protocol is {1}", intProtocol, highestProtocol);
  333. } else if (intProtocol < 0) {
  334. this._protocol = highestProtocol;
  335. } else {
  336. this._protocol = intProtocol;
  337. }
  338. }
  339. [Documentation("dump(obj) -> None\n\n"
  340. + "Pickle obj and write the result to the file object that was passed to the\n"
  341. + "constructor\n."
  342. + "\n"
  343. + "Note that you may call dump() multiple times to pickle multiple objects. To\n"
  344. + "unpickle the stream, you will need to call Unpickler's load() method a\n"
  345. + "corresponding number of times.\n"
  346. + "\n"
  347. + "The first time a particular object is encountered, it will be pickled normally.\n"
  348. + "If the object is encountered again (in the same or a later dump() call), a\n"
  349. + "reference to the previously generated value will be pickled. Unpickling will\n"
  350. + "then create multiple references to a single object."
  351. )]
  352. public void dump(CodeContext/*!*/ context, object obj) {
  353. if (_protocol >= 2) WriteProto(context);
  354. Save(context, obj);
  355. Write(context, Opcode.Stop);
  356. }
  357. [Documentation("clear_memo() -> None\n\n"
  358. + "Clear the memo, which is used internally by the pickler to keep track of which\n"
  359. + "objects have already been pickled (so that shared or recursive objects are\n"
  360. + "pickled only once)."
  361. )]
  362. public void clear_memo() {
  363. _memo.Clear();
  364. }
  365. [Documentation("getvalue() -> string\n\n"
  366. + "Return the value of the internal string. Raises PicklingError if a file object\n"
  367. + "was passed to this pickler's constructor."
  368. )]
  369. public object getvalue(CodeContext/*!*/ context) {
  370. if (_file is PythonReadableFileOutput) {
  371. return ((PythonReadableFileOutput)_file).GetValue(context);
  372. }
  373. throw PythonExceptions.CreateThrowable(PicklingError(context), "Attempt to getvalue() a non-list-based pickler");
  374. }
  375. #endregion
  376. #region Save functions
  377. private void Save(CodeContext/*!*/ context, object obj) {
  378. if (_persist_id != null) {
  379. string res = Converter.ConvertToString(PythonContext.GetContext(context).CallSplat(_persist_id, obj));
  380. if (res != null) {
  381. SavePersId(context, res);
  382. return;
  383. }
  384. }
  385. if (_memo.Contains(PythonOps.Id(obj))) {
  386. WriteGet(context, obj);
  387. } else {
  388. PickleFunction pickleFunction;
  389. PythonType objType = DynamicHelpers.GetPythonType(obj);
  390. if (!dispatchTable.TryGetValue(objType, out pickleFunction)) {
  391. if (objType.IsSubclassOf(TypeCache.PythonType)) {
  392. // treat classes with metaclasses like regular classes
  393. pickleFunction = SaveGlobal;
  394. } else {
  395. pickleFunction = SaveObject;
  396. }
  397. }
  398. pickleFunction(context, obj);
  399. }
  400. }
  401. private void SavePersId(CodeContext/*!*/ context, string res) {
  402. if (this.binary != 0) {
  403. Save(context, res);
  404. Write(context, Opcode.BinPersid);
  405. } else {
  406. Write(context, Opcode.PersId);
  407. Write(context, res);
  408. Write(context, "\n");
  409. }
  410. }
  411. private void SaveBoolean(CodeContext/*!*/ context, object obj) {
  412. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.Boolean), "arg must be bool");
  413. if (_protocol < 2) {
  414. Write(context, Opcode.Int);
  415. Write(context, String.Format("0{0}", ((bool)obj) ? 1 : 0));
  416. Write(context, Newline);
  417. } else {
  418. if ((bool)obj) {
  419. Write(context, Opcode.NewTrue);
  420. } else {
  421. Write(context, Opcode.NewFalse);
  422. }
  423. }
  424. }
  425. private void SaveDict(CodeContext/*!*/ context, object obj) {
  426. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.Dict), "arg must be dict");
  427. Debug.Assert(!_memo.Contains(PythonOps.Id(obj)));
  428. Memoize(obj);
  429. if (_protocol < 1) {
  430. Write(context, Opcode.Mark);
  431. Write(context, Opcode.Dict);
  432. } else {
  433. Write(context, Opcode.EmptyDict);
  434. }
  435. WritePut(context, obj);
  436. BatchSetItems(context, (DictionaryOps.iteritems((IDictionary<object, object>)obj)));
  437. }
  438. private void SaveFloat(CodeContext/*!*/ context, object obj) {
  439. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.Double), "arg must be float");
  440. if (_protocol < 1) {
  441. Write(context, Opcode.Float);
  442. WriteFloatAsString(context, obj);
  443. } else {
  444. Write(context, Opcode.BinFloat);
  445. WriteFloat64(context, obj);
  446. }
  447. }
  448. private void SaveGlobal(CodeContext/*!*/ context, object obj) {
  449. Debug.Assert(
  450. DynamicHelpers.GetPythonType(obj).Equals(TypeCache.OldClass) ||
  451. DynamicHelpers.GetPythonType(obj).Equals(TypeCache.Function) ||
  452. DynamicHelpers.GetPythonType(obj).Equals(TypeCache.BuiltinFunction) ||
  453. DynamicHelpers.GetPythonType(obj).Equals(TypeCache.PythonType) ||
  454. DynamicHelpers.GetPythonType(obj).IsSubclassOf(TypeCache.PythonType),
  455. "arg must be classic class, function, built-in function or method, or new-style type"
  456. );
  457. object name;
  458. if (PythonOps.TryGetBoundAttr(context, obj, "__name__", out name)) {
  459. SaveGlobalByName(context, obj, name);
  460. } else {
  461. throw CannotPickle(context, obj, "could not determine its __name__");
  462. }
  463. }
  464. private void SaveGlobalByName(CodeContext/*!*/ context, object obj, object name) {
  465. Debug.Assert(!_memo.Contains(PythonOps.Id(obj)));
  466. object moduleName = FindModuleForGlobal(context, obj, name);
  467. if (_protocol >= 2) {
  468. object code;
  469. if (((IDictionary<object, object>)PythonCopyReg.GetExtensionRegistry(context)).TryGetValue(PythonTuple.MakeTuple(moduleName, name), out code)) {
  470. if (IsUInt8(context, code)) {
  471. Write(context, Opcode.Ext1);
  472. WriteUInt8(context, code);
  473. } else if (IsUInt16(context, code)) {
  474. Write(context, Opcode.Ext2);
  475. WriteUInt16(context, code);
  476. } else if (IsInt32(context, code)) {
  477. Write(context, Opcode.Ext4);
  478. WriteInt32(context, code);
  479. } else {
  480. throw PythonOps.RuntimeError("unrecognized integer format");
  481. }
  482. return;
  483. }
  484. }
  485. Memoize(obj);
  486. Write(context, Opcode.Global);
  487. WriteStringPair(context, moduleName, name);
  488. WritePut(context, obj);
  489. }
  490. private void SaveInstance(CodeContext/*!*/ context, object obj) {
  491. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.OldInstance), "arg must be old-class instance");
  492. Debug.Assert(!_memo.Contains(PythonOps.Id(obj)));
  493. Write(context, Opcode.Mark);
  494. // Memoize() call isn't in the usual spot to allow class to be memoized before
  495. // instance (when using proto other than 0) to match CPython's bytecode output
  496. object objClass;
  497. if (!PythonOps.TryGetBoundAttr(context, obj, "__class__", out objClass)) {
  498. throw CannotPickle(context, obj, "could not determine its __class__");
  499. }
  500. if (_protocol < 1) {
  501. object className, classModuleName;
  502. if (!PythonOps.TryGetBoundAttr(context, objClass, "__name__", out className)) {
  503. throw CannotPickle(context, obj, "its __class__ has no __name__");
  504. }
  505. classModuleName = FindModuleForGlobal(context, objClass, className);
  506. Memoize(obj);
  507. WriteInitArgs(context, obj);
  508. Write(context, Opcode.Inst);
  509. WriteStringPair(context, classModuleName, className);
  510. } else {
  511. Save(context, objClass);
  512. Memoize(obj);
  513. WriteInitArgs(context, obj);
  514. Write(context, Opcode.Obj);
  515. }
  516. WritePut(context, obj);
  517. object getStateCallable;
  518. if (PythonOps.TryGetBoundAttr(context, obj, "__getstate__", out getStateCallable)) {
  519. Save(context, PythonCalls.Call(context, getStateCallable));
  520. } else {
  521. Save(context, PythonOps.GetBoundAttr(context, obj, "__dict__"));
  522. }
  523. Write(context, Opcode.Build);
  524. }
  525. private void SaveInteger(CodeContext/*!*/ context, object obj) {
  526. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.Int32), "arg must be int");
  527. if (_protocol < 1) {
  528. Write(context, Opcode.Int);
  529. WriteIntAsString(context, obj);
  530. } else {
  531. if (IsUInt8(context, obj)) {
  532. Write(context, Opcode.BinInt1);
  533. WriteUInt8(context, obj);
  534. } else if (IsUInt16(context, obj)) {
  535. Write(context, Opcode.BinInt2);
  536. WriteUInt16(context, obj);
  537. } else if (IsInt32(context, obj)) {
  538. Write(context, Opcode.BinInt);
  539. WriteInt32(context, obj);
  540. } else {
  541. throw PythonOps.RuntimeError("unrecognized integer format");
  542. }
  543. }
  544. }
  545. private void SaveList(CodeContext/*!*/ context, object obj) {
  546. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.List), "arg must be list");
  547. Debug.Assert(!_memo.Contains(PythonOps.Id(obj)));
  548. Memoize(obj);
  549. if (_protocol < 1) {
  550. Write(context, Opcode.Mark);
  551. Write(context, Opcode.List);
  552. } else {
  553. Write(context, Opcode.EmptyList);
  554. }
  555. WritePut(context, obj);
  556. BatchAppends(context, ((IEnumerable)obj).GetEnumerator());
  557. }
  558. private void SaveLong(CodeContext/*!*/ context, object obj) {
  559. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.BigInteger), "arg must be long");
  560. if (_protocol < 2) {
  561. Write(context, Opcode.Long);
  562. WriteLongAsString(context, obj);
  563. } else {
  564. if (((BigInteger)obj).IsZero()) {
  565. Write(context, Opcode.Long1);
  566. WriteUInt8(context, 0);
  567. } else {
  568. byte[] dataBytes = ((BigInteger)obj).ToByteArray();
  569. if (dataBytes.Length < 256) {
  570. Write(context, Opcode.Long1);
  571. WriteUInt8(context, dataBytes.Length);
  572. } else {
  573. Write(context, Opcode.Long4);
  574. WriteInt32(context, dataBytes.Length);
  575. }
  576. foreach (byte b in dataBytes) {
  577. WriteUInt8(context, b);
  578. }
  579. }
  580. }
  581. }
  582. private void SaveNone(CodeContext/*!*/ context, object obj) {
  583. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.Null), "arg must be None");
  584. Write(context, Opcode.NoneValue);
  585. }
  586. /// <summary>
  587. /// Call the appropriate reduce method for obj and pickle the object using
  588. /// the resulting data. Use the first available of
  589. /// copy_reg.dispatch_table[type(obj)], obj.__reduce_ex__, and obj.__reduce__.
  590. /// </summary>
  591. private void SaveObject(CodeContext/*!*/ context, object obj) {
  592. Debug.Assert(!_memo.Contains(PythonOps.Id(obj)));
  593. Memoize(obj);
  594. object reduceCallable, result;
  595. PythonType objType = DynamicHelpers.GetPythonType(obj);
  596. if (((IDictionary<object, object>)PythonCopyReg.GetDispatchTable(context)).TryGetValue(objType, out reduceCallable)) {
  597. result = PythonCalls.Call(context, reduceCallable, obj);
  598. } else if (PythonOps.TryGetBoundAttr(context, obj, "__reduce_ex__", out reduceCallable)) {
  599. if (obj is PythonType) {
  600. result = context.LanguageContext.Call(context, reduceCallable, obj, _protocol);
  601. } else {
  602. result = context.LanguageContext.Call(context, reduceCallable, _protocol);
  603. }
  604. } else if (PythonOps.TryGetBoundAttr(context, obj, "__reduce__", out reduceCallable)) {
  605. if (obj is PythonType) {
  606. result = context.LanguageContext.Call(context, reduceCallable, obj);
  607. } else {
  608. result = context.LanguageContext.Call(context, reduceCallable);
  609. }
  610. } else {
  611. throw PythonOps.AttributeError("no reduce function found for {0}", obj);
  612. }
  613. if (objType.Equals(TypeCache.String)) {
  614. if (_memo.Contains(PythonOps.Id(obj))) {
  615. WriteGet(context, obj);
  616. } else {
  617. SaveGlobalByName(context, obj, result);
  618. }
  619. } else if (result is PythonTuple) {
  620. PythonTuple rt = (PythonTuple)result;
  621. switch (rt.__len__()) {
  622. case 2:
  623. SaveReduce(context, obj, reduceCallable, rt[0], rt[1], null, null, null);
  624. break;
  625. case 3:
  626. SaveReduce(context, obj, reduceCallable, rt[0], rt[1], rt[2], null, null);
  627. break;
  628. case 4:
  629. SaveReduce(context, obj, reduceCallable, rt[0], rt[1], rt[2], rt[3], null);
  630. break;
  631. case 5:
  632. SaveReduce(context, obj, reduceCallable, rt[0], rt[1], rt[2], rt[3], rt[4]);
  633. break;
  634. default:
  635. throw CannotPickle(context, obj, "tuple returned by {0} must have to to five elements", reduceCallable);
  636. }
  637. } else {
  638. throw CannotPickle(context, obj, "{0} must return string or tuple", reduceCallable);
  639. }
  640. }
  641. /// <summary>
  642. /// Pickle the result of a reduce function.
  643. ///
  644. /// Only context, obj, func, and reduceCallable are required; all other arguments may be null.
  645. /// </summary>
  646. private void SaveReduce(CodeContext/*!*/ context, object obj, object reduceCallable, object func, object args, object state, object listItems, object dictItems) {
  647. if (!PythonOps.IsCallable(context, func)) {
  648. throw CannotPickle(context, obj, "func from reduce() should be callable");
  649. } else if (!(args is PythonTuple) && args != null) {
  650. throw CannotPickle(context, obj, "args from reduce() should be a tuple");
  651. } else if (listItems != null && !(listItems is IEnumerator)) {
  652. throw CannotPickle(context, obj, "listitems from reduce() should be a list iterator");
  653. } else if (dictItems != null && !(dictItems is IEnumerator)) {
  654. throw CannotPickle(context, obj, "dictitems from reduce() should be a dict iterator");
  655. }
  656. object funcName;
  657. string funcNameString;
  658. if (!PythonOps.TryGetBoundAttr(context, func, "__name__", out funcName)) {
  659. throw CannotPickle(context, obj, "func from reduce() ({0}) should have a __name__ attribute");
  660. } else if (!Converter.TryConvertToString(funcName, out funcNameString) || funcNameString == null) {
  661. throw CannotPickle(context, obj, "__name__ of func from reduce() must be string");
  662. }
  663. if (_protocol >= 2 && "__newobj__" == funcNameString) {
  664. if (args == null) {
  665. throw CannotPickle(context, obj, "__newobj__ arglist is None");
  666. }
  667. PythonTuple argsTuple = (PythonTuple)args;
  668. if (argsTuple.__len__() == 0) {
  669. throw CannotPickle(context, obj, "__newobj__ arglist is empty");
  670. } else if (!DynamicHelpers.GetPythonType(obj).Equals(argsTuple[0])) {
  671. throw CannotPickle(context, obj, "args[0] from __newobj__ args has the wrong class");
  672. }
  673. Save(context, argsTuple[0]);
  674. Save(context, argsTuple[new Slice(1, null)]);
  675. Write(context, Opcode.NewObj);
  676. } else {
  677. Save(context, func);
  678. Save(context, args);
  679. Write(context, Opcode.Reduce);
  680. }
  681. WritePut(context, obj);
  682. if (state != null) {
  683. Save(context, state);
  684. Write(context, Opcode.Build);
  685. }
  686. if (listItems != null) {
  687. BatchAppends(context, (IEnumerator)listItems);
  688. }
  689. if (dictItems != null) {
  690. BatchSetItems(context, (IEnumerator)dictItems);
  691. }
  692. }
  693. private void SaveTuple(CodeContext/*!*/ context, object obj) {
  694. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.PythonTuple), "arg must be tuple");
  695. Debug.Assert(!_memo.Contains(PythonOps.Id(obj)));
  696. PythonTuple t = (PythonTuple)obj;
  697. string opcode;
  698. bool needMark = false;
  699. if (_protocol > 0 && t.__len__() == 0) {
  700. opcode = Opcode.EmptyTuple;
  701. } else if (_protocol >= 2 && t.__len__() == 1) {
  702. opcode = Opcode.Tuple1;
  703. } else if (_protocol >= 2 && t.__len__() == 2) {
  704. opcode = Opcode.Tuple2;
  705. } else if (_protocol >= 2 && t.__len__() == 3) {
  706. opcode = Opcode.Tuple3;
  707. } else {
  708. opcode = Opcode.Tuple;
  709. needMark = true;
  710. }
  711. if (needMark) Write(context, Opcode.Mark);
  712. foreach (object o in t) {
  713. Save(context, o);
  714. }
  715. if (_memo.Contains(PythonOps.Id(obj))) {
  716. // recursive tuple
  717. if (_protocol == 1) {
  718. Write(context, Opcode.PopMark);
  719. } else {
  720. if (_protocol == 0) {
  721. Write(context, Opcode.Pop);
  722. }
  723. for (int i = 0; i < t.__len__(); i++) {
  724. Write(context, Opcode.Pop);
  725. }
  726. }
  727. WriteGet(context, obj);
  728. return;
  729. }
  730. Write(context, opcode);
  731. if (t.__len__() > 0) {
  732. Memoize(t);
  733. WritePut(context, t);
  734. }
  735. }
  736. private void SaveUnicode(CodeContext/*!*/ context, object obj) {
  737. Debug.Assert(DynamicHelpers.GetPythonType(obj).Equals(TypeCache.String), "arg must be unicode");
  738. Debug.Assert(!_memo.Contains(PythonOps.Id(obj)));
  739. Memoize(obj);
  740. if (_protocol < 1) {
  741. Write(context, Opcode.Unicode);
  742. WriteUnicodeStringRaw(context, obj);
  743. } else {
  744. Write(context, Opcode.BinUnicode);
  745. WriteUnicodeStringUtf8(context, obj);
  746. }
  747. WritePut(context, obj);
  748. }
  749. #endregion
  750. #region Output encoding
  751. /// <summary>
  752. /// Write value in pickle decimalnl_short format.
  753. /// </summary>
  754. private void WriteFloatAsString(CodeContext/*!*/ context, object value) {
  755. Debug.Assert(DynamicHelpers.GetPythonType(value).Equals(TypeCache.Double));
  756. Write(context, DoubleOps.__repr__(context, (double)value));
  757. Write(context, Newline);
  758. }
  759. /// <summary>
  760. /// Write value in pickle float8 format.
  761. /// </summary>
  762. private void WriteFloat64(CodeContext/*!*/ context, object value) {
  763. Debug.Assert(DynamicHelpers.GetPythonType(value).Equals(TypeCache.Double));
  764. Write(context, _float64.pack(context, value));
  765. }
  766. /// <summary>
  767. /// Write value in pickle uint1 format.
  768. /// </summary>
  769. private void WriteUInt8(CodeContext/*!*/ context, object value) {
  770. Debug.Assert(IsUInt8(context, value));
  771. Write(context, _uint8.pack(context, value));
  772. }
  773. /// <summary>
  774. /// Write value in pickle uint2 format.
  775. /// </summary>
  776. private void WriteUInt16(CodeContext/*!*/ context, object value) {
  777. Debug.Assert(IsUInt16(context, value));
  778. Write(context, _uint16.pack(context, value));
  779. }
  780. /// <summary>
  781. /// Write value in pickle int4 format.
  782. /// </summary>
  783. private void WriteInt32(CodeContext/*!*/ context, object value) {
  784. Debug.Assert(IsInt32(context, value));
  785. Write(context, _uint32.pack(context, value));
  786. }
  787. /// <summary>
  788. /// Write value in pickle decimalnl_short format.
  789. /// </summary>
  790. private void WriteIntAsString(CodeContext/*!*/ context, object value) {
  791. Debug.Assert(IsInt32(context, value));
  792. Write(context, PythonOps.Repr(context, value));
  793. Write(context, Newline);
  794. }
  795. /// <summary>
  796. /// Write value in pickle decimalnl_long format.
  797. /// </summary>
  798. private void WriteLongAsString(CodeContext/*!*/ context, object value) {
  799. Debug.Assert(DynamicHelpers.GetPythonType(value).Equals(TypeCache.BigInteger));
  800. Write(context, PythonOps.Repr(context, value));
  801. Write(context, Newline);
  802. }
  803. /// <summary>
  804. /// Write value in pickle unicodestringnl format.
  805. /// </summary>
  806. private void WriteUnicodeStringRaw(CodeContext/*!*/ context, object value) {
  807. Debug.Assert(DynamicHelpers.GetPythonType(value).Equals(TypeCache.String));
  808. // manually escape backslash and newline
  809. Write(context, StringOps.RawUnicodeEscapeEncode(((string)value).Replace("\\", "\\u005c").Replace("\n", "\\u000a")));
  810. Write(context, Newline);
  811. }
  812. /// <summary>
  813. /// Write value in pickle unicodestring4 format.
  814. /// </summary>
  815. private void WriteUnicodeStringUtf8(CodeContext/*!*/ context, object value) {
  816. Debug.Assert(DynamicHelpers.GetPythonType(value).Equals(TypeCache.String));
  817. string encodedString = System.Text.Encoding.UTF8.GetBytes((string)value).MakeString();
  818. WriteInt32(context, encodedString.Length);
  819. Write(context, encodedString);
  820. }
  821. /// <summary>
  822. /// Write value in pickle stringnl_noescape_pair format.
  823. /// </summary>
  824. private void WriteStringPair(CodeContext/*!*/ context, object value1, object value2) {
  825. Debug.Assert(DynamicHelpers.GetPythonType(value1).Equals(TypeCache.String));
  826. Debug.Assert(DynamicHelpers.GetPythonType(value2).Equals(TypeCache.String));
  827. #if DEBUG
  828. Debug.Assert(IsPrintableAscii(value1));
  829. Debug.Assert(IsPrintableAscii(value2));
  830. #endif
  831. Write(context, (string)value1);
  832. Write(context, Newline);
  833. Write(context, (string)value2);
  834. Write(context, Newline);
  835. }
  836. #endregion
  837. #region Type checking
  838. /// <summary>
  839. /// Return true if value is appropriate for formatting in pickle uint1 format.
  840. /// </summary>
  841. private bool IsUInt8(CodeContext/*!*/ context, object value) {
  842. PythonContext pc = PythonContext.GetContext(context);
  843. return pc.LessThanOrEqual(0, value) && pc.LessThan(value, 1 << 8);
  844. }
  845. /// <summary>
  846. /// Return true if value is appropriate for formatting in pickle uint2 format.
  847. /// </summary>
  848. private bool IsUInt16(CodeContext/*!*/ context, object value) {
  849. PythonContext pc = PythonContext.GetContext(context);
  850. return pc.LessThanOrEqual(1 << 8, value) && pc.LessThan(value, 1 << 16);
  851. }
  852. /// <summary>
  853. /// Return true if value is appropriate for formatting in pickle int4 format.
  854. /// </summary>
  855. private bool IsInt32(CodeContext/*!*/ context, object value) {
  856. PythonContext pc = PythonContext.GetContext(context);
  857. return pc.LessThanOrEqual(Int32.MinValue, value) && pc.LessThanOrEqual(value, Int32.MaxValue);
  858. }
  859. #if DEBUG
  860. /// <summary>
  861. /// Return true if value is a string where each value is in the range of printable ASCII characters.
  862. /// </summary>
  863. private bool IsPrintableAscii(object value) {
  864. Debug.Assert(DynamicHelpers.GetPythonType(value).Equals(TypeCache.String));
  865. string strValue = (string)value;
  866. foreach (char c in strValue) {
  867. if (!(LowestPrintableChar <= c && c <= HighestPrintableChar)) return false;
  868. }
  869. return true;
  870. }
  871. #endif
  872. #endregion
  873. #region Output generation helpers
  874. private void Write(CodeContext/*!*/ context, string data) {
  875. _file.Write(context, data);
  876. }
  877. private void WriteGet(CodeContext/*!*/ context, object obj) {
  878. Debug.Assert(_memo.Contains(PythonOps.Id(obj)));
  879. // Memo entries are tuples, and the first element is the memo index
  880. IList<object> memoEntry = (IList<object>)_memo[PythonOps.Id(obj)];
  881. object index = memoEntry[0];
  882. Debug.Assert(PythonContext.GetContext(context).GreaterThanOrEqual(index, 0));
  883. if (_protocol < 1) {
  884. Write(context, Opcode.Get);
  885. WriteIntAsString(context, index);
  886. } else {
  887. if (IsUInt8(context, index)) {
  888. Write(context, Opcode.BinGet);
  889. WriteUInt8(context, index);
  890. } else {
  891. Write(context, Opcode.LongBinGet);
  892. WriteInt32(context, index);
  893. }
  894. }
  895. }
  896. private void WriteInitArgs(CodeContext/*!*/ context, object obj) {
  897. object getInitArgsCallable;
  898. if (PythonOps.TryGetBoundAttr(context, obj, "__getinitargs__", out getInitArgsCallable)) {
  899. object initArgs = PythonCalls.Call(context, getInitArgsCallable);
  900. if (!(initArgs is PythonTuple)) {
  901. throw CannotPickle(context, obj, "__getinitargs__() must return tuple");
  902. }
  903. foreach (object arg in (PythonTuple)initArgs) {
  904. Save(context, arg);
  905. }
  906. }
  907. }
  908. private void WritePut(CodeContext/*!*/ context, object obj) {
  909. Debug.Assert(_memo.Contains(PythonOps.Id(obj)));
  910. // Memo entries are tuples, and the first element is the memo index
  911. IList<object> memoEntry = (IList<object>)_memo[PythonOps.Id(obj)];
  912. object index = memoEntry[0];
  913. Debug.Assert(PythonContext.GetContext(context).GreaterThanOrEqual(index, 0));
  914. if (_protocol < 1) {
  915. Write(context, Opcode.Put);
  916. WriteIntAsString(context, index);
  917. } else {
  918. if (IsUInt8(context, index)) {
  919. Write(context, Opcode.BinPut);
  920. WriteUInt8(context, index);
  921. } else {
  922. Write(context, Opcode.LongBinPut);
  923. WriteInt32(context, index);
  924. }
  925. }
  926. }
  927. private void WriteProto(CodeContext/*!*/ context) {
  928. Write(context, Opcode.Proto);
  929. WriteUInt8(context, _protocol);
  930. }
  931. /// <summary>
  932. /// Emit a series of opcodes that will set append all items indexed by iter
  933. /// to the object at the top of the stack. Use APPENDS if possible, but
  934. /// append no more than BatchSize items at a time.
  935. /// </summary>
  936. private void BatchAppends(CodeContext/*!*/ context, IEnumerator enumerator) {
  937. if (_protocol < 1) {
  938. while (enumerator.MoveNext()) {
  939. Save(context, enumerator.Current);
  940. Write(context, Opcode.Append);
  941. }
  942. } else {
  943. object next;
  944. if (enumerator.MoveNext()) {
  945. next = enumerator.Current;
  946. } else {
  947. return;
  948. }
  949. int batchCompleted = 0;
  950. object current;
  951. // We do a one-item lookahead to avoid emitting an APPENDS for a
  952. // single remaining item.
  953. while (enumerator.MoveNext()) {
  954. current = next;
  955. next = enumerator.Current;
  956. if (batchCompleted == _BATCHSIZE) {
  957. Write(context, Opcode.Appends);
  958. batchCompleted = 0;
  959. }
  960. if (batchCompleted == 0) {
  961. Write(context, Opcode.Mark);
  962. }
  963. Save(context, current);
  964. batchCompleted++;
  965. }
  966. if (batchCompleted == _BATCHSIZE) {
  967. Write(context, Opcode.Appends);
  968. batchCompleted = 0;
  969. }
  970. Save(context, next);
  971. batchCompleted++;
  972. if (batchCompleted > 1) {
  973. Write(context, Opcode.Appends);
  974. } else {
  975. Write(context, Opcode.Append);
  976. }
  977. }
  978. }
  979. /// <summary>
  980. /// Emit a series of opcodes that will set all (key, value) pairs indexed by
  981. /// iter in the object at the top of the stack. Use SETITEMS if possible,
  982. /// but append no more than BatchSize items at a time.
  983. /// </summary>
  984. private void BatchSetItems(CodeContext/*!*/ context, IEnumerator enumerator) {
  985. PythonTuple kvTuple;
  986. if (_protocol < 1) {
  987. while (enumerator.MoveNext()) {
  988. kvTuple = (PythonTuple)enumerator.Current;
  989. Save(context, kvTuple[0]);
  990. Save(context, kvTuple[1]);
  991. Write(context, Opcode.SetItem);
  992. }
  993. } else {
  994. object nextKey, nextValue;
  995. if (enumerator.MoveNext()) {
  996. kvTuple = (PythonTuple)enumerator.Current;
  997. nextKey = kvTuple[0];
  998. nextValue = kvTuple[1];
  999. } else {
  1000. return;
  1001. }
  1002. int batchCompleted = 0;
  1003. object curKey, curValue;
  1004. // We do a one-item lookahead to avoid emitting a SETITEMS for a
  1005. // single remaining item.
  1006. while (enumerator.MoveNext()) {
  1007. curKey = nextKey;
  1008. curValue = nextValue;
  1009. kvTuple = (PythonTuple)enumerator.Current;
  1010. nextKey = kvTuple[0];
  1011. nextValue = kvTuple[1];
  1012. if (batchCompleted == _BATCHSIZE) {
  1013. Write(context, Opcode.SetItems);
  1014. batchCompleted = 0;
  1015. }
  1016. if (batchCompleted == 0) {
  1017. Write(context, Opcode.Mark);
  1018. }
  1019. Save(context, curKey);
  1020. Save(context, curValue);
  1021. batchCompleted++;
  1022. }
  1023. if (batchCompleted == _BATCHSIZE) {
  1024. Write(context, Opcode.SetItems);
  1025. batchCompleted = 0;
  1026. }
  1027. Save(context, nextKey);
  1028. Save(context, nextValue);
  1029. batchCompleted++;
  1030. if (batchCompleted > 1) {
  1031. Write(context, Opcode.SetItems);
  1032. } else {
  1033. Write(context, Opcode.SetItem);
  1034. }
  1035. }
  1036. }
  1037. #endregion
  1038. #region Other private helper methods
  1039. private Exception CannotPickle(CodeContext/*!*/ context, object obj, string format, params object[] args) {
  1040. StringBuilder msgBuilder = new StringBuilder();
  1041. msgBuilder.Append("Can't pickle ");
  1042. msgBuilder.Append(PythonOps.ToString(context, obj));
  1043. if (format != null) {
  1044. msgBuilder.Append(": ");
  1045. msgBuilder.Append(String.Format(format, args));
  1046. }
  1047. return PythonExceptions.CreateThrowable(PickleError(context), msgBuilder.ToString());
  1048. }
  1049. private void Memoize(object obj) {
  1050. if (!_memo.Contains(PythonOps.Id(obj))) {
  1051. _memo[PythonOps.Id(obj)] = PythonTuple.MakeTuple(_memo.Count, obj);
  1052. }
  1053. }
  1054. /// <summary>
  1055. /// Find the module for obj and ensure that obj is reachable in that module by the given name.
  1056. ///
  1057. /// Throw PicklingError if any of the following are true:
  1058. /// - The module couldn't be determined.
  1059. /// - The module couldn't be loaded.
  1060. /// - The given name doesn't exist in the module.
  1061. /// - The given name is a different object than obj.
  1062. ///
  1063. /// Otherwise, return the name of the module.
  1064. ///
  1065. /// To determine which module obj lives in, obj.__module__ is used if available. The
  1066. /// module named by obj.__module__ is loaded if needed. If obj has no __module__
  1067. /// attribute, then each loaded module is searched. If a loaded module has an
  1068. /// attribute with the given name, and that attribute is the same object as obj,
  1069. /// then that module is used.
  1070. /// </summary>
  1071. private object FindModuleForGlobal(CodeContext/*!*/ context, object obj, object name) {
  1072. object module;
  1073. object moduleName;
  1074. if (PythonOps.TryGetBoundAttr(context, obj, "__module__", out moduleName)) {
  1075. // TODO: Global SystemState
  1076. Builtin.__import__(context, Converter.ConvertToString(moduleName));
  1077. object foundObj;
  1078. if (Importer.TryGetExistingModule(context, Converter.ConvertToString(moduleName), out module) &&
  1079. PythonOps.TryGetBoundAttr(context, module, Converter.ConvertToString(name), out foundObj)) {
  1080. if (PythonOps.IsRetBool(foundObj, obj)) {
  1081. return moduleName;
  1082. } else {
  1083. throw CannotPickle(context, obj, "it's not the same object as {0}.{1}", moduleName, name);
  1084. }
  1085. } else {
  1086. throw CannotPickle(context, obj, "it's not found as {0}.{1}", moduleName, name);
  1087. }
  1088. } else {
  1089. // No obj.__module__, so crawl through all loaded modules looking for obj
  1090. foreach (KeyValuePair<object, object> modulePair in PythonContext.GetContext(context).SystemStateModules) {
  1091. moduleName = modulePair.Key;
  1092. module = modulePair.Value;
  1093. object foundObj;
  1094. if (PythonOps.TryGetBoundAttr(context, module, Converter.ConvertToString(name), out foundObj) &&
  1095. PythonOps.IsRetBool(foundObj, obj)
  1096. ) {
  1097. return moduleName;
  1098. }
  1099. }
  1100. throw CannotPickle(context, obj, "could not determine its module");
  1101. }
  1102. }
  1103. #endregion
  1104. }
  1105. #endregion
  1106. #region Unpickler object
  1107. public static UnpicklerObject Unpickler(CodeContext/*!*/ context, object file) {
  1108. return new UnpicklerObject(context, file);
  1109. }
  1110. [Documentation("Unpickler(file) -> Unpickler object\n\n"
  1111. + "An Unpickler object reads a pickle bytecode stream and creates corresponding\n"
  1112. + "objects."
  1113. + "\n"
  1114. + "file: an object (such as an open file or a StringIO) with read(num_chars) and\n"
  1115. + " readline() methods that return strings"
  1116. )]
  1117. [PythonType("Unpickler"), PythonHidden]
  1118. public class UnpicklerObject {
  1119. private readonly object _mark = new object();
  1120. private delegate void LoadFunction(CodeContext/*!*/ context);
  1121. private readonly Dictionary<string, LoadFunction> _dispatch;
  1122. private IFileInput _file;
  1123. private List _stack;
  1124. private IDictionary<object, object> _memo;
  1125. private object _pers_loader;
  1126. public UnpicklerObject(CodeContext context, object file) {
  1127. this._file = file as IFileInput ?? new PythonFileInput(context, file);
  1128. _memo = new PythonDictionary();
  1129. _dispatch = new Dictionary<string, LoadFunction>();
  1130. _dispatch[""] = LoadEof;
  1131. _dispatch[Opcode.Append] = LoadAppend;
  1132. _dispatch[Opcode.Appends] = LoadAppends;
  1133. _dispatch[Opcode.BinFloat] = LoadBinFloat;
  1134. _dispatch[Opcode.BinGet] = LoadBinGet;
  1135. _dispatch[Opcode.BinInt] = LoadBinInt;
  1136. _dispatch[Opcode.BinInt1] = LoadBinInt1;
  1137. _dispatch[Opcode.BinInt2] = LoadBinInt2;
  1138. _dispatch[Opcode.BinPersid] = LoadBinPersid;
  1139. _dispatch[Opcode.BinPut] = LoadBinPut;
  1140. _dispatch[Opcode.BinString] = LoadBinString;
  1141. _dispatch[Opcode.BinUnicode] = LoadBinUnicode;
  1142. _dispatch[Opcode.Build] = LoadBuild;
  1143. _dispatch[Opcode.Dict] = LoadDict;
  1144. _dispatch[Opcode.Dup] = LoadDup;
  1145. _dispatch[Opcode.EmptyDict] = LoadEmptyDict;
  1146. _dispatch[Opcode.EmptyList] = LoadEmptyList;
  1147. _dispatch[Opcode.EmptyTuple] = LoadEmptyTuple;
  1148. _dispatch[Opcode.Ext1] = LoadExt1;
  1149. _dispatch[Opcode.Ext2] = LoadExt2;
  1150. _dispatch[Opcode.Ext4] = LoadExt4;
  1151. _dispatch[Opcode.Float] = LoadFloat;
  1152. _dispatch[Opcode.Get] = LoadGet;
  1153. _dispatch[Opcode.Global] = LoadGlobal;
  1154. _dispatch[Opcode.Inst] = LoadInst;
  1155. _dispatch[Opcode.Int] = LoadInt;
  1156. _dispatch[Opcode.List] = LoadList;
  1157. _dispatch[Opcode.Long] = LoadLong;
  1158. _dispatch[Opcode.Long1] = LoadLong1;
  1159. _dispatch[Opcode.Long4] = LoadLong4;
  1160. _dispatch[Opcode.LongBinGet] = LoadLongBinGet;
  1161. _dispatch[Opcode.LongBinPut] = LoadLongBinPut;
  1162. _dispatch[Opcode.Mark] = LoadMark;
  1163. _dispatch[Opcode.NewFalse] = LoadNewFalse;
  1164. _dispatch[Opcode.NewObj] = LoadNewObj;
  1165. _dispatch[Opcode.NewTrue] = LoadNewTrue;
  1166. _dispatch[Opcode.NoneValue] = LoadNoneValue;
  1167. _dispatch[Opcode.Obj] = LoadObj;
  1168. _dispatch[Opcode.PersId] = LoadPersId;
  1169. _dispatch[Opcode.Pop] = LoadPop;
  1170. _dispatch[Opcode.PopMark] = LoadPopMark;
  1171. _dispatch[Opcode.Proto] = LoadProto;
  1172. _dispatch[Opcode.Put] = LoadPut;
  1173. _dispatch[Opcode.Reduce] = LoadReduce;
  1174. _dispatch[Opcode.SetItem] = LoadSetItem;
  1175. _dispatch[Opcode.SetItems] = LoadSetItems;
  1176. _dispatch[Opcode.ShortBinstring] = LoadShortBinstring;
  1177. _dispatch[Opcode.String] = LoadString;
  1178. _dispatch[Opcode.Tuple] = LoadTuple;
  1179. _dispatch[Opcode.Tuple1] = LoadTuple1;
  1180. _dispatch[Opcode.Tuple2] = LoadTuple2;
  1181. _dispatch[Opcode.Tuple3] = LoadTuple3;
  1182. _dispatch[Opcode.Unicode] = LoadUnicode;
  1183. }
  1184. [Documentation("load() -> unpickled object\n\n"
  1185. + "Read pickle data from the file object that was passed to the constructor and\n"
  1186. + "return the corresponding unpickled objects."
  1187. )]
  1188. public object load(CodeContext/*!*/ context) {
  1189. _stack = new List();
  1190. string opcode = Read(context, 1);
  1191. while (opcode != Opcode.Stop) {
  1192. if (!_dispatch.ContainsKey(opcode)) {
  1193. throw CannotUnpickle(context, "invalid opcode: {0}", PythonOps.Repr(context, opcode));
  1194. }
  1195. _dispatch[opcode](context);
  1196. opcode = Read(context, 1);
  1197. }
  1198. return _stack.pop();
  1199. }
  1200. [Documentation("noload() -> unpickled object\n\n"
  1201. // 1234567890123456789012345678901234567890123456789012345678901234567890123456789
  1202. + "Like load(), but don't import any modules or create create any instances of\n"
  1203. + "user-defined types. (Builtin objects such as ints, tuples, etc. are created as\n"
  1204. + "with load().)\n"
  1205. + "\n"
  1206. + "This is primarily useful for scanning a pickle for persistent ids without\n"
  1207. + "incurring the overhead of completely unpickling an object. See the pickle\n"
  1208. + "module documentation for more information about persistent ids."
  1209. )]
  1210. public void noload(CodeContext/*!*/ context) {
  1211. throw PythonOps.NotImplementedError("noload() is not implemented");
  1212. }
  1213. private Exception CannotUnpickle(CodeContext/*!*/ context, string format, params object[] args) {
  1214. return PythonExceptions.CreateThrowable(UnpicklingError(context), String.Format(format, args));
  1215. }
  1216. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  1217. public IDictionary<object, object> memo {
  1218. get { return _memo; }
  1219. set { _memo = value; }
  1220. }
  1221. public object persistent_load {
  1222. get {
  1223. return _pers_loader;
  1224. }
  1225. set {
  1226. _pers_loader = value;
  1227. }
  1228. }
  1229. private object MemoGet(CodeContext/*!*/ context, long key) {
  1230. object value;
  1231. if (_memo.TryGetValue(key, out value)) return value;
  1232. throw PythonExceptions.CreateThrowable(BadPickleGet(context), String.Format("memo key {0} not found", key));
  1233. }
  1234. private void MemoPut(long key, object value) {
  1235. _memo[key] = value;
  1236. }
  1237. [PropertyMethod, System.Runtime.CompilerServices.SpecialName]
  1238. public int GetMarkIndex(CodeContext/*!*/ context) {
  1239. int i = _stack.__len__() - 1;
  1240. while (i > 0 && _stack[i] != _mark) i -= 1;
  1241. if (i == -1) throw CannotUnpickle(context, "mark not found");
  1242. return i;
  1243. }
  1244. private string Read(CodeContext/*!*/ context, int size) {
  1245. string res = _file.Read(context, size);
  1246. if (res.Length < size) {
  1247. throw PythonOps.EofError("unexpected EOF while unpickling");
  1248. }
  1249. return res;
  1250. }
  1251. private string ReadLineNoNewline(CodeContext/*!*/ context) {
  1252. string raw = _file.ReadLine(context);
  1253. return raw.Substring(0, raw.Length - 1);
  1254. }
  1255. private object ReadFloatString(CodeContext/*!*/ context) {
  1256. return DoubleOps.__new__(context, TypeCache.Double, ReadLineNoNewline(context));
  1257. }
  1258. private double ReadFloat64(CodeContext/*!*/ context) {
  1259. int index = 0;
  1260. return PythonStruct.CreateDoubleValue(context, ref index, false, Read(context, 8));
  1261. }
  1262. private object ReadIntFromString(CodeContext/*!*/ context) {
  1263. string raw = ReadLineNoNewline(context);
  1264. if ("00" == raw) return ScriptingRuntimeHelpers.False;
  1265. else if ("01" == raw) return ScriptingRuntimeHelpers.True;
  1266. return Int32Ops.__new__(context, TypeCache.Int32, raw);
  1267. }
  1268. private int ReadInt32(CodeContext/*!*/ context) {
  1269. int index = 0;
  1270. return PythonStruct.CreateIntValue(context, ref index, true, Read(context, 4));
  1271. }
  1272. private object ReadLongFromString(CodeContext/*!*/ context) {
  1273. return BigIntegerOps.__new__(context, TypeCache.BigInteger, ReadLineNoNewline(context));
  1274. }
  1275. private object ReadLong(CodeContext/*!*/ context, int size) {
  1276. return new BigInteger(Read(context, size).MakeByteArray());
  1277. }
  1278. private char ReadUInt8(CodeContext/*!*/ context) {
  1279. int index = 0;
  1280. return PythonStruct.CreateCharValue(context, ref index, Read(context, 1));
  1281. }
  1282. private ushort ReadUInt16(CodeContext/*!*/ context) {
  1283. int index = 0;
  1284. return PythonStruct.CreateUShortValue(context, ref index, true, Read(context, 2));
  1285. }
  1286. public object find_global(CodeContext/*!*/ context, object module, object attr) {
  1287. object moduleObject;
  1288. if (!Importer.TryGetExistingModule(context, Converter.ConvertToString(module), out moduleObject)) {
  1289. Builtin.__import__(context, Converter.ConvertToString(module));
  1290. moduleObject = PythonContext.GetContext(context).SystemStateModules[module];
  1291. }
  1292. return PythonOps.GetBoundAttr(context, moduleObject, Converter.ConvertToString(attr));
  1293. }
  1294. private object MakeInstance(CodeContext/*!*/ context, object cls, object[] args) {
  1295. OldClass oc = cls as OldClass;
  1296. if (oc != null) {
  1297. OldInstance inst = new OldInstance(context, oc);
  1298. if (args.Length != 0 || PythonOps.HasAttr(context, cls, "__getinitargs__")) {
  1299. PythonOps.CallWithContext(context, PythonOps.GetBoundAttr(context, inst, "__init__"), args);
  1300. }
  1301. return inst;
  1302. }
  1303. return PythonOps.CallWithContext(context, cls, args);
  1304. }
  1305. private void PopMark(int markIndex) {
  1306. _stack.__delslice__(markIndex, _stack.__len__());
  1307. }
  1308. /// <summary>
  1309. /// Interpret everything from markIndex to the top of the stack as a sequence
  1310. /// of key, value, key, value, etc. Set dict[key] = value for each. Pop
  1311. /// everything from markIndex up when done.
  1312. /// </summary>
  1313. private void SetItems(PythonDictionary dict, int markIndex) {
  1314. for (int i = markIndex + 1; i < _stack.__len__(); i += 2) {
  1315. dict[_stack[i]] = _stack[i+1];
  1316. }
  1317. PopMark(markIndex);
  1318. }
  1319. private void LoadEof(CodeContext/*!*/ context) {
  1320. throw PythonOps.EofError("unexpected end of opcode stream");
  1321. }
  1322. private void LoadAppend(CodeContext/*!*/ context) {
  1323. object item = _stack.pop();
  1324. object seq = _stack[-1];
  1325. if (seq is List) {
  1326. ((List)seq).append(item);
  1327. } else {
  1328. PythonCalls.Call(context, PythonOps.GetBoundAttr(context, seq, "append"), item);
  1329. }
  1330. }
  1331. private void LoadAppends(CodeContext/*!*/ context) {
  1332. int markIndex = GetMarkIndex(context);
  1333. object seq = _stack[markIndex - 1];
  1334. object stackSlice = _stack.__getslice__(markIndex + 1, _stack.__len__());
  1335. if (seq is List) {
  1336. ((List)seq).extend(stackSlice);
  1337. } else {
  1338. PythonOps.CallWithContext(context, PythonOps.GetBoundAttr(context, seq, "extend"), stackSlice);
  1339. }
  1340. PopMark(markIndex);
  1341. }
  1342. private void LoadBinFloat(CodeContext/*!*/ context) {
  1343. _stack.append(ReadFloat64(context));
  1344. }
  1345. private void LoadBinGet(CodeContext/*!*/ context) {
  1346. _stack.append(MemoGet(context, (long)ReadUInt8(context)));
  1347. }
  1348. private void LoadBinInt(CodeContext/*!*/ context) {
  1349. _stack.append(ReadInt32(context));
  1350. }
  1351. private void LoadBinInt1(CodeContext/*!*/ context) {
  1352. _stack.append((int)ReadUInt8(context));
  1353. }
  1354. private void LoadBinInt2(CodeContext/*!*/ context) {
  1355. _stack.append((int)ReadUInt16(context));
  1356. }
  1357. private void LoadBinPersid(CodeContext/*!*/ context) {
  1358. if (_pers_loader == null) throw CannotUnpickle(context, "cannot unpickle binary persistent ID w/o persistent_load");
  1359. _stack.append(PythonContext.GetContext(context).CallSplat(_pers_loader, _stack.pop()));
  1360. }
  1361. private void LoadBinPut(CodeContext/*!*/ context) {
  1362. MemoPut((long)ReadUInt8(context), _stack[-1]);
  1363. }
  1364. private void LoadBinString(CodeContext/*!*/ context) {
  1365. _stack.append(Read(context, ReadInt32(context)));
  1366. }
  1367. private void LoadBinUnicode(CodeContext/*!*/ context) {
  1368. _stack.append(StringOps.decode(context, Read(context, ReadInt32(context)), "utf-8", "strict"));
  1369. }
  1370. private void LoadBuild(CodeContext/*!*/ context) {
  1371. object arg = _stack.pop();
  1372. object inst = _stack[-1];
  1373. object setStateCallable;
  1374. if (PythonOps.TryGetBoundAttr(context, inst, "__setstate__", out setStateCallable)) {
  1375. PythonOps.CallWithContext(context, setStateCallable, arg);
  1376. return;
  1377. }
  1378. PythonDictionary dict;
  1379. PythonDictionary slots;
  1380. if (arg == null) {
  1381. dict = null;
  1382. slots = null;
  1383. } else if (arg is PythonDictionary) {
  1384. dict = (PythonDictionary)arg;
  1385. slots = null;
  1386. } else if (arg is PythonTuple) {
  1387. PythonTuple argsTuple = (PythonTuple)arg;
  1388. if (argsTuple.__len__() != 2) {
  1389. throw PythonOps.ValueError("state for object without __setstate__ must be None, dict, or 2-tuple");
  1390. }
  1391. dict = (PythonDictionary)argsTuple[0];
  1392. slots = (PythonDictionary)argsTuple[1];
  1393. } else {
  1394. throw PythonOps.ValueError("state for object without __setstate__ must be None, dict, or 2-tuple");
  1395. }
  1396. if (dict != null) {
  1397. object instDict;
  1398. if (PythonOps.TryGetBoundAttr(context, inst, "__dict__", out instDict)) {
  1399. PythonDictionary realDict = instDict as PythonDictionary;
  1400. if (realDict != null) {
  1401. realDict.update(context, dict);
  1402. } else {
  1403. object updateCallable;
  1404. if (PythonOps.TryGetBoundAttr(context, instDict, "update", out updateCallable)) {
  1405. PythonOps.CallWithContext(context, updateCallable, dict);
  1406. } else {
  1407. throw CannotUnpickle(context, "could not update __dict__ {0} when building {1}", dict, inst);
  1408. }
  1409. }
  1410. }
  1411. }
  1412. if (slots != null) {
  1413. foreach(object key in (IEnumerable)slots) {
  1414. PythonOps.SetAttr(context, inst, (string)key, slots[key]);
  1415. }
  1416. }
  1417. }
  1418. private void LoadDict(CodeContext/*!*/ context) {
  1419. int markIndex = GetMarkIndex(context);
  1420. PythonDictionary dict = new PythonDictionary((_stack.__len__() - 1 - markIndex) / 2);
  1421. SetItems(dict, markIndex);
  1422. _stack.append(dict);
  1423. }
  1424. private void LoadDup(CodeContext/*!*/ context) {
  1425. _stack.append(_stack[-1]);
  1426. }
  1427. private void LoadEmptyDict(CodeContext/*!*/ context) {
  1428. _stack.append(new PythonDictionary());
  1429. }
  1430. private void LoadEmptyList(CodeContext/*!*/ context) {
  1431. _stack.append(PythonOps.MakeList());
  1432. }
  1433. private void LoadEmptyTuple(CodeContext/*!*/ context) {
  1434. _stack.append(PythonTuple.MakeTuple());
  1435. }
  1436. private void LoadExt1(CodeContext/*!*/ context) {
  1437. PythonTuple global = (PythonTuple)PythonCopyReg.GetInvertedRegistry(context)[(int)ReadUInt8(context)];
  1438. _stack.append(find_global(context, global[0], global[1]));
  1439. }
  1440. private void LoadExt2(CodeContext/*!*/ context) {
  1441. PythonTuple global = (PythonTuple)PythonCopyReg.GetInvertedRegistry(context)[(int)ReadUInt16(context)];
  1442. _stack.append(find_global(context, global[0], global[1]));
  1443. }
  1444. private void LoadExt4(CodeContext/*!*/ context) {
  1445. PythonTuple global = (PythonTuple)PythonCopyReg.GetInvertedRegistry(context)[ReadInt32(context)];
  1446. _stack.append(find_global(context, global[0], global[1]));
  1447. }
  1448. private void LoadFloat(CodeContext/*!*/ context) {
  1449. _stack.append(ReadFloatString(context));
  1450. }
  1451. private void LoadGet(CodeContext/*!*/ context) {
  1452. try {
  1453. _stack.append(MemoGet(context, (long)(int)ReadIntFromString(context)));
  1454. } catch (ArgumentException) {
  1455. throw PythonExceptions.CreateThrowable(BadPickleGet(context), "while executing GET: invalid integer value");
  1456. }
  1457. }
  1458. private void LoadGlobal(CodeContext/*!*/ context) {
  1459. string module = ReadLineNoNewline(context);
  1460. string attr = ReadLineNoNewline(context);
  1461. _stack.append(find_global(context, module, attr));
  1462. }
  1463. private void LoadInst(CodeContext/*!*/ context) {
  1464. LoadGlobal(context);
  1465. object cls = _stack.pop();
  1466. if (cls is OldClass || cls is PythonType) {
  1467. int markIndex = GetMarkIndex(context);
  1468. object[] args = _stack.GetSliceAsArray(markIndex + 1, _stack.__len__());
  1469. PopMark(markIndex);
  1470. _stack.append(MakeInstance(context, cls, args));
  1471. } else {
  1472. throw PythonOps.TypeError("expected class or type after INST, got {0}", DynamicHelpers.GetPythonType(cls));
  1473. }
  1474. }
  1475. private void LoadInt(CodeContext/*!*/ context) {
  1476. _stack.append(ReadIntFromString(context));
  1477. }
  1478. private void LoadList(CodeContext/*!*/ context) {
  1479. int markIndex = GetMarkIndex(context);
  1480. object list = _stack.__getslice__(markIndex + 1, _stack.__len__());
  1481. PopMark(markIndex);
  1482. _stack.append(list);
  1483. }
  1484. private void LoadLong(CodeContext/*!*/ context) {
  1485. _stack.append(ReadLongFromString(context));
  1486. }
  1487. private void LoadLong1(CodeContext/*!*/ context) {
  1488. _stack.append(ReadLong(context, ReadUInt8(context)));
  1489. }
  1490. private void LoadLong4(CodeContext/*!*/ context) {
  1491. _stack.append(ReadLong(context, ReadInt32(context)));
  1492. }
  1493. private void LoadLongBinGet(CodeContext/*!*/ context) {
  1494. _stack.append(MemoGet(context, (long)(int)ReadInt32(context)));
  1495. }
  1496. private void LoadLongBinPut(CodeContext/*!*/ context) {
  1497. MemoPut((long)(int)ReadInt32(context), _stack[-1]);
  1498. }
  1499. private void LoadMark(CodeContext/*!*/ context) {
  1500. _stack.append(_mark);
  1501. }
  1502. private void LoadNewFalse(CodeContext/*!*/ context) {
  1503. _stack.append(ScriptingRuntimeHelpers.False);
  1504. }
  1505. private void LoadNewObj(CodeContext/*!*/ context) {
  1506. PythonTuple args = _stack.pop() as PythonTuple;
  1507. if (args == null) {
  1508. throw PythonOps.TypeError("expected tuple as second argument to NEWOBJ, got {0}", DynamicHelpers.GetPythonType(args));
  1509. }
  1510. PythonType cls = _stack.pop() as PythonType;
  1511. if (args == null) {
  1512. throw PythonOps.TypeError("expected new-style type as first argument to NEWOBJ, got {0}", DynamicHelpers.GetPythonType(args));
  1513. }
  1514. PythonTypeSlot dts;
  1515. object value;
  1516. if (cls.TryResolveSlot(context, "__new__", out dts) &&
  1517. dts.TryGetValue(context, null, cls, out value)) {
  1518. object[] newargs = new object[args.__len__() + 1];
  1519. ((ICollection)args).CopyTo(newargs, 1);
  1520. newargs[0] = cls;
  1521. _stack.append(PythonOps.CallWithContext(context, value, newargs));
  1522. return;
  1523. }
  1524. throw PythonOps.TypeError("didn't find __new__");
  1525. }
  1526. private void LoadNewTrue(CodeContext/*!*/ context) {
  1527. _stack.append(ScriptingRuntimeHelpers.True);
  1528. }
  1529. private void LoadNoneValue(CodeContext/*!*/ context) {
  1530. _stack.append(null);
  1531. }
  1532. private void LoadObj(CodeContext/*!*/ context) {
  1533. int markIndex = GetMarkIndex(context);
  1534. if ((markIndex + 1) >= _stack.Count) {
  1535. throw PythonExceptions.CreateThrowable(UnpicklingError(context), "could not find MARK");
  1536. }
  1537. object cls = _stack[markIndex + 1];
  1538. if (cls is OldClass || cls is PythonType) {
  1539. object[] args = _stack.GetSliceAsArray(markIndex + 2, _stack.__len__());
  1540. PopMark(markIndex);
  1541. _stack.append(MakeInstance(context, cls, args));
  1542. } else {
  1543. throw PythonOps.TypeError("expected class or type as first argument to INST, got {0}", DynamicHelpers.GetPythonType(cls));
  1544. }
  1545. }
  1546. private void LoadPersId(CodeContext/*!*/ context) {
  1547. if (_pers_loader == null) {
  1548. throw CannotUnpickle(context, "A load persistent ID instruction is present but no persistent_load function is available");
  1549. }
  1550. _stack.append(PythonContext.GetContext(context).CallSplat(_pers_loader, ReadLineNoNewline(context)));
  1551. }
  1552. private void LoadPop(CodeContext/*!*/ context) {
  1553. _stack.pop();
  1554. }
  1555. private void LoadPopMark(CodeContext/*!*/ context) {
  1556. PopMark(GetMarkIndex(context));
  1557. }
  1558. private void LoadProto(CodeContext/*!*/ context) {
  1559. int proto = ReadUInt8(context);
  1560. if (proto > 2) throw PythonOps.ValueError("unsupported pickle protocol: {0}", proto);
  1561. // discard result
  1562. }
  1563. private void LoadPut(CodeContext/*!*/ context) {
  1564. MemoPut((long)(int)ReadIntFromString(context), _stack[-1]);
  1565. }
  1566. private void LoadReduce(CodeContext/*!*/ context) {
  1567. object args = _stack.pop();
  1568. object callable = _stack.pop();
  1569. if (args == null) {
  1570. _stack.append(PythonCalls.Call(context, PythonOps.GetBoundAttr(context, callable, "__basicnew__")));
  1571. } else if (!DynamicHelpers.GetPythonType(args).Equals(TypeCache.PythonTuple)) {
  1572. throw PythonOps.TypeError(
  1573. "while executing REDUCE, expected tuple at the top of the stack, but got {0}",
  1574. DynamicHelpers.GetPythonType(args)
  1575. );
  1576. }
  1577. _stack.append(PythonOps.CallWithArgsTupleAndContext(context, callable, ArrayUtils.EmptyObjects, args));
  1578. }
  1579. private void LoadSetItem(CodeContext/*!*/ context) {
  1580. object value = _stack.pop();
  1581. object key = _stack.pop();
  1582. PythonDictionary dict = _stack[-1] as PythonDictionary;
  1583. if (dict == null) {
  1584. throw PythonOps.TypeError(
  1585. "while executing SETITEM, expected dict at stack[-3], but got {0}",
  1586. DynamicHelpers.GetPythonType(_stack[-1])
  1587. );
  1588. }
  1589. dict[key] = value;
  1590. }
  1591. private void LoadSetItems(CodeContext/*!*/ context) {
  1592. int markIndex = GetMarkIndex(context);
  1593. PythonDictionary dict = _stack[markIndex - 1] as PythonDictionary;
  1594. if (dict == null) {
  1595. throw PythonOps.TypeError(
  1596. "while executing SETITEMS, expected dict below last mark, but got {0}",
  1597. DynamicHelpers.GetPythonType(_stack[markIndex - 1])
  1598. );
  1599. }
  1600. SetItems(dict, markIndex);
  1601. }
  1602. private void LoadShortBinstring(CodeContext/*!*/ context) {
  1603. _stack.append(Read(context, ReadUInt8(context)));
  1604. }
  1605. private void LoadString(CodeContext/*!*/ context) {
  1606. string repr = ReadLineNoNewline(context);
  1607. if (repr.Length < 2 ||
  1608. !(
  1609. repr[0] == '"' && repr[repr.Length - 1] == '"' ||
  1610. repr[0] == '\'' && repr[repr.Length - 1] == '\''
  1611. )
  1612. ) {
  1613. throw PythonOps.ValueError("while executing STRING, expected string that starts and ends with quotes");
  1614. }
  1615. _stack.append(StringOps.decode(context, repr.Substring(1, repr.Length - 2), "string-escape", "strict"));
  1616. }
  1617. private void LoadTuple(CodeContext/*!*/ context) {
  1618. int markIndex = GetMarkIndex(context);
  1619. PythonTuple tuple = PythonTuple.MakeTuple(_stack.GetSliceAsArray(markIndex + 1, _stack.__len__()));
  1620. PopMark(markIndex);
  1621. _stack.append(tuple);
  1622. }
  1623. private void LoadTuple1(CodeContext/*!*/ context) {
  1624. object item0 = _stack.pop();
  1625. _stack.append(PythonTuple.MakeTuple(item0));
  1626. }
  1627. private void LoadTuple2(CodeContext/*!*/ context) {
  1628. object item1 = _stack.pop();
  1629. object item0 = _stack.pop();
  1630. _stack.append(PythonTuple.MakeTuple(item0, item1));
  1631. }
  1632. private void LoadTuple3(CodeContext/*!*/ context) {
  1633. object item2 = _stack.pop();
  1634. object item1 = _stack.pop();
  1635. object item0 = _stack.pop();
  1636. _stack.append(PythonTuple.MakeTuple(item0, item1, item2));
  1637. }
  1638. private void LoadUnicode(CodeContext/*!*/ context) {
  1639. _stack.append(StringOps.decode(context, ReadLineNoNewline(context), "raw-unicode-escape", "strict"));
  1640. }
  1641. }
  1642. #endregion
  1643. private static PythonType PicklingError(CodeContext/*!*/ context) {
  1644. return (PythonType)PythonContext.GetContext(context).GetModuleState("PicklingError");
  1645. }
  1646. private static PythonType PickleError(CodeContext/*!*/ context) {
  1647. return (PythonType)PythonContext.GetContext(context).GetModuleState("PickleError");
  1648. }
  1649. private static PythonType UnpicklingError(CodeContext/*!*/ context) {
  1650. return (PythonType)PythonContext.GetContext(context).GetModuleState("UnpicklingError");
  1651. }
  1652. private static PythonType BadPickleGet(CodeContext/*!*/ context) {
  1653. return (PythonType)PythonContext.GetContext(context).GetModuleState("BadPickleGet");
  1654. }
  1655. }
  1656. }