rb-threadframe 0.32
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.
- data/LICENSE +23 -0
- data/NEWS +2 -0
- data/README.md +9 -0
- data/Rakefile +162 -0
- data/ext/extconf.rb +12 -0
- data/ext/iseq_extra.c +404 -0
- data/ext/iseq_extra.h +8 -0
- data/ext/iseq_mini.h +41 -0
- data/ext/node.h +483 -0
- data/ext/proc_extra.c +108 -0
- data/ext/proc_extra.h +3 -0
- data/ext/thread_extra.c +84 -0
- data/ext/thread_extra.h +5 -0
- data/ext/thread_frame.c +1022 -0
- data/ext/thread_frame.h +4 -0
- data/ext/thread_pthread.h +24 -0
- data/include/method_mini.h +90 -0
- data/include/node.h +483 -0
- data/include/ruby19_externs.h +36 -0
- data/include/thread_pthread.h +24 -0
- data/include/vm_core_mini.h +357 -0
- data/lib/iseq_extra.rb +89 -0
- data/lib/thread_frame.rb +3 -0
- data/test/unit/cfunc-use.rb +11 -0
- data/test/unit/test-argc.rb +45 -0
- data/test/unit/test-binding.rb +44 -0
- data/test/unit/test-invalid.rb +40 -0
- data/test/unit/test-iseq-brkpt.rb +61 -0
- data/test/unit/test-iseq.rb +121 -0
- data/test/unit/test-lib-iseq-extra.rb +57 -0
- data/test/unit/test-prev.rb +54 -0
- data/test/unit/test-proc.rb +23 -0
- data/test/unit/test-return-stop.rb +64 -0
- data/test/unit/test-settracefunc.rb +315 -0
- data/test/unit/test-source.rb +104 -0
- data/test/unit/test-sp-size.rb +45 -0
- data/test/unit/test-thread-trace-masks.rb +90 -0
- data/test/unit/test-thread.rb +168 -0
- data/test/unit/test-trace.rb +55 -0
- data/threadframe.rd +163 -0
- metadata +110 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
/* Ruby 1.9 externs that we need.
|
2
|
+
|
3
|
+
We assume all structure typedefs needed below have been defined
|
4
|
+
previously.
|
5
|
+
|
6
|
+
Routines marked "new" are not found in an unmodified Ruby 1.9.
|
7
|
+
Routines marked remove "static" are static routines that need to be
|
8
|
+
made extern.
|
9
|
+
*/
|
10
|
+
|
11
|
+
|
12
|
+
/* From iseq */
|
13
|
+
extern VALUE iseq_alloc_shared(VALUE klass); /* new */
|
14
|
+
extern VALUE rb_cISeq;
|
15
|
+
extern const char * ruby_node_name(int node);
|
16
|
+
extern VALUE rb_iseq_arity(VALUE iseqval);
|
17
|
+
|
18
|
+
/* From proc.c */
|
19
|
+
extern int method_arity(VALUE method); /* removed "static" */
|
20
|
+
extern VALUE rb_binding_frame_new(void *vth, void *vcfp); /* new */
|
21
|
+
|
22
|
+
|
23
|
+
/* From thread.c */
|
24
|
+
extern rb_control_frame_t * thread_control_frame(void *); /* new */
|
25
|
+
extern VALUE rb_cThread; /* Thread class */
|
26
|
+
|
27
|
+
|
28
|
+
extern VALUE rb_iseq_disasm_internal(rb_iseq_t *iseqdat); /* new */
|
29
|
+
extern VALUE rb_cRubyVM; /* RubyVM class */
|
30
|
+
|
31
|
+
/* From vm.c */
|
32
|
+
extern int rb_vm_get_sourceline(const rb_control_frame_t *cfp);
|
33
|
+
extern rb_control_frame_t * rb_vm_get_ruby_level_next_cfp(rb_thread_t *th, rb_control_frame_t *cfp);
|
34
|
+
|
35
|
+
/* From node.c */
|
36
|
+
extern VALUE rb_parser_dump_tree(NODE *node, int comment);
|
@@ -0,0 +1,24 @@
|
|
1
|
+
/**********************************************************************
|
2
|
+
|
3
|
+
thread_pthread.h -
|
4
|
+
|
5
|
+
$Author$
|
6
|
+
|
7
|
+
Copyright (C) 2004-2007 Koichi Sasada
|
8
|
+
|
9
|
+
**********************************************************************/
|
10
|
+
|
11
|
+
#ifndef RUBY_THREAD_PTHREAD_H
|
12
|
+
#define RUBY_THREAD_PTHREAD_H
|
13
|
+
|
14
|
+
#include <pthread.h>
|
15
|
+
typedef pthread_t rb_thread_id_t;
|
16
|
+
typedef pthread_mutex_t rb_thread_lock_t;
|
17
|
+
typedef pthread_cond_t rb_thread_cond_t;
|
18
|
+
|
19
|
+
typedef struct native_thread_data_struct {
|
20
|
+
void *signal_thread_list;
|
21
|
+
pthread_cond_t sleep_cond;
|
22
|
+
} native_thread_data_t;
|
23
|
+
|
24
|
+
#endif /* RUBY_THREAD_PTHREAD_H */
|
@@ -0,0 +1,357 @@
|
|
1
|
+
/* Headers Exposing a little more of the 1.9 runtime and some
|
2
|
+
method prototypes for extensions to the Thread class.
|
3
|
+
*/
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <signal.h>
|
6
|
+
#include "thread_pthread.h"
|
7
|
+
#include "node.h"
|
8
|
+
|
9
|
+
/* From vm_core.h: */
|
10
|
+
|
11
|
+
/* Frame information: */
|
12
|
+
#define VM_FRAME_MAGIC_METHOD 0x11
|
13
|
+
#define VM_FRAME_MAGIC_BLOCK 0x21
|
14
|
+
#define VM_FRAME_MAGIC_CLASS 0x31
|
15
|
+
#define VM_FRAME_MAGIC_TOP 0x41
|
16
|
+
#define VM_FRAME_MAGIC_FINISH 0x51
|
17
|
+
#define VM_FRAME_MAGIC_CFUNC 0x61
|
18
|
+
#define VM_FRAME_MAGIC_PROC 0x71
|
19
|
+
#define VM_FRAME_MAGIC_IFUNC 0x81
|
20
|
+
#define VM_FRAME_MAGIC_EVAL 0x91
|
21
|
+
#define VM_FRAME_MAGIC_LAMBDA 0xa1
|
22
|
+
#define VM_FRAME_MAGIC_MASK_BITS 8
|
23
|
+
#define VM_FRAME_MAGIC_MASK (~(~0<<VM_FRAME_MAGIC_MASK_BITS))
|
24
|
+
|
25
|
+
#define VM_FRAME_TYPE(cfp) ((cfp)->flag & VM_FRAME_MAGIC_MASK)
|
26
|
+
|
27
|
+
#define VM_FRAME_TRACE_RETURN 0x01 /* Call trace hook on return. */
|
28
|
+
#define VM_FRAME_TRACE_OFF 0x02 /* Turn of event hook tracing in this frame
|
29
|
+
and any frames created from this one. */
|
30
|
+
|
31
|
+
/* other frame flag */
|
32
|
+
#define VM_FRAME_FLAG_PASSED 0x0100
|
33
|
+
|
34
|
+
#define RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp) (cfp+1)
|
35
|
+
#define RUBYVM_CFUNC_FRAME_P(cfp) \
|
36
|
+
(VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_CFUNC)
|
37
|
+
#define RUBY_VM_NEXT_CONTROL_FRAME(cfp) (cfp-1)
|
38
|
+
#define RUBY_VM_END_CONTROL_FRAME(th) \
|
39
|
+
((rb_control_frame_t *)((th)->stack + (th)->stack_size))
|
40
|
+
#define RUBY_VM_VALID_CONTROL_FRAME_P(cfp, ecfp) \
|
41
|
+
((void *)(ecfp) > (void *)(cfp))
|
42
|
+
#define RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp) \
|
43
|
+
(!RUBY_VM_VALID_CONTROL_FRAME_P((cfp), RUBY_VM_END_CONTROL_FRAME(th)))
|
44
|
+
|
45
|
+
#define RUBY_VM_IFUNC_P(ptr) (BUILTIN_TYPE(ptr) == T_NODE)
|
46
|
+
#define RUBY_VM_NORMAL_ISEQ_P(ptr) \
|
47
|
+
(ptr && !RUBY_VM_IFUNC_P(ptr))
|
48
|
+
|
49
|
+
|
50
|
+
#if 1
|
51
|
+
#define GetCoreDataFromValue(obj, type, ptr) do { \
|
52
|
+
ptr = (type*)DATA_PTR(obj); \
|
53
|
+
} while (0)
|
54
|
+
#else
|
55
|
+
#define GetCoreDataFromValue(obj, type, ptr) Data_Get_Struct(obj, type, ptr)
|
56
|
+
#endif
|
57
|
+
#if 1
|
58
|
+
#define GetCoreDataFromValue(obj, type, ptr) do { \
|
59
|
+
ptr = (type*)DATA_PTR(obj); \
|
60
|
+
} while (0)
|
61
|
+
#else
|
62
|
+
#define GetCoreDataFromValue(obj, type, ptr) Data_Get_Struct(obj, type, ptr)
|
63
|
+
#endif
|
64
|
+
#define GetISeqPtr(obj, ptr) \
|
65
|
+
GetCoreDataFromValue(obj, rb_iseq_t, ptr)
|
66
|
+
|
67
|
+
/* Opaque types (for now at least) */
|
68
|
+
typedef struct iseq_catch_table_entry iseq_catch_table_entry_t;
|
69
|
+
|
70
|
+
#ifndef NSIG
|
71
|
+
# define NSIG (_SIGMAX + 1) /* For QNX */
|
72
|
+
#endif
|
73
|
+
|
74
|
+
#define RUBY_NSIG NSIG
|
75
|
+
|
76
|
+
typedef struct rb_compile_option_struct {
|
77
|
+
int inline_const_cache;
|
78
|
+
int peephole_optimization;
|
79
|
+
int tailcall_optimization;
|
80
|
+
int specialized_instruction;
|
81
|
+
int operands_unification;
|
82
|
+
int instructions_unification;
|
83
|
+
int stack_caching;
|
84
|
+
int trace_instruction;
|
85
|
+
int debug_level;
|
86
|
+
} rb_compile_option_t;
|
87
|
+
|
88
|
+
/* Instruction sequence */
|
89
|
+
typedef struct rb_iseq_struct {
|
90
|
+
/***************/
|
91
|
+
/* static data */
|
92
|
+
/***************/
|
93
|
+
|
94
|
+
VALUE type; /* instruction sequence type */
|
95
|
+
VALUE name; /* String: iseq name */
|
96
|
+
VALUE filename; /* file information where this sequence from */
|
97
|
+
VALUE filepath; /* real file path or nil */
|
98
|
+
VALUE *iseq; /* iseq (insn number and operands) */
|
99
|
+
VALUE *iseq_encoded; /* encoded iseq */
|
100
|
+
unsigned long iseq_size;
|
101
|
+
VALUE mark_ary; /* Array: includes operands which should be GC marked */
|
102
|
+
VALUE coverage; /* coverage array */
|
103
|
+
unsigned short line_no;
|
104
|
+
|
105
|
+
/* insn info, must be freed */
|
106
|
+
struct iseq_insn_info_entry *insn_info_table;
|
107
|
+
size_t insn_info_size;
|
108
|
+
|
109
|
+
ID *local_table; /* must free */
|
110
|
+
int local_table_size;
|
111
|
+
|
112
|
+
/* method, class frame: sizeof(vars) + 1, block frame: sizeof(vars) */
|
113
|
+
int local_size;
|
114
|
+
|
115
|
+
struct iseq_inline_cache_entry *ic_entries;
|
116
|
+
int ic_size;
|
117
|
+
|
118
|
+
/**
|
119
|
+
* argument information
|
120
|
+
*
|
121
|
+
* def m(a1, a2, ..., aM, # mandatory
|
122
|
+
* b1=(...), b2=(...), ..., bN=(...), # optional
|
123
|
+
* *c, # rest
|
124
|
+
* d1, d2, ..., dO, # post
|
125
|
+
* &e) # block
|
126
|
+
* =>
|
127
|
+
*
|
128
|
+
* argc = M
|
129
|
+
* arg_rest = M+N+1 // or -1 if no rest arg
|
130
|
+
* arg_opts = N
|
131
|
+
* arg_opts_tbl = [ (N entries) ]
|
132
|
+
* arg_post_len = O // 0 if no post arguments
|
133
|
+
* arg_post_start = M+N+2
|
134
|
+
* arg_block = M+N + 1 + O + 1 // -1 if no block arg
|
135
|
+
* arg_simple = 0 if not simple arguments.
|
136
|
+
* = 1 if no opt, rest, post, block.
|
137
|
+
* = 2 if ambiguos block parameter ({|a|}).
|
138
|
+
* arg_size = argument size.
|
139
|
+
*/
|
140
|
+
|
141
|
+
int argc;
|
142
|
+
int arg_simple;
|
143
|
+
int arg_rest;
|
144
|
+
int arg_block;
|
145
|
+
int arg_opts;
|
146
|
+
int arg_post_len;
|
147
|
+
int arg_post_start;
|
148
|
+
int arg_size;
|
149
|
+
VALUE *arg_opt_table;
|
150
|
+
|
151
|
+
size_t stack_max; /* for stack overflow check */
|
152
|
+
|
153
|
+
/* catch table */
|
154
|
+
iseq_catch_table_entry_t *catch_table;
|
155
|
+
int catch_table_size;
|
156
|
+
|
157
|
+
/* for child iseq */
|
158
|
+
struct rb_iseq_struct *parent_iseq;
|
159
|
+
struct rb_iseq_struct *local_iseq;
|
160
|
+
|
161
|
+
/****************/
|
162
|
+
/* dynamic data */
|
163
|
+
/****************/
|
164
|
+
|
165
|
+
VALUE self;
|
166
|
+
VALUE orig; /* non-NULL if its data have origin */
|
167
|
+
|
168
|
+
/* block inlining */
|
169
|
+
/*
|
170
|
+
* NODE *node;
|
171
|
+
* void *special_block_builder;
|
172
|
+
* void *cached_special_block_builder;
|
173
|
+
* VALUE cached_special_block;
|
174
|
+
*/
|
175
|
+
|
176
|
+
/* klass/module nest information stack (cref) */
|
177
|
+
NODE *cref_stack;
|
178
|
+
VALUE klass;
|
179
|
+
|
180
|
+
/* misc */
|
181
|
+
ID defined_method_id; /* for define_method */
|
182
|
+
|
183
|
+
/* used at compile time */
|
184
|
+
struct iseq_compile_data *compile_data;
|
185
|
+
/* Used to set a breakpoint at a VM instruction */
|
186
|
+
unsigned char *breakpoints;
|
187
|
+
} rb_iseq_t;
|
188
|
+
|
189
|
+
enum ruby_special_exceptions {
|
190
|
+
ruby_error_reenter,
|
191
|
+
ruby_error_nomemory,
|
192
|
+
ruby_error_sysstack,
|
193
|
+
ruby_special_error_count
|
194
|
+
};
|
195
|
+
|
196
|
+
typedef struct rb_vm_struct {
|
197
|
+
VALUE self;
|
198
|
+
|
199
|
+
rb_thread_lock_t global_vm_lock;
|
200
|
+
|
201
|
+
struct rb_thread_struct *main_thread;
|
202
|
+
struct rb_thread_struct *running_thread;
|
203
|
+
|
204
|
+
st_table *living_threads;
|
205
|
+
VALUE thgroup_default;
|
206
|
+
|
207
|
+
int running;
|
208
|
+
int thread_abort_on_exception;
|
209
|
+
unsigned long trace_flag;
|
210
|
+
volatile int sleeper;
|
211
|
+
|
212
|
+
/* object management */
|
213
|
+
VALUE mark_object_ary;
|
214
|
+
|
215
|
+
VALUE special_exceptions[ruby_special_error_count];
|
216
|
+
|
217
|
+
/* load */
|
218
|
+
VALUE top_self;
|
219
|
+
VALUE load_path;
|
220
|
+
VALUE loaded_features;
|
221
|
+
struct st_table *loading_table;
|
222
|
+
|
223
|
+
/* signal */
|
224
|
+
struct {
|
225
|
+
VALUE cmd;
|
226
|
+
int safe;
|
227
|
+
} trap_list[RUBY_NSIG];
|
228
|
+
|
229
|
+
/* hook */
|
230
|
+
rb_event_hook_t *event_hooks;
|
231
|
+
|
232
|
+
int src_encoding_index;
|
233
|
+
|
234
|
+
VALUE verbose, debug, progname;
|
235
|
+
VALUE coverages;
|
236
|
+
|
237
|
+
struct unlinked_method_entry_list_entry *unlinked_method_entry_list;
|
238
|
+
|
239
|
+
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
|
240
|
+
struct rb_objspace *objspace;
|
241
|
+
#endif
|
242
|
+
} rb_vm_t;
|
243
|
+
|
244
|
+
#include "method_mini.h"
|
245
|
+
|
246
|
+
typedef struct {
|
247
|
+
VALUE *pc; /* cfp[0] */
|
248
|
+
VALUE *sp; /* cfp[1] */
|
249
|
+
VALUE *bp; /* cfp[2] */
|
250
|
+
rb_iseq_t *iseq; /* cfp[3] */
|
251
|
+
VALUE flag; /* cfp[4] */
|
252
|
+
VALUE self; /* cfp[5] / block[0] */
|
253
|
+
VALUE *lfp; /* cfp[6] / block[1] */
|
254
|
+
VALUE *dfp; /* cfp[7] / block[2] */
|
255
|
+
rb_iseq_t *block_iseq; /* cfp[8] / block[3] */
|
256
|
+
VALUE proc; /* cfp[9] / block[4] */
|
257
|
+
const rb_method_entry_t *me;/* cfp[10] */
|
258
|
+
short int tracing; /* Bits to control per-frame event tracing.
|
259
|
+
See VM_FRAME_TRACE_xxx defines.
|
260
|
+
*/
|
261
|
+
} rb_control_frame_t;
|
262
|
+
|
263
|
+
typedef struct rb_block_struct {
|
264
|
+
VALUE self; /* share with method frame if it's only block */
|
265
|
+
VALUE *lfp; /* share with method frame if it's only block */
|
266
|
+
VALUE *dfp; /* share with method frame if it's only block */
|
267
|
+
rb_iseq_t *iseq;
|
268
|
+
VALUE proc;
|
269
|
+
} rb_block_t;
|
270
|
+
|
271
|
+
#define GetThreadPtr(obj, ptr) \
|
272
|
+
GetCoreDataFromValue(obj, rb_thread_t, ptr)
|
273
|
+
|
274
|
+
#define GetProcPtr(obj, ptr) \
|
275
|
+
GetCoreDataFromValue(obj, rb_proc_t, ptr)
|
276
|
+
|
277
|
+
typedef struct rb_thread_struct
|
278
|
+
{
|
279
|
+
VALUE self;
|
280
|
+
rb_vm_t *vm;
|
281
|
+
|
282
|
+
/* execution information */
|
283
|
+
VALUE *stack; /* must free, must mark. rb: seems to be nil. */
|
284
|
+
unsigned long stack_size; /* Number of stack (or rb_control_frame_t) entries */
|
285
|
+
rb_control_frame_t *cfp;
|
286
|
+
|
287
|
+
int safe_level;
|
288
|
+
int raised_flag;
|
289
|
+
VALUE last_status; /* $? */
|
290
|
+
|
291
|
+
/* passing state */
|
292
|
+
int state;
|
293
|
+
|
294
|
+
/* tracer */
|
295
|
+
rb_event_hook_t *event_hooks;
|
296
|
+
rb_event_flag_t event_flags;
|
297
|
+
int tracing; /* 0 if not tracing. If less than 0, skip that many
|
298
|
+
C call/return pairs */
|
299
|
+
int exec_event_tracing; /* 0 if not in rb_threadptr_evec_event_hooks. */
|
300
|
+
int trace_skip_insn_count; /* # of VM instructions to skip */
|
301
|
+
|
302
|
+
/* misc */
|
303
|
+
int method_missing_reason;
|
304
|
+
int abort_on_exception;
|
305
|
+
|
306
|
+
/* for rb_iterate */
|
307
|
+
const rb_block_t *passed_block;
|
308
|
+
|
309
|
+
/* for bmethod */
|
310
|
+
const rb_method_entry_t *passed_me;
|
311
|
+
|
312
|
+
/* for load(true) */
|
313
|
+
VALUE top_self;
|
314
|
+
VALUE top_wrapper;
|
315
|
+
|
316
|
+
/* eval env */
|
317
|
+
rb_block_t *base_block;
|
318
|
+
|
319
|
+
VALUE *local_lfp;
|
320
|
+
VALUE local_svar;
|
321
|
+
|
322
|
+
/* Lot's of other stuff ...
|
323
|
+
thread control ... */
|
324
|
+
} rb_thread_t;
|
325
|
+
|
326
|
+
typedef struct {
|
327
|
+
rb_block_t block;
|
328
|
+
|
329
|
+
VALUE envval; /* for GC mark */
|
330
|
+
VALUE blockprocval;
|
331
|
+
int safe_level;
|
332
|
+
int is_from_method;
|
333
|
+
int is_lambda;
|
334
|
+
} rb_proc_t;
|
335
|
+
|
336
|
+
#define GetEnvPtr(obj, ptr) \
|
337
|
+
GetCoreDataFromValue(obj, rb_env_t, ptr)
|
338
|
+
|
339
|
+
typedef struct {
|
340
|
+
VALUE *env;
|
341
|
+
int env_size;
|
342
|
+
int local_size;
|
343
|
+
VALUE prev_envval; /* for GC mark */
|
344
|
+
rb_block_t block;
|
345
|
+
} rb_env_t;
|
346
|
+
|
347
|
+
#define GetBindingPtr(obj, ptr) \
|
348
|
+
GetCoreDataFromValue(obj, rb_binding_t, ptr)
|
349
|
+
|
350
|
+
typedef struct {
|
351
|
+
VALUE env;
|
352
|
+
VALUE filename;
|
353
|
+
unsigned short line_no;
|
354
|
+
} rb_binding_t;
|
355
|
+
|
356
|
+
#define GET_THREAD() ruby_current_thread
|
357
|
+
extern rb_thread_t *ruby_current_thread;
|
data/lib/iseq_extra.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require_relative '../ext/thread_frame'
|
3
|
+
# Some additions to RubyVM::InstructionSequence
|
4
|
+
class RubyVM::InstructionSequence
|
5
|
+
|
6
|
+
# Turns a instruction sequence type field into a string name
|
7
|
+
TYPE2STR = %w(top method block class rescue ensure eval main guard)
|
8
|
+
|
9
|
+
# Returns a String containing a list of arguments for the RubyVM::InstructionSequence
|
10
|
+
# A semicolon separates required arguments from optional ones.
|
11
|
+
# For example: for
|
12
|
+
# def evaluate(context, statements, file = __FILE__, line = __LINE__)
|
13
|
+
# we return:
|
14
|
+
# context, statements; file, line
|
15
|
+
def format_args
|
16
|
+
required_max = arity < 0 ? -arity-1 : arity
|
17
|
+
args = 0.upto(required_max-1).map do |i|
|
18
|
+
local_name(i)
|
19
|
+
end.join(', ')
|
20
|
+
|
21
|
+
last = local_table_size-1
|
22
|
+
if last >= required_max
|
23
|
+
opt_args = required_max.upto(last).map do |i|
|
24
|
+
local_name(i)
|
25
|
+
end.join(', ')
|
26
|
+
args += '; ' + opt_args
|
27
|
+
else
|
28
|
+
args = '?'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Basically hash.invert but since each offset can represent many
|
33
|
+
# lines, we have to double loop. FIXME: Is there a more efficient way?
|
34
|
+
def lineoffsets
|
35
|
+
result = {}
|
36
|
+
offsetlines.each do |offset, lines|
|
37
|
+
lines.each do |line|
|
38
|
+
result[line] ||= []
|
39
|
+
result[line] << offset
|
40
|
+
end
|
41
|
+
end
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return An array of VM instruction byte offsets (Fixnums) for a
|
46
|
+
# given line_number.
|
47
|
+
def line2offsets(line_number)
|
48
|
+
offsetlines.select do |offset, lines|
|
49
|
+
lines.member?(line_number)
|
50
|
+
end.keys
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns a cryptographic checksum (in particluar a SHA1) for the
|
54
|
+
# encoded bytes of the instruction sequence.
|
55
|
+
#
|
56
|
+
# ==== Example
|
57
|
+
#
|
58
|
+
# proc{ 5 }.iseq.sha1 => 'b361a73f9efd7dc4d2c5e86d4e94d40b36141d42'
|
59
|
+
def sha1
|
60
|
+
Digest::SHA1.hexdigest(encoded)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
if __FILE__ == $0
|
66
|
+
# Demo it.
|
67
|
+
iseq = RubyVM::ThreadFrame.current.iseq
|
68
|
+
puts iseq.format_args
|
69
|
+
puts iseq.disassemble
|
70
|
+
puts iseq.lineoffsets
|
71
|
+
puts iseq.sha1
|
72
|
+
p iseq.line2offsets(__LINE__)
|
73
|
+
p iseq.line2offsets(__LINE__+100)
|
74
|
+
|
75
|
+
def show_type # :nodoc:
|
76
|
+
tf = RubyVM::ThreadFrame.current
|
77
|
+
while tf do
|
78
|
+
is = tf.iseq
|
79
|
+
if is
|
80
|
+
ist = tf.iseq.type
|
81
|
+
isn = RubyVM::InstructionSequence::TYPE2STR[ist]
|
82
|
+
puts "instruction sequence #{is.inspect} has type #{isn} (#{ist})."
|
83
|
+
end
|
84
|
+
tf = tf.prev
|
85
|
+
end
|
86
|
+
end
|
87
|
+
show_type
|
88
|
+
end
|
89
|
+
|