python-pickle 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.github/workflows/ruby.yml +27 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +14 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +149 -0
- data/Rakefile +13 -0
- data/gemspec.yml +25 -0
- data/lib/python/pickle/byte_array.rb +40 -0
- data/lib/python/pickle/deserializer.rb +595 -0
- data/lib/python/pickle/exceptions.rb +12 -0
- data/lib/python/pickle/instruction.rb +52 -0
- data/lib/python/pickle/instructions/add_items.rb +26 -0
- data/lib/python/pickle/instructions/append.rb +24 -0
- data/lib/python/pickle/instructions/appends.rb +26 -0
- data/lib/python/pickle/instructions/bin_bytes.rb +32 -0
- data/lib/python/pickle/instructions/bin_bytes8.rb +32 -0
- data/lib/python/pickle/instructions/bin_float.rb +29 -0
- data/lib/python/pickle/instructions/bin_get.rb +27 -0
- data/lib/python/pickle/instructions/bin_int1.rb +29 -0
- data/lib/python/pickle/instructions/bin_put.rb +29 -0
- data/lib/python/pickle/instructions/bin_string.rb +32 -0
- data/lib/python/pickle/instructions/bin_unicode.rb +32 -0
- data/lib/python/pickle/instructions/bin_unicode8.rb +32 -0
- data/lib/python/pickle/instructions/build.rb +24 -0
- data/lib/python/pickle/instructions/byte_array8.rb +32 -0
- data/lib/python/pickle/instructions/dict.rb +17 -0
- data/lib/python/pickle/instructions/dup.rb +24 -0
- data/lib/python/pickle/instructions/empty_dict.rb +26 -0
- data/lib/python/pickle/instructions/empty_list.rb +26 -0
- data/lib/python/pickle/instructions/empty_set.rb +26 -0
- data/lib/python/pickle/instructions/empty_tuple.rb +26 -0
- data/lib/python/pickle/instructions/ext1.rb +29 -0
- data/lib/python/pickle/instructions/ext2.rb +29 -0
- data/lib/python/pickle/instructions/ext4.rb +29 -0
- data/lib/python/pickle/instructions/float.rb +24 -0
- data/lib/python/pickle/instructions/frame.rb +29 -0
- data/lib/python/pickle/instructions/frozen_set.rb +26 -0
- data/lib/python/pickle/instructions/get.rb +27 -0
- data/lib/python/pickle/instructions/global.rb +62 -0
- data/lib/python/pickle/instructions/has_length_and_value.rb +58 -0
- data/lib/python/pickle/instructions/has_value.rb +50 -0
- data/lib/python/pickle/instructions/int.rb +24 -0
- data/lib/python/pickle/instructions/list.rb +24 -0
- data/lib/python/pickle/instructions/long.rb +24 -0
- data/lib/python/pickle/instructions/long1.rb +32 -0
- data/lib/python/pickle/instructions/long4.rb +32 -0
- data/lib/python/pickle/instructions/long_bin_get.rb +27 -0
- data/lib/python/pickle/instructions/mark.rb +24 -0
- data/lib/python/pickle/instructions/memoize.rb +26 -0
- data/lib/python/pickle/instructions/new_false.rb +24 -0
- data/lib/python/pickle/instructions/new_obj.rb +26 -0
- data/lib/python/pickle/instructions/new_obj_ex.rb +26 -0
- data/lib/python/pickle/instructions/new_true.rb +24 -0
- data/lib/python/pickle/instructions/next_buffer.rb +26 -0
- data/lib/python/pickle/instructions/none.rb +24 -0
- data/lib/python/pickle/instructions/pop.rb +24 -0
- data/lib/python/pickle/instructions/pop_mark.rb +24 -0
- data/lib/python/pickle/instructions/proto.rb +29 -0
- data/lib/python/pickle/instructions/put.rb +24 -0
- data/lib/python/pickle/instructions/readonly_buffer.rb +26 -0
- data/lib/python/pickle/instructions/reduce.rb +24 -0
- data/lib/python/pickle/instructions/set_item.rb +24 -0
- data/lib/python/pickle/instructions/set_items.rb +26 -0
- data/lib/python/pickle/instructions/short_bin_bytes.rb +32 -0
- data/lib/python/pickle/instructions/short_bin_string.rb +32 -0
- data/lib/python/pickle/instructions/short_bin_unicode.rb +32 -0
- data/lib/python/pickle/instructions/stack_global.rb +26 -0
- data/lib/python/pickle/instructions/stop.rb +24 -0
- data/lib/python/pickle/instructions/string.rb +24 -0
- data/lib/python/pickle/instructions/tuple.rb +24 -0
- data/lib/python/pickle/instructions/tuple1.rb +24 -0
- data/lib/python/pickle/instructions/tuple2.rb +24 -0
- data/lib/python/pickle/instructions/tuple3.rb +24 -0
- data/lib/python/pickle/protocol.rb +56 -0
- data/lib/python/pickle/protocol0.rb +399 -0
- data/lib/python/pickle/protocol1.rb +183 -0
- data/lib/python/pickle/protocol2.rb +229 -0
- data/lib/python/pickle/protocol3.rb +163 -0
- data/lib/python/pickle/protocol4.rb +285 -0
- data/lib/python/pickle/protocol5.rb +218 -0
- data/lib/python/pickle/py_class.rb +75 -0
- data/lib/python/pickle/py_object.rb +141 -0
- data/lib/python/pickle/tuple.rb +19 -0
- data/lib/python/pickle/version.rb +6 -0
- data/lib/python/pickle.rb +226 -0
- data/python-pickle.gemspec +62 -0
- data/spec/byte_array_spec.rb +54 -0
- data/spec/deserializer_spec.rb +1201 -0
- data/spec/fixtures/ascii_str_v3.pkl +0 -0
- data/spec/fixtures/ascii_str_v4.pkl +0 -0
- data/spec/fixtures/ascii_str_v5.pkl +0 -0
- data/spec/fixtures/bin_str_v0.pkl +3 -0
- data/spec/fixtures/bin_str_v1.pkl +0 -0
- data/spec/fixtures/bin_str_v2.pkl +0 -0
- data/spec/fixtures/bin_str_v3.pkl +0 -0
- data/spec/fixtures/bin_str_v4.pkl +0 -0
- data/spec/fixtures/bin_str_v5.pkl +0 -0
- data/spec/fixtures/bytearray_v0.pkl +10 -0
- data/spec/fixtures/bytearray_v1.pkl +0 -0
- data/spec/fixtures/bytearray_v2.pkl +0 -0
- data/spec/fixtures/bytearray_v3.pkl +0 -0
- data/spec/fixtures/bytearray_v4.pkl +0 -0
- data/spec/fixtures/bytearray_v5.pkl +0 -0
- data/spec/fixtures/class_v0.pkl +4 -0
- data/spec/fixtures/class_v1.pkl +0 -0
- data/spec/fixtures/class_v2.pkl +0 -0
- data/spec/fixtures/class_v3.pkl +0 -0
- data/spec/fixtures/class_v4.pkl +0 -0
- data/spec/fixtures/class_v5.pkl +0 -0
- data/spec/fixtures/dict_v0.pkl +6 -0
- data/spec/fixtures/dict_v1.pkl +0 -0
- data/spec/fixtures/dict_v2.pkl +0 -0
- data/spec/fixtures/dict_v3.pkl +0 -0
- data/spec/fixtures/dict_v4.pkl +0 -0
- data/spec/fixtures/dict_v5.pkl +0 -0
- data/spec/fixtures/escaped_str_v0.pkl +3 -0
- data/spec/fixtures/escaped_str_v1.pkl +0 -0
- data/spec/fixtures/escaped_str_v2.pkl +0 -0
- data/spec/fixtures/false_v0.pkl +2 -0
- data/spec/fixtures/false_v1.pkl +2 -0
- data/spec/fixtures/false_v2.pkl +1 -0
- data/spec/fixtures/false_v3.pkl +1 -0
- data/spec/fixtures/false_v4.pkl +1 -0
- data/spec/fixtures/false_v5.pkl +1 -0
- data/spec/fixtures/float_v0.pkl +2 -0
- data/spec/fixtures/float_v1.pkl +1 -0
- data/spec/fixtures/float_v2.pkl +1 -0
- data/spec/fixtures/float_v3.pkl +1 -0
- data/spec/fixtures/float_v4.pkl +0 -0
- data/spec/fixtures/float_v5.pkl +0 -0
- data/spec/fixtures/function_v0.pkl +4 -0
- data/spec/fixtures/function_v1.pkl +0 -0
- data/spec/fixtures/function_v2.pkl +0 -0
- data/spec/fixtures/function_v3.pkl +0 -0
- data/spec/fixtures/function_v4.pkl +0 -0
- data/spec/fixtures/function_v5.pkl +0 -0
- data/spec/fixtures/hex_str_v0.pkl +3 -0
- data/spec/fixtures/hex_str_v1.pkl +0 -0
- data/spec/fixtures/hex_str_v2.pkl +0 -0
- data/spec/fixtures/int_v0.pkl +2 -0
- data/spec/fixtures/int_v1.pkl +1 -0
- data/spec/fixtures/int_v2.pkl +1 -0
- data/spec/fixtures/int_v3.pkl +1 -0
- data/spec/fixtures/int_v4.pkl +1 -0
- data/spec/fixtures/int_v5.pkl +1 -0
- data/spec/fixtures/list_v0.pkl +7 -0
- data/spec/fixtures/list_v1.pkl +0 -0
- data/spec/fixtures/list_v2.pkl +0 -0
- data/spec/fixtures/list_v3.pkl +0 -0
- data/spec/fixtures/list_v4.pkl +0 -0
- data/spec/fixtures/list_v5.pkl +0 -0
- data/spec/fixtures/long_v0.pkl +2 -0
- data/spec/fixtures/long_v1.pkl +2 -0
- data/spec/fixtures/long_v2.pkl +0 -0
- data/spec/fixtures/long_v3.pkl +0 -0
- data/spec/fixtures/long_v4.pkl +0 -0
- data/spec/fixtures/long_v5.pkl +0 -0
- data/spec/fixtures/nested_dict_v0.pkl +12 -0
- data/spec/fixtures/nested_dict_v1.pkl +0 -0
- data/spec/fixtures/nested_dict_v2.pkl +0 -0
- data/spec/fixtures/nested_dict_v3.pkl +0 -0
- data/spec/fixtures/nested_dict_v4.pkl +0 -0
- data/spec/fixtures/nested_dict_v5.pkl +0 -0
- data/spec/fixtures/nested_list_v0.pkl +9 -0
- data/spec/fixtures/nested_list_v1.pkl +0 -0
- data/spec/fixtures/nested_list_v2.pkl +0 -0
- data/spec/fixtures/nested_list_v3.pkl +0 -0
- data/spec/fixtures/nested_list_v4.pkl +0 -0
- data/spec/fixtures/nested_list_v5.pkl +0 -0
- data/spec/fixtures/none_v0.pkl +1 -0
- data/spec/fixtures/none_v1.pkl +1 -0
- data/spec/fixtures/none_v2.pkl +1 -0
- data/spec/fixtures/none_v3.pkl +1 -0
- data/spec/fixtures/none_v4.pkl +1 -0
- data/spec/fixtures/none_v5.pkl +1 -0
- data/spec/fixtures/object_v0.pkl +19 -0
- data/spec/fixtures/object_v1.pkl +0 -0
- data/spec/fixtures/object_v2.pkl +0 -0
- data/spec/fixtures/object_v3.pkl +0 -0
- data/spec/fixtures/object_v4.pkl +0 -0
- data/spec/fixtures/object_v5.pkl +0 -0
- data/spec/fixtures/str_v0.pkl +3 -0
- data/spec/fixtures/str_v1.pkl +0 -0
- data/spec/fixtures/str_v2.pkl +0 -0
- data/spec/fixtures/str_v3.pkl +0 -0
- data/spec/fixtures/str_v4.pkl +0 -0
- data/spec/fixtures/str_v5.pkl +0 -0
- data/spec/fixtures/true_v0.pkl +2 -0
- data/spec/fixtures/true_v1.pkl +2 -0
- data/spec/fixtures/true_v2.pkl +1 -0
- data/spec/fixtures/true_v3.pkl +1 -0
- data/spec/fixtures/true_v4.pkl +1 -0
- data/spec/fixtures/true_v5.pkl +1 -0
- data/spec/fixtures/unicode_str_v0.pkl +3 -0
- data/spec/fixtures/unicode_str_v1.pkl +0 -0
- data/spec/fixtures/unicode_str_v2.pkl +0 -0
- data/spec/fixtures/unicode_str_v3.pkl +0 -0
- data/spec/fixtures/unicode_str_v4.pkl +0 -0
- data/spec/fixtures/unicode_str_v5.pkl +0 -0
- data/spec/generate_pickles2.py +41 -0
- data/spec/generate_pickles3.py +40 -0
- data/spec/integration/load/protocol0_spec.rb +258 -0
- data/spec/integration/load/protocol1_spec.rb +258 -0
- data/spec/integration/load/protocol2_spec.rb +258 -0
- data/spec/integration/load/protocol3_spec.rb +258 -0
- data/spec/integration/load/protocol4_spec.rb +258 -0
- data/spec/integration/load/protocol5_spec.rb +258 -0
- data/spec/integration/parse/protocol0_spec.rb +467 -0
- data/spec/integration/parse/protocol1_spec.rb +459 -0
- data/spec/integration/parse/protocol2_spec.rb +471 -0
- data/spec/integration/parse/protocol3_spec.rb +407 -0
- data/spec/integration/parse/protocol4_spec.rb +439 -0
- data/spec/integration/parse/protocol5_spec.rb +419 -0
- data/spec/pickle_spec.rb +163 -0
- data/spec/protocol0_read_instruction_examples.rb +211 -0
- data/spec/protocol0_spec.rb +445 -0
- data/spec/protocol1_read_instruction_examples.rb +156 -0
- data/spec/protocol1_spec.rb +59 -0
- data/spec/protocol2_read_instruction_examples.rb +135 -0
- data/spec/protocol2_spec.rb +128 -0
- data/spec/protocol3_read_instruction_examples.rb +29 -0
- data/spec/protocol3_spec.rb +32 -0
- data/spec/protocol4_read_instruction_examples.rb +142 -0
- data/spec/protocol4_spec.rb +58 -0
- data/spec/protocol5_spec.rb +68 -0
- data/spec/py_class_spec.rb +62 -0
- data/spec/py_object_spec.rb +149 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/tuple_spec.rb +18 -0
- metadata +325 -0
@@ -0,0 +1,595 @@
|
|
1
|
+
require 'python/pickle/py_class'
|
2
|
+
require 'python/pickle/py_object'
|
3
|
+
require 'python/pickle/tuple'
|
4
|
+
require 'python/pickle/byte_array'
|
5
|
+
require 'python/pickle/exceptions'
|
6
|
+
|
7
|
+
require 'python/pickle/instructions/proto'
|
8
|
+
require 'python/pickle/instructions/frame'
|
9
|
+
require 'python/pickle/instructions/get'
|
10
|
+
require 'python/pickle/instructions/bin_get'
|
11
|
+
require 'python/pickle/instructions/long_bin_get'
|
12
|
+
require 'python/pickle/instructions/mark'
|
13
|
+
require 'python/pickle/instructions/pop_mark'
|
14
|
+
require 'python/pickle/instructions/dup'
|
15
|
+
require 'python/pickle/instructions/put'
|
16
|
+
require 'python/pickle/instructions/bin_put'
|
17
|
+
require 'python/pickle/instructions/pop'
|
18
|
+
require 'python/pickle/instructions/memoize'
|
19
|
+
require 'python/pickle/instructions/ext1'
|
20
|
+
require 'python/pickle/instructions/ext2'
|
21
|
+
require 'python/pickle/instructions/ext4'
|
22
|
+
require 'python/pickle/instructions/none'
|
23
|
+
require 'python/pickle/instructions/new_true'
|
24
|
+
require 'python/pickle/instructions/new_false'
|
25
|
+
require 'python/pickle/instructions/float'
|
26
|
+
require 'python/pickle/instructions/bin_float'
|
27
|
+
require 'python/pickle/instructions/int'
|
28
|
+
require 'python/pickle/instructions/bin_int1'
|
29
|
+
require 'python/pickle/instructions/long'
|
30
|
+
require 'python/pickle/instructions/long1'
|
31
|
+
require 'python/pickle/instructions/long4'
|
32
|
+
require 'python/pickle/instructions/bin_bytes'
|
33
|
+
require 'python/pickle/instructions/short_bin_bytes'
|
34
|
+
require 'python/pickle/instructions/bin_bytes8'
|
35
|
+
require 'python/pickle/instructions/string'
|
36
|
+
require 'python/pickle/instructions/bin_string'
|
37
|
+
require 'python/pickle/instructions/short_bin_string'
|
38
|
+
require 'python/pickle/instructions/bin_unicode'
|
39
|
+
require 'python/pickle/instructions/short_bin_unicode'
|
40
|
+
require 'python/pickle/instructions/bin_unicode8'
|
41
|
+
require 'python/pickle/instructions/byte_array8'
|
42
|
+
require 'python/pickle/instructions/empty_list'
|
43
|
+
require 'python/pickle/instructions/empty_tuple'
|
44
|
+
require 'python/pickle/instructions/tuple'
|
45
|
+
require 'python/pickle/instructions/empty_dict'
|
46
|
+
require 'python/pickle/instructions/append'
|
47
|
+
require 'python/pickle/instructions/appends'
|
48
|
+
require 'python/pickle/instructions/list'
|
49
|
+
require 'python/pickle/instructions/tuple1'
|
50
|
+
require 'python/pickle/instructions/tuple2'
|
51
|
+
require 'python/pickle/instructions/tuple3'
|
52
|
+
require 'python/pickle/instructions/dict'
|
53
|
+
require 'python/pickle/instructions/global'
|
54
|
+
require 'python/pickle/instructions/stack_global'
|
55
|
+
require 'python/pickle/instructions/new_obj'
|
56
|
+
require 'python/pickle/instructions/new_obj_ex'
|
57
|
+
require 'python/pickle/instructions/reduce'
|
58
|
+
require 'python/pickle/instructions/build'
|
59
|
+
require 'python/pickle/instructions/set_item'
|
60
|
+
require 'python/pickle/instructions/set_items'
|
61
|
+
require 'python/pickle/instructions/stop'
|
62
|
+
|
63
|
+
module Python
|
64
|
+
module Pickle
|
65
|
+
#
|
66
|
+
# Handles deserializing a stream of Python Pickle instructions.
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
#
|
70
|
+
class Deserializer
|
71
|
+
|
72
|
+
# The meta-stack for saving/restoring {#stack}.
|
73
|
+
#
|
74
|
+
# @return [Array]
|
75
|
+
attr_reader :meta_stack
|
76
|
+
|
77
|
+
# The object stack.
|
78
|
+
#
|
79
|
+
# @return [Array]
|
80
|
+
attr_reader :stack
|
81
|
+
|
82
|
+
# The memo dictionary.
|
83
|
+
#
|
84
|
+
# @return [Array]
|
85
|
+
attr_reader :memo
|
86
|
+
|
87
|
+
# Mapping of Python constants to Ruby classes and methods.
|
88
|
+
#
|
89
|
+
# @return [Hash{String => Hash{String => Class,Method}}]
|
90
|
+
attr_reader :constants
|
91
|
+
|
92
|
+
# Mapping of Python Pickle extension codes to Ruby objects.
|
93
|
+
#
|
94
|
+
# @return [Hash{Integer => Object}]
|
95
|
+
attr_reader :extensions
|
96
|
+
|
97
|
+
# The Python `object` class.
|
98
|
+
OBJECT_CLASS = PyClass.new('__builtins__','object')
|
99
|
+
|
100
|
+
#
|
101
|
+
# Initializes the deserializer.
|
102
|
+
#
|
103
|
+
# @param [Hash{Integer => Object}] extensions
|
104
|
+
# A Hash of registered extension IDs and their Objects.
|
105
|
+
#
|
106
|
+
# @param [Hash{String => Hash{String => Class,Method}}] constants
|
107
|
+
# An optional mapping of custom Python constant names to Ruby classes
|
108
|
+
# or methods.
|
109
|
+
#
|
110
|
+
def initialize(constants: nil, extensions: nil)
|
111
|
+
@meta_stack = []
|
112
|
+
@stack = []
|
113
|
+
@memo = []
|
114
|
+
|
115
|
+
@constants = {
|
116
|
+
# Python 2.x
|
117
|
+
'copy_reg' => {
|
118
|
+
'_reconstructor' => method(:copyreg_reconstructor)
|
119
|
+
},
|
120
|
+
|
121
|
+
'__builtin__' => {
|
122
|
+
'object' => OBJECT_CLASS,
|
123
|
+
'bytearray' => ByteArray
|
124
|
+
},
|
125
|
+
|
126
|
+
# Python 3.x
|
127
|
+
'builtins' => {
|
128
|
+
'object' => OBJECT_CLASS,
|
129
|
+
'bytearray' => ByteArray
|
130
|
+
}
|
131
|
+
}
|
132
|
+
@constants.merge!(constants) if constants
|
133
|
+
|
134
|
+
@extensions = {}
|
135
|
+
@extensions.merge!(extensions) if extensions
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Pushes the {#stack} onto the {#meta_stack}.
|
140
|
+
#
|
141
|
+
def push_meta_stack
|
142
|
+
@meta_stack.push(@stack)
|
143
|
+
@stack = []
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Pops a previous stack off of {#meta_stack} and restores {#stack}.
|
148
|
+
#
|
149
|
+
# @return [Array]
|
150
|
+
# The current {#stack} values will be returned.
|
151
|
+
#
|
152
|
+
def pop_meta_stack
|
153
|
+
items = @stack
|
154
|
+
@stack = (@meta_stack.pop || [])
|
155
|
+
return items
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Executes a Python Pickle instruction.
|
160
|
+
#
|
161
|
+
def execute(instruction)
|
162
|
+
case instruction
|
163
|
+
when Instructions::Proto,
|
164
|
+
Instructions::Frame
|
165
|
+
# no-op
|
166
|
+
when Instructions::Get,
|
167
|
+
Instructions::BinGet,
|
168
|
+
Instructions::LongBinGet
|
169
|
+
execute_get(instruction)
|
170
|
+
when Instructions::MARK then execute_mark
|
171
|
+
when Instructions::POP_MARK then execute_pop_mark
|
172
|
+
when Instructions::DUP then execute_dup
|
173
|
+
when Instructions::Put,
|
174
|
+
Instructions::BinPut
|
175
|
+
execute_put(instruction)
|
176
|
+
when Instructions::POP then execute_pop
|
177
|
+
when Instructions::MEMOIZE then execute_memoize
|
178
|
+
when Instructions::Ext1,
|
179
|
+
Instructions::Ext2,
|
180
|
+
Instructions::Ext4
|
181
|
+
execute_ext(instruction)
|
182
|
+
when Instructions::NONE then execute_none
|
183
|
+
when Instructions::NEWTRUE then execute_newtrue
|
184
|
+
when Instructions::NEWFALSE then execute_newfalse
|
185
|
+
when Instructions::Float,
|
186
|
+
Instructions::BinFloat,
|
187
|
+
Instructions::Int,
|
188
|
+
Instructions::BinInt1,
|
189
|
+
Instructions::Long,
|
190
|
+
Instructions::Long1,
|
191
|
+
Instructions::Long4,
|
192
|
+
Instructions::BinBytes,
|
193
|
+
Instructions::ShortBinBytes,
|
194
|
+
Instructions::BinBytes8,
|
195
|
+
Instructions::String,
|
196
|
+
Instructions::BinString,
|
197
|
+
Instructions::ShortBinString,
|
198
|
+
Instructions::BinUnicode,
|
199
|
+
Instructions::ShortBinUnicode,
|
200
|
+
Instructions::BinUnicode8
|
201
|
+
@stack.push(instruction.value)
|
202
|
+
when Instructions::ByteArray8 then execute_byte_array8(instruction)
|
203
|
+
when Instructions::EMPTY_LIST then execute_empty_list
|
204
|
+
when Instructions::EMPTY_TUPLE then execute_empty_tuple
|
205
|
+
when Instructions::TUPLE then execute_tuple
|
206
|
+
when Instructions::EMPTY_DICT then execute_empty_dict
|
207
|
+
when Instructions::APPEND then execute_append
|
208
|
+
when Instructions::APPENDS then execute_appends
|
209
|
+
when Instructions::LIST then execute_list
|
210
|
+
when Instructions::TUPLE1 then execute_tuple1
|
211
|
+
when Instructions::TUPLE2 then execute_tuple2
|
212
|
+
when Instructions::TUPLE3 then execute_tuple3
|
213
|
+
when Instructions::DICT then execute_dict
|
214
|
+
when Instructions::Global then execute_global(instruction)
|
215
|
+
when Instructions::STACK_GLOBAL then execute_stack_global
|
216
|
+
when Instructions::NEWOBJ then execute_newobj
|
217
|
+
when Instructions::NEWOBJ_EX then execute_newobj_ex
|
218
|
+
when Instructions::REDUCE then execute_reduce
|
219
|
+
when Instructions::BUILD then execute_build
|
220
|
+
when Instructions::SETITEM then execute_setitem
|
221
|
+
when Instructions::SETITEMS then execute_setitems
|
222
|
+
when Instructions::STOP
|
223
|
+
return :halt, @stack.pop
|
224
|
+
else
|
225
|
+
raise(NotImplementedError,"instruction is currently not fully supported: #{instruction.inspect}")
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# Executes a `GET`, `BINGET`, or `LONG_BINGET` instruction.
|
231
|
+
#
|
232
|
+
# @param [Instructions::Get, Instructions::BinGet, Instructions::LongBinGet] instructions
|
233
|
+
# The `GET`, `BINGET`, or `LONG_BINGET` instruction.
|
234
|
+
#
|
235
|
+
def execute_get(instruction)
|
236
|
+
index = instruction.value
|
237
|
+
|
238
|
+
@stack.push(@memo[index])
|
239
|
+
end
|
240
|
+
|
241
|
+
#
|
242
|
+
# Executes a `MARK` instruction.
|
243
|
+
#
|
244
|
+
def execute_mark
|
245
|
+
push_meta_stack
|
246
|
+
end
|
247
|
+
|
248
|
+
#
|
249
|
+
# Executes a `POP_MARK` instruction.
|
250
|
+
#
|
251
|
+
def execute_pop_mark
|
252
|
+
pop_meta_stack
|
253
|
+
end
|
254
|
+
|
255
|
+
#
|
256
|
+
# Executes a `DUP` instruction.
|
257
|
+
#
|
258
|
+
def execute_dup
|
259
|
+
@stack.push(@stack.last)
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
# Executes a `PUT`, `BINPUT`, or `LONG_BINPUT` instruction.
|
264
|
+
#
|
265
|
+
# @param [Instructions::Get, Instructions::BinGet, Instructions::LongBinGet] instructions
|
266
|
+
# The `PUT`, `BINPUT`, or `LONG_BINPUT` instruction.
|
267
|
+
#
|
268
|
+
def execute_put(instruction)
|
269
|
+
index = instruction.value
|
270
|
+
value = @stack.last
|
271
|
+
|
272
|
+
@memo[index] = value
|
273
|
+
end
|
274
|
+
|
275
|
+
#
|
276
|
+
# Executes the `POP` instruction.
|
277
|
+
#
|
278
|
+
def execute_pop
|
279
|
+
@stack.pop
|
280
|
+
end
|
281
|
+
|
282
|
+
#
|
283
|
+
# Executes the `MEMOIZE` instruction.
|
284
|
+
#
|
285
|
+
def execute_memoize
|
286
|
+
@memo.push(@stack.last)
|
287
|
+
end
|
288
|
+
|
289
|
+
#
|
290
|
+
# Executes a `EXT1`, `EXT2`, or `EXT4` instruction.
|
291
|
+
#
|
292
|
+
# @param [Instructions::Ext1, Instructions::Ext2, Instructions::Ext4] instruction
|
293
|
+
# The `EXT1`, `EXT2`, or `EXT4` instruction.
|
294
|
+
#
|
295
|
+
# @raise [DeserializationError]
|
296
|
+
# The execution ID was not found in {#extensions}.
|
297
|
+
#
|
298
|
+
def execute_ext(instruction)
|
299
|
+
ext_id = instruction.value
|
300
|
+
object = @extensions.fetch(ext_id) do
|
301
|
+
raise(DeserializationError,"unknown extension ID: #{ext_id.inspect}")
|
302
|
+
end
|
303
|
+
|
304
|
+
@stack.push(object)
|
305
|
+
end
|
306
|
+
|
307
|
+
#
|
308
|
+
# Executes a `NONE` instruction.
|
309
|
+
#
|
310
|
+
def execute_none
|
311
|
+
@stack.push(nil)
|
312
|
+
end
|
313
|
+
|
314
|
+
#
|
315
|
+
# Executes a `NEWTRUE` instruction.
|
316
|
+
#
|
317
|
+
def execute_newtrue
|
318
|
+
@stack.push(true)
|
319
|
+
end
|
320
|
+
|
321
|
+
#
|
322
|
+
# Executes a `NEWFALSE` instruction.
|
323
|
+
#
|
324
|
+
def execute_newfalse
|
325
|
+
@stack.push(false)
|
326
|
+
end
|
327
|
+
|
328
|
+
#
|
329
|
+
# Executes a `BYTEARRAY8` instruction.
|
330
|
+
#
|
331
|
+
def execute_byte_array8(instruction)
|
332
|
+
@stack.push(ByteArray.new(instruction.value))
|
333
|
+
end
|
334
|
+
|
335
|
+
#
|
336
|
+
# Executes the `EMPTY_LIST` instruction.
|
337
|
+
#
|
338
|
+
def execute_empty_list
|
339
|
+
@stack.push([])
|
340
|
+
end
|
341
|
+
|
342
|
+
#
|
343
|
+
# Executes the `EMPTY_TUPLE` instruction.
|
344
|
+
#
|
345
|
+
def execute_empty_tuple
|
346
|
+
@stack.push(Tuple.new)
|
347
|
+
end
|
348
|
+
|
349
|
+
#
|
350
|
+
# Executes a `TUPLE` instruction.
|
351
|
+
#
|
352
|
+
def execute_tuple
|
353
|
+
items = Tuple.new(pop_meta_stack)
|
354
|
+
@stack.push(items)
|
355
|
+
end
|
356
|
+
|
357
|
+
#
|
358
|
+
# Executes an `EMPTY_DICT` instruction.
|
359
|
+
#
|
360
|
+
def execute_empty_dict
|
361
|
+
@stack.push({})
|
362
|
+
end
|
363
|
+
|
364
|
+
#
|
365
|
+
# Executes an `APPEND` instruction.
|
366
|
+
#
|
367
|
+
def execute_append
|
368
|
+
item = @stack.pop
|
369
|
+
list = @stack.last
|
370
|
+
|
371
|
+
unless list.kind_of?(Array)
|
372
|
+
raise(DeserializationError,"cannot append element #{item.inspect} onto a non-Array: #{list.inspect}")
|
373
|
+
end
|
374
|
+
|
375
|
+
list.push(item)
|
376
|
+
end
|
377
|
+
|
378
|
+
#
|
379
|
+
# Executes an `APPENDS` instruction.
|
380
|
+
#
|
381
|
+
def execute_appends
|
382
|
+
items = pop_meta_stack
|
383
|
+
list = @stack.last
|
384
|
+
|
385
|
+
unless list.kind_of?(Array)
|
386
|
+
raise(DeserializationError,"cannot append elements #{items.inspect} onto a non-Array: #{list.inspect}")
|
387
|
+
end
|
388
|
+
|
389
|
+
list.concat(items)
|
390
|
+
end
|
391
|
+
|
392
|
+
#
|
393
|
+
# Executes a `LIST` instruction.
|
394
|
+
#
|
395
|
+
def execute_list
|
396
|
+
elements = pop_meta_stack
|
397
|
+
@stack.push(elements)
|
398
|
+
end
|
399
|
+
|
400
|
+
#
|
401
|
+
# Executes a `TUPLE1` instruction.
|
402
|
+
#
|
403
|
+
def execute_tuple1
|
404
|
+
new_tuple = Tuple.new(@stack.pop(1))
|
405
|
+
|
406
|
+
@stack.push(new_tuple)
|
407
|
+
end
|
408
|
+
|
409
|
+
#
|
410
|
+
# Executes a `TUPLE2` instruction.
|
411
|
+
#
|
412
|
+
def execute_tuple2
|
413
|
+
new_tuple = Tuple.new(@stack.pop(2))
|
414
|
+
|
415
|
+
@stack.push(new_tuple)
|
416
|
+
end
|
417
|
+
|
418
|
+
#
|
419
|
+
# Executes a `TUPLE3` instruction.
|
420
|
+
#
|
421
|
+
def execute_tuple3
|
422
|
+
new_tuple = Tuple.new(@stack.pop(3))
|
423
|
+
|
424
|
+
@stack.push(new_tuple)
|
425
|
+
end
|
426
|
+
|
427
|
+
#
|
428
|
+
# Executes a `DICT` instruction.
|
429
|
+
#
|
430
|
+
def execute_dict
|
431
|
+
pairs = pop_meta_stack
|
432
|
+
new_dict = {}
|
433
|
+
|
434
|
+
until pairs.empty?
|
435
|
+
key, value = pairs.pop(2)
|
436
|
+
new_dict[key] = value
|
437
|
+
end
|
438
|
+
|
439
|
+
@stack.push(new_dict)
|
440
|
+
end
|
441
|
+
|
442
|
+
#
|
443
|
+
# Implements Python's `copyreg._reconstructor` function for Python Pickle
|
444
|
+
# protocol 0 compatibility.
|
445
|
+
#
|
446
|
+
# @param [PyClass, Class] class
|
447
|
+
# The Python or Ruby class to be initialized.
|
448
|
+
#
|
449
|
+
# @param [PyClass] super_class
|
450
|
+
# The Python super-class of the class.
|
451
|
+
#
|
452
|
+
# @param [Array, nil] init_arg
|
453
|
+
# The argument(s) that will be passed to the class'es `new` method.
|
454
|
+
#
|
455
|
+
def copyreg_reconstructor(klass,super_class,init_arg)
|
456
|
+
klass.new(*init_arg)
|
457
|
+
end
|
458
|
+
|
459
|
+
#
|
460
|
+
# Resolves a constant that exists in a Python namespace.
|
461
|
+
#
|
462
|
+
# @param [String] namespace
|
463
|
+
# The namespace name.
|
464
|
+
#
|
465
|
+
# @param [String] name
|
466
|
+
# The name of the constant within the namespace.
|
467
|
+
#
|
468
|
+
# @return [Class, PyClass, Method, nil]
|
469
|
+
# The resolved class or method.
|
470
|
+
#
|
471
|
+
def resolve_constant(namespace,name)
|
472
|
+
constant = if (mod = @constants[namespace])
|
473
|
+
mod[name]
|
474
|
+
end
|
475
|
+
|
476
|
+
return constant || PyClass.new(namespace,name)
|
477
|
+
end
|
478
|
+
|
479
|
+
#
|
480
|
+
# Executes a `GLOBAL` instruction.
|
481
|
+
#
|
482
|
+
# @param [Instructions::Global] instruction
|
483
|
+
# The `GLOBAL` instruction.
|
484
|
+
#
|
485
|
+
def execute_global(instruction)
|
486
|
+
namespace = instruction.namespace
|
487
|
+
name = instruction.name
|
488
|
+
constant = resolve_constant(namespace,name)
|
489
|
+
|
490
|
+
@stack.push(constant)
|
491
|
+
end
|
492
|
+
|
493
|
+
#
|
494
|
+
# Executes a `STACK_GLOBAL` instruction.
|
495
|
+
#
|
496
|
+
def execute_stack_global
|
497
|
+
namespace, name = @stack.pop(2)
|
498
|
+
constant = resolve_constant(namespace,name)
|
499
|
+
|
500
|
+
@stack.push(constant)
|
501
|
+
end
|
502
|
+
|
503
|
+
#
|
504
|
+
# Executes a `NEWOBJ` instruction.
|
505
|
+
#
|
506
|
+
def execute_newobj
|
507
|
+
py_class, args = @stack.pop(2)
|
508
|
+
py_object = py_class.new(*args)
|
509
|
+
|
510
|
+
@stack.push(py_object)
|
511
|
+
end
|
512
|
+
|
513
|
+
#
|
514
|
+
# Executes a `NEWOBJ_EX` instruction.
|
515
|
+
#
|
516
|
+
def execute_newobj_ex
|
517
|
+
py_class, args, kwargs = @stack.pop(3)
|
518
|
+
py_object = if kwargs
|
519
|
+
kwargs = kwargs.transform_keys(&:to_sym)
|
520
|
+
|
521
|
+
py_class.new(*args,**kwargs)
|
522
|
+
else
|
523
|
+
py_class.new(*args)
|
524
|
+
end
|
525
|
+
|
526
|
+
@stack.push(py_object)
|
527
|
+
end
|
528
|
+
|
529
|
+
#
|
530
|
+
# Executes a `REDUCE` instruction.
|
531
|
+
#
|
532
|
+
def execute_reduce
|
533
|
+
callable, arg = @stack.pop(2)
|
534
|
+
object = case callable
|
535
|
+
when PyClass, Class
|
536
|
+
callable.new(*arg)
|
537
|
+
when Method
|
538
|
+
callable.call(*arg)
|
539
|
+
else
|
540
|
+
raise(DeserializationError,"cannot execute REDUCE on a non-class: #{callable.inspect}")
|
541
|
+
end
|
542
|
+
|
543
|
+
@stack.push(object)
|
544
|
+
end
|
545
|
+
|
546
|
+
#
|
547
|
+
# Executes a `BUILD` instruction.
|
548
|
+
#
|
549
|
+
def execute_build
|
550
|
+
arg = @stack.pop
|
551
|
+
object = @stack.last
|
552
|
+
|
553
|
+
if object.respond_to?(:__setstate__)
|
554
|
+
object.__setstate__(arg)
|
555
|
+
elsif object.kind_of?(Hash)
|
556
|
+
object.merge!(arg)
|
557
|
+
else
|
558
|
+
raise(DeserializationError,"cannot execute BUILD on an object that does not define a __setstate__ method or is not a Hash: #{object.inspect}")
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
#
|
563
|
+
# Executes a `SETITEM` instruction.
|
564
|
+
#
|
565
|
+
def execute_setitem
|
566
|
+
key, value = @stack.pop(2)
|
567
|
+
dict = @stack.last
|
568
|
+
|
569
|
+
unless dict.kind_of?(Hash)
|
570
|
+
raise(DeserializationError,"cannot set key (#{key.inspect}) and value (#{value.inspect}) into non-Hash: #{dict.inspect}")
|
571
|
+
end
|
572
|
+
|
573
|
+
dict[key] = value
|
574
|
+
end
|
575
|
+
|
576
|
+
#
|
577
|
+
# Executes a `SETITEMS` instruction.
|
578
|
+
#
|
579
|
+
def execute_setitems
|
580
|
+
pairs = pop_meta_stack
|
581
|
+
dict = @stack.last
|
582
|
+
|
583
|
+
unless dict.kind_of?(Hash)
|
584
|
+
raise(DeserializationError,"cannot set key value pairs (#{pairs.inspect}) into non-Hash: #{dict.inspect}")
|
585
|
+
end
|
586
|
+
|
587
|
+
until pairs.empty?
|
588
|
+
key, value = pairs.pop(2)
|
589
|
+
dict[key] = value
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Python
|
2
|
+
module Pickle
|
3
|
+
class Instruction
|
4
|
+
|
5
|
+
# The opcode name.
|
6
|
+
#
|
7
|
+
# @return [Symbol]
|
8
|
+
attr_reader :opcode
|
9
|
+
|
10
|
+
#
|
11
|
+
# Initializes the instruction.
|
12
|
+
#
|
13
|
+
# @param [Symbol] opcode
|
14
|
+
#
|
15
|
+
def initialize(opcode)
|
16
|
+
@opcode = opcode
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Compares the instruction to another instruction.
|
21
|
+
#
|
22
|
+
# @param [Instruction] other
|
23
|
+
# The other instruction to compare against.
|
24
|
+
#
|
25
|
+
# @return [Boolean]
|
26
|
+
# Indicates whether the other instruction matches this one.
|
27
|
+
#
|
28
|
+
def ==(other)
|
29
|
+
(self.class == other.class) && (@opcode == other.opcode)
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Converts the instruction into a String.
|
34
|
+
#
|
35
|
+
# @return [String]
|
36
|
+
#
|
37
|
+
def to_s
|
38
|
+
@opcode.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Inspects the instruction.
|
43
|
+
#
|
44
|
+
# @return [String]
|
45
|
+
#
|
46
|
+
def inspect
|
47
|
+
"#<#{self.class}: #{self}>"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'python/pickle/instruction'
|
2
|
+
|
3
|
+
module Python
|
4
|
+
module Pickle
|
5
|
+
module Instructions
|
6
|
+
#
|
7
|
+
# Represents a pickle `ADDITEMS` instruction.
|
8
|
+
#
|
9
|
+
# @note Introduced in protocol 4.
|
10
|
+
#
|
11
|
+
class AddItems < Instruction
|
12
|
+
|
13
|
+
#
|
14
|
+
# Initializes the `ADDITEMS` instruction.
|
15
|
+
#
|
16
|
+
def initialize
|
17
|
+
super(:ADDITEMS)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# The `ADDITEMS` instruction.
|
23
|
+
ADDITEMS = AddItems.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'python/pickle/instruction'
|
2
|
+
|
3
|
+
module Python
|
4
|
+
module Pickle
|
5
|
+
module Instructions
|
6
|
+
#
|
7
|
+
# Represents a pickle `APPEND` instruction.
|
8
|
+
#
|
9
|
+
class Append < Instruction
|
10
|
+
|
11
|
+
#
|
12
|
+
# Initializes the `APPEND` instruction.
|
13
|
+
#
|
14
|
+
def initialize
|
15
|
+
super(:APPEND)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
# The `APPEND` instruction.
|
21
|
+
APPEND = Append.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|