node-marshal 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/COPYING +23 -23
- data/README.rdoc +56 -49
- data/bin/noderbc +63 -63
- data/bin/noderbc.bat +0 -0
- data/ext/node-marshal/base85r.c +194 -194
- data/ext/node-marshal/extconf.rb +2 -2
- data/ext/node-marshal/node193.h +321 -321
- data/ext/node-marshal/node220.h +347 -347
- data/ext/node-marshal/node230.h +361 -361
- data/ext/node-marshal/nodedump.c +2356 -2296
- data/ext/node-marshal/nodedump.h +60 -60
- data/ext/node-marshal/nodeinfo.c +619 -615
- data/lib/node-marshal.rb +227 -227
- data/test/lifegame.rb +145 -145
- data/test/test_base.rb +226 -226
- data/test/test_complex.rb +136 -136
- data/test/test_lifegame.rb +68 -68
- data/test/test_namedarg.rb +54 -0
- data/test/test_obfuscator.rb +36 -36
- data/test/test_qcall.rb +52 -52
- data/test/tinytet.rb +79 -79
- metadata +4 -3
data/ext/node-marshal/nodedump.c
CHANGED
@@ -1,2296 +1,2356 @@
|
|
1
|
-
/*
|
2
|
-
* This file contains implementation of classes for Ruby nodes
|
3
|
-
* marshalization (i.e. loading and saving them from disk)
|
4
|
-
*
|
5
|
-
* (C) 2015-
|
6
|
-
* License: BSD-2-Clause
|
7
|
-
*/
|
8
|
-
#define __STDC_FORMAT_MACROS
|
9
|
-
#include <stdio.h>
|
10
|
-
#include <stdlib.h>
|
11
|
-
#include <inttypes.h>
|
12
|
-
#include <ruby.h>
|
13
|
-
#include <ruby/version.h>
|
14
|
-
|
15
|
-
/*
|
16
|
-
* Some global variables
|
17
|
-
*/
|
18
|
-
static VALUE cNodeObjAddresses, cNodeInfo;
|
19
|
-
|
20
|
-
/*
|
21
|
-
* Part 1. .H files: nodedump functions + parts of Ruby internals
|
22
|
-
*/
|
23
|
-
#include "nodedump.h"
|
24
|
-
|
25
|
-
#ifdef WITH_CUSTOM_RB_GLOBAL_ENTRY
|
26
|
-
/* Custom (and slow) implementation of rb_global_entry internal API for Ruby 2.3
|
27
|
-
(original rb_global_entry API was opened before Ruby 2.3)
|
28
|
-
It uses a hack with the node creation. The main idea of the hack is
|
29
|
-
to create a node from the expression containing only a name of the global variable
|
30
|
-
and extract global entry address from NODE_GVAR u3 "leaf" */
|
31
|
-
static struct rb_global_entry *rb_global_entry(ID id)
|
32
|
-
{
|
33
|
-
NODE *node, *gvar_node;
|
34
|
-
struct rb_global_entry *gentry;
|
35
|
-
/* a) Step 1: create node from the expression consisting only from
|
36
|
-
our global variable */
|
37
|
-
node = rb_compile_string("<compiled>", rb_id2str(id), NUM2INT(1));
|
38
|
-
if (nd_type(node) != NODE_SCOPE)
|
39
|
-
{
|
40
|
-
return NULL;
|
41
|
-
}
|
42
|
-
/* b) Trace the node to the NODE_GVAR */
|
43
|
-
gvar_node = node->u2.node;
|
44
|
-
if (nd_type(gvar_node) == NODE_PRELUDE) /* Present only in 2.3 */
|
45
|
-
{
|
46
|
-
gvar_node = gvar_node->u2.node;
|
47
|
-
}
|
48
|
-
if (nd_type(gvar_node) != NODE_GVAR) /* Error: no GVAR found */
|
49
|
-
{
|
50
|
-
return NULL;
|
51
|
-
}
|
52
|
-
/* c) Get the global entry address and return its address */
|
53
|
-
gentry = gvar_node->u3.entry;
|
54
|
-
return gentry;
|
55
|
-
}
|
56
|
-
#endif
|
57
|
-
|
58
|
-
|
59
|
-
/*
|
60
|
-
* Part 2. Information about the nodes
|
61
|
-
*
|
62
|
-
*/
|
63
|
-
|
64
|
-
// Pre-2.0 Ruby versions don't use this version
|
65
|
-
#if RUBY_API_VERSION_MAJOR == 2
|
66
|
-
#define USE_RB_ARGS_INFO 1
|
67
|
-
#endif
|
68
|
-
|
69
|
-
#if RUBY_API_VERSION_MAJOR == 1
|
70
|
-
#define RESET_GC_FLAGS 1
|
71
|
-
#endif
|
72
|
-
|
73
|
-
|
74
|
-
// Some generic utilities
|
75
|
-
int is_value_in_heap(VALUE val)
|
76
|
-
{
|
77
|
-
if (val == Qfalse || val == Qtrue ||
|
78
|
-
val == Qnil || val == Qundef ||
|
79
|
-
(val & FIXNUM_FLAG)
|
80
|
-
#ifdef FLONUM_MASK
|
81
|
-
|| ((val & FLONUM_MASK) == FLONUM_FLAG) // This memory trick with floats is present only in 2.x
|
82
|
-
#endif
|
83
|
-
)
|
84
|
-
{
|
85
|
-
return 0;
|
86
|
-
}
|
87
|
-
else
|
88
|
-
return 1;
|
89
|
-
}
|
90
|
-
|
91
|
-
|
92
|
-
/*
|
93
|
-
* Converts Ruby string with hexadecimal number
|
94
|
-
* to the Ruby VALUE
|
95
|
-
*/
|
96
|
-
VALUE str_to_value(VALUE str)
|
97
|
-
{
|
98
|
-
intptr_t ans = (intptr_t) Qnil;
|
99
|
-
sscanf(RSTRING_PTR(str), "%"PRIxPTR, &ans);
|
100
|
-
return (VALUE) ans;
|
101
|
-
}
|
102
|
-
|
103
|
-
|
104
|
-
/*
|
105
|
-
* Converts Ruby VALUE (i.e. machine address) to the
|
106
|
-
* hexadecimal Ruby string
|
107
|
-
*/
|
108
|
-
VALUE value_to_str(VALUE val)
|
109
|
-
{
|
110
|
-
char str[16];
|
111
|
-
sprintf(str, "%" PRIxPTR, (intptr_t) val);
|
112
|
-
return rb_str_new2(str);
|
113
|
-
}
|
114
|
-
|
115
|
-
/*
|
116
|
-
* Converts VALUE to the sequence of bytes using big-endian
|
117
|
-
* standard. Returns number of non-zero bytes
|
118
|
-
*
|
119
|
-
* Inputs
|
120
|
-
* val -- input value
|
121
|
-
* buf -- pointer to the output buffer
|
122
|
-
* Returns
|
123
|
-
* number of written bytes
|
124
|
-
*/
|
125
|
-
int value_to_bin(VALUE val, unsigned char *buf)
|
126
|
-
{
|
127
|
-
int i, len = 0;
|
128
|
-
unsigned char byte;
|
129
|
-
for (i = sizeof(VALUE) - 1; i >= 0; i--)
|
130
|
-
{
|
131
|
-
byte = (unsigned char) ((val >> (i * 8)) & 0xFF);
|
132
|
-
if (len > 0 || byte != 0)
|
133
|
-
{
|
134
|
-
*buf++ = byte;
|
135
|
-
len++;
|
136
|
-
}
|
137
|
-
}
|
138
|
-
return len;
|
139
|
-
}
|
140
|
-
|
141
|
-
/*
|
142
|
-
* Converts sequence of bytes (big-endian standard) to the VALUE.
|
143
|
-
*
|
144
|
-
* Inputs
|
145
|
-
* buf -- poiner to the input buffer
|
146
|
-
* len -- number of bytes
|
147
|
-
* Returns
|
148
|
-
* VALUE
|
149
|
-
*/
|
150
|
-
VALUE bin_to_value(unsigned char *buf, int len)
|
151
|
-
{
|
152
|
-
VALUE val = (VALUE) 0;
|
153
|
-
int i;
|
154
|
-
for (i = len - 1; i >= 0; i--)
|
155
|
-
val |= ((VALUE) *buf++) << (i * 8);
|
156
|
-
return val;
|
157
|
-
}
|
158
|
-
|
159
|
-
#define NODES_CTBL_SIZE 256
|
160
|
-
static int nodes_ctbl[NODES_CTBL_SIZE * 3];
|
161
|
-
|
162
|
-
|
163
|
-
/*
|
164
|
-
* Part 3. Functions for node marshalization
|
165
|
-
*/
|
166
|
-
|
167
|
-
/*
|
168
|
-
* Keeps the information about node elements position
|
169
|
-
* in the memory and its IDs/ordinals for export to the file
|
170
|
-
*/
|
171
|
-
typedef struct {
|
172
|
-
VALUE vals; // values: key=>val Hash
|
173
|
-
VALUE ids; // identifiers: key=>id Hash
|
174
|
-
VALUE pos; // free identifier
|
175
|
-
} LeafTableInfo;
|
176
|
-
|
177
|
-
void LeafTableInfo_init(LeafTableInfo *lti)
|
178
|
-
{
|
179
|
-
lti->vals = rb_hash_new();
|
180
|
-
lti->ids = rb_hash_new();
|
181
|
-
lti->pos = 0;
|
182
|
-
}
|
183
|
-
|
184
|
-
void LeafTableInfo_mark(LeafTableInfo *lti)
|
185
|
-
{
|
186
|
-
rb_gc_mark(lti->vals);
|
187
|
-
rb_gc_mark(lti->ids);
|
188
|
-
}
|
189
|
-
|
190
|
-
|
191
|
-
int LeafTableInfo_addEntry(LeafTableInfo *lti, VALUE key, VALUE value)
|
192
|
-
{
|
193
|
-
VALUE v_id = rb_hash_aref(lti->ids, key);
|
194
|
-
if (v_id == Qnil)
|
195
|
-
{
|
196
|
-
int id = lti->pos++;
|
197
|
-
rb_hash_aset(lti->vals, key, value);
|
198
|
-
rb_hash_aset(lti->ids, key, INT2FIX(id));
|
199
|
-
return id;
|
200
|
-
}
|
201
|
-
else
|
202
|
-
{
|
203
|
-
return FIX2INT(v_id);
|
204
|
-
}
|
205
|
-
}
|
206
|
-
|
207
|
-
/*
|
208
|
-
* Adds Ruby ID data type as the entry to the LeafTableInfo struct.
|
209
|
-
* Main features:
|
210
|
-
* 1) ID will be converted to Fixnum
|
211
|
-
* 2) If ID can be converted to string by rb_id2str it will be saved as
|
212
|
-
String object. Otherwise it will be converted to Fixnum.
|
213
|
-
*/
|
214
|
-
int LeafTableInfo_addIDEntry(LeafTableInfo *lti, ID id)
|
215
|
-
{
|
216
|
-
VALUE r_idval = rb_id2str(id);
|
217
|
-
if (TYPE(r_idval) != T_STRING)
|
218
|
-
{
|
219
|
-
r_idval = INT2FIX(id);
|
220
|
-
}
|
221
|
-
return LeafTableInfo_addEntry(lti, INT2FIX(id), r_idval);
|
222
|
-
}
|
223
|
-
|
224
|
-
VALUE LeafTableInfo_getLeavesTable(LeafTableInfo *lti)
|
225
|
-
{
|
226
|
-
VALUE key, keys = rb_funcall(lti->vals, rb_intern("keys"), 0);
|
227
|
-
unsigned int i;
|
228
|
-
VALUE val;
|
229
|
-
for (i = 0; i < lti->pos; i++)
|
230
|
-
{
|
231
|
-
key = RARRAY_PTR(keys)[i];
|
232
|
-
val = rb_hash_aref(lti->vals, key);
|
233
|
-
rb_ary_store(keys, i, val);
|
234
|
-
}
|
235
|
-
return keys;
|
236
|
-
}
|
237
|
-
|
238
|
-
int LeafTableInfo_keyToID(LeafTableInfo *lti, VALUE key)
|
239
|
-
{
|
240
|
-
VALUE id = rb_hash_aref(lti->ids, key);
|
241
|
-
return (id == Qnil) ? -1 : FIX2INT(id);
|
242
|
-
}
|
243
|
-
|
244
|
-
VALUE LeafTableInfo_keyToValue(LeafTableInfo *lti, VALUE key)
|
245
|
-
{
|
246
|
-
return rb_hash_aref(lti->vals, key);
|
247
|
-
}
|
248
|
-
|
249
|
-
/* The structure keeps information about the node
|
250
|
-
that is required for its dumping to the file
|
251
|
-
(mainly hashes with relocatable identifiers) */
|
252
|
-
typedef struct {
|
253
|
-
LeafTableInfo syms; // Node symbols
|
254
|
-
LeafTableInfo lits; // Node literals
|
255
|
-
LeafTableInfo idtabs; // Table of identifiers
|
256
|
-
#ifdef USE_RB_ARGS_INFO
|
257
|
-
LeafTableInfo args; // Table of arguments
|
258
|
-
#endif
|
259
|
-
LeafTableInfo gentries; // Global variables table
|
260
|
-
LeafTableInfo nodes; // Table of nodes
|
261
|
-
LeafTableInfo pnodes; // Table of parent nodes
|
262
|
-
} NODEInfo;
|
263
|
-
|
264
|
-
void NODEInfo_init(NODEInfo *info)
|
265
|
-
{
|
266
|
-
LeafTableInfo_init(&(info->syms));
|
267
|
-
LeafTableInfo_init(&(info->lits));
|
268
|
-
LeafTableInfo_init(&(info->idtabs));
|
269
|
-
#ifdef USE_RB_ARGS_INFO
|
270
|
-
LeafTableInfo_init(&(info->args));
|
271
|
-
#endif
|
272
|
-
LeafTableInfo_init(&(info->gentries));
|
273
|
-
LeafTableInfo_init(&(info->nodes));
|
274
|
-
LeafTableInfo_init(&(info->pnodes));
|
275
|
-
}
|
276
|
-
|
277
|
-
void NODEInfo_mark(NODEInfo *info)
|
278
|
-
{
|
279
|
-
LeafTableInfo_mark(&(info->syms));
|
280
|
-
LeafTableInfo_mark(&(info->lits));
|
281
|
-
LeafTableInfo_mark(&(info->idtabs));
|
282
|
-
#ifdef USE_RB_ARGS_INFO
|
283
|
-
LeafTableInfo_mark(&(info->args));
|
284
|
-
#endif
|
285
|
-
LeafTableInfo_mark(&(info->gentries));
|
286
|
-
LeafTableInfo_mark(&(info->nodes));
|
287
|
-
LeafTableInfo_mark(&(info->pnodes));
|
288
|
-
}
|
289
|
-
|
290
|
-
void NODEInfo_free(NODEInfo *info)
|
291
|
-
{
|
292
|
-
xfree(info);
|
293
|
-
}
|
294
|
-
|
295
|
-
LeafTableInfo *NODEInfo_getTableByID(NODEInfo *info, int id)
|
296
|
-
{
|
297
|
-
switch (id)
|
298
|
-
{
|
299
|
-
case NT_ID:
|
300
|
-
return &info->syms;
|
301
|
-
case NT_VALUE:
|
302
|
-
return &info->lits;
|
303
|
-
case NT_IDTABLE:
|
304
|
-
return &info->idtabs;
|
305
|
-
#ifdef USE_RB_ARGS_INFO
|
306
|
-
case NT_ARGS:
|
307
|
-
return &info->args;
|
308
|
-
#endif
|
309
|
-
case NT_ENTRY:
|
310
|
-
return &info->gentries;
|
311
|
-
case NT_NODE:
|
312
|
-
return &info->nodes;
|
313
|
-
default:
|
314
|
-
return NULL;
|
315
|
-
}
|
316
|
-
}
|
317
|
-
|
318
|
-
/*
|
319
|
-
* Converts node value to the binary data
|
320
|
-
* Input parameters:
|
321
|
-
* info -- current NODEInfo structure
|
322
|
-
* node -- parent node (that contains the value)
|
323
|
-
* ptr -- pointer to the output memory buffer
|
324
|
-
* type -- type of the entry (NT_...)
|
325
|
-
* value -- node->u?.value VALUE
|
326
|
-
* child_id -- child node number (1,2,3)
|
327
|
-
* Returns:
|
328
|
-
* Byte that contains the next information
|
329
|
-
* a) upper half-byte: VL_... data type (for node loader)
|
330
|
-
* b) lower half-byte: number of bytes written to the buffer
|
331
|
-
*/
|
332
|
-
#define DUMP_RAW_VALUE(vl_ans, vl) (vl_ans | (value_to_bin(vl, (unsigned char *) ptr) << 4))
|
333
|
-
static int dump_node_value(NODEInfo *info, char *ptr, NODE *node, int type, VALUE value, int child_id)
|
334
|
-
{
|
335
|
-
if (type == NT_NULL || type == NT_LONG)
|
336
|
-
{
|
337
|
-
return DUMP_RAW_VALUE(VL_RAW, value);
|
338
|
-
}
|
339
|
-
else if (type == NT_NODE)
|
340
|
-
{
|
341
|
-
if (value == 0)
|
342
|
-
{ // Variant a: empty node
|
343
|
-
return DUMP_RAW_VALUE(VL_RAW, value);
|
344
|
-
}
|
345
|
-
else if (nd_type(node) == NODE_ATTRASGN && value == 1 && child_id == 1)
|
346
|
-
{ // Special case: "self"
|
347
|
-
return DUMP_RAW_VALUE(VL_RAW, value);
|
348
|
-
}
|
349
|
-
else if (TYPE(value) != T_NODE)
|
350
|
-
{
|
351
|
-
rb_raise(rb_eArgError, "dump_node_value, parent node %s (ADR 0x%s): child node %d (ADR 0x%s): is not a node\n"
|
352
|
-
" Type: %s (%d), Value: %s",
|
353
|
-
ruby_node_name(nd_type(node)), RSTRING_PTR(value_to_str((VALUE) node)),
|
354
|
-
child_id, RSTRING_PTR(value_to_str(value)),
|
355
|
-
RSTRING_PTR(rb_funcall(rb_funcall(value, rb_intern("class"), 0), rb_intern("to_s"), 0)),
|
356
|
-
TYPE(value),
|
357
|
-
RSTRING_PTR(rb_funcall(value, rb_intern("to_s"), 0)) );
|
358
|
-
}
|
359
|
-
else
|
360
|
-
{ // Variant b: not empty node
|
361
|
-
VALUE id = LeafTableInfo_keyToID(&info->nodes, value_to_str(value));
|
362
|
-
if (id == (VALUE) -1)
|
363
|
-
{
|
364
|
-
rb_raise(rb_eArgError, "dump_node_value, parent node %s (ADR 0x%s): child node %d (ADR 0x%s) not found",
|
365
|
-
ruby_node_name(nd_type(node)), RSTRING_PTR(value_to_str((VALUE) node)),
|
366
|
-
child_id, RSTRING_PTR(value_to_str(value)));
|
367
|
-
return VL_RAW;
|
368
|
-
}
|
369
|
-
else
|
370
|
-
{
|
371
|
-
return DUMP_RAW_VALUE(VL_NODE, id);
|
372
|
-
}
|
373
|
-
return VL_NODE;
|
374
|
-
}
|
375
|
-
}
|
376
|
-
else if (type == NT_VALUE)
|
377
|
-
{
|
378
|
-
if (!is_value_in_heap(value))
|
379
|
-
{ // a) value that is inside VALUE
|
380
|
-
return DUMP_RAW_VALUE(VL_RAW, value);
|
381
|
-
}
|
382
|
-
else
|
383
|
-
{ // b) value that requires reference to literals table
|
384
|
-
VALUE id = LeafTableInfo_keyToID(&info->lits, value_to_str(value));
|
385
|
-
if (id == (VALUE) -1)
|
386
|
-
rb_raise(rb_eArgError, "Cannot find literal");
|
387
|
-
else
|
388
|
-
return DUMP_RAW_VALUE(VL_LIT, id);
|
389
|
-
}
|
390
|
-
}
|
391
|
-
else if (type == NT_ID)
|
392
|
-
{
|
393
|
-
ID sym = (VALUE) value; // We are working with RAW data from RAM!
|
394
|
-
VALUE id = LeafTableInfo_keyToID(&info->syms, INT2FIX(sym));
|
395
|
-
if (id == (VALUE) -1)
|
396
|
-
{
|
397
|
-
rb_raise(rb_eArgError, "Cannot find symbol ID %d (%s) (parent node %s, line %d)",
|
398
|
-
(int) sym, RSTRING_PTR(rb_id2str(ID2SYM(sym))),
|
399
|
-
ruby_node_name(nd_type(node)), nd_line(node));
|
400
|
-
return VL_RAW;
|
401
|
-
}
|
402
|
-
else
|
403
|
-
{
|
404
|
-
return DUMP_RAW_VALUE(VL_ID, id);
|
405
|
-
}
|
406
|
-
}
|
407
|
-
else if (type == NT_ENTRY || type == NT_ARGS || type == NT_IDTABLE)
|
408
|
-
{
|
409
|
-
VALUE key = value_to_str(value);
|
410
|
-
LeafTableInfo *lti = NODEInfo_getTableByID(info, type);
|
411
|
-
VALUE id = LeafTableInfo_keyToID(lti, key);
|
412
|
-
if (id == (VALUE) -1)
|
413
|
-
{
|
414
|
-
rb_raise(rb_eArgError, "Cannot find some entry");
|
415
|
-
return VL_RAW;
|
416
|
-
}
|
417
|
-
else
|
418
|
-
{
|
419
|
-
switch(type)
|
420
|
-
{
|
421
|
-
case NT_ENTRY: return DUMP_RAW_VALUE(VL_GVAR, id);
|
422
|
-
case NT_IDTABLE: return DUMP_RAW_VALUE(VL_IDTABLE, id);
|
423
|
-
case NT_ARGS: return DUMP_RAW_VALUE(VL_ARGS, id);
|
424
|
-
default: rb_raise(rb_eArgError, "Internal error");
|
425
|
-
}
|
426
|
-
}
|
427
|
-
}
|
428
|
-
else
|
429
|
-
{
|
430
|
-
rb_raise(rb_eArgError, "Unknown child node type %d", type);
|
431
|
-
}
|
432
|
-
}
|
433
|
-
|
434
|
-
/*
|
435
|
-
* Converts information about nodes to the binary string.
|
436
|
-
* It uses dump_node_value function for the low-level conversion
|
437
|
-
* of node "leaves" to the actual binary data.
|
438
|
-
*
|
439
|
-
* See load_nodes_from_str for the descrpition of the binary string format.
|
440
|
-
*/
|
441
|
-
static VALUE dump_nodes(NODEInfo *info)
|
442
|
-
{
|
443
|
-
int node_size = sizeof(int) + sizeof(VALUE) * 4;
|
444
|
-
int i, nt, flags_len;
|
445
|
-
NODE *node;
|
446
|
-
char *bin, *ptr, *rtypes;
|
447
|
-
VALUE nodes_ary = rb_funcall(info->nodes.vals, rb_intern("keys"), 0);
|
448
|
-
VALUE nodes_bin = rb_str_new(NULL, RARRAY_LEN(nodes_ary) * node_size);
|
449
|
-
VALUE ut[3];
|
450
|
-
bin = RSTRING_PTR(nodes_bin);
|
451
|
-
|
452
|
-
for (i = 0, ptr = bin; i < RARRAY_LEN(nodes_ary); i++)
|
453
|
-
{
|
454
|
-
node = RNODE(str_to_value(RARRAY_PTR(nodes_ary)[i]));
|
455
|
-
nt = nd_type(node);
|
456
|
-
rtypes = (char *) ptr; ptr += sizeof(int);
|
457
|
-
flags_len = value_to_bin(node->flags >> 5, (unsigned char *) ptr); ptr += flags_len;
|
458
|
-
|
459
|
-
ut[0] = nodes_ctbl[nt * 3];
|
460
|
-
ut[1] = nodes_ctbl[nt * 3 + 1];
|
461
|
-
ut[2] = nodes_ctbl[nt * 3 + 2];
|
462
|
-
if (nt ==
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
*
|
490
|
-
* contains
|
491
|
-
*
|
492
|
-
*
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
ptr
|
521
|
-
|
522
|
-
ptr
|
523
|
-
rtypes[
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
}
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
*
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
VALUE
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
rb_hash_aset(ans, ID2SYM(rb_intern("
|
548
|
-
|
549
|
-
|
550
|
-
rb_hash_aset(ans, ID2SYM(rb_intern("
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
//
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
eptr[ind] = INT2FIX(
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
/* Some Ruby 1.9.3
|
703
|
-
if (nd_type(node) ==
|
704
|
-
{
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
varg =
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
rb_ary_push(varg,
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
uref[
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
void
|
1087
|
-
{
|
1088
|
-
VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("
|
1089
|
-
int i;
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
relocs->
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
{
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
ord
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
*
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
*
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
//
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
//
|
1336
|
-
|
1337
|
-
|
1338
|
-
flags =
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
}
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
rb_raise(rb_eArgError, "
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
/*
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
{
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
*
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
*/
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
|
1687
|
-
|
1688
|
-
|
1689
|
-
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
{
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
}
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
-
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
*
|
1762
|
-
*
|
1763
|
-
*
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
1802
|
-
|
1803
|
-
/*
|
1804
|
-
* call-seq:
|
1805
|
-
* obj.
|
1806
|
-
*
|
1807
|
-
*
|
1808
|
-
*
|
1809
|
-
|
1810
|
-
|
1811
|
-
|
1812
|
-
*
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
* -
|
1818
|
-
*
|
1819
|
-
*
|
1820
|
-
*
|
1821
|
-
*
|
1822
|
-
*
|
1823
|
-
*
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
|
1828
|
-
*
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
* -
|
1836
|
-
*
|
1837
|
-
*
|
1838
|
-
*
|
1839
|
-
|
1840
|
-
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
|
1863
|
-
|
1864
|
-
|
1865
|
-
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
|
1870
|
-
|
1871
|
-
|
1872
|
-
|
1873
|
-
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
|
1917
|
-
|
1918
|
-
|
1919
|
-
|
1920
|
-
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1925
|
-
|
1926
|
-
|
1927
|
-
|
1928
|
-
|
1929
|
-
|
1930
|
-
|
1931
|
-
|
1932
|
-
|
1933
|
-
|
1934
|
-
|
1935
|
-
|
1936
|
-
|
1937
|
-
|
1938
|
-
|
1939
|
-
|
1940
|
-
|
1941
|
-
|
1942
|
-
|
1943
|
-
|
1944
|
-
|
1945
|
-
|
1946
|
-
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
|
1951
|
-
|
1952
|
-
|
1953
|
-
|
1954
|
-
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1958
|
-
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
|
1965
|
-
{
|
1966
|
-
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
rb_ary_push(
|
1977
|
-
|
1978
|
-
|
1979
|
-
|
1980
|
-
|
1981
|
-
}
|
1982
|
-
else if (ut[i] ==
|
1983
|
-
{
|
1984
|
-
|
1985
|
-
|
1986
|
-
|
1987
|
-
|
1988
|
-
|
1989
|
-
|
1990
|
-
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
|
1997
|
-
|
1998
|
-
|
1999
|
-
|
2000
|
-
|
2001
|
-
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
|
2009
|
-
|
2010
|
-
|
2011
|
-
|
2012
|
-
|
2013
|
-
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
2017
|
-
|
2018
|
-
|
2019
|
-
|
2020
|
-
|
2021
|
-
|
2022
|
-
|
2023
|
-
|
2024
|
-
|
2025
|
-
|
2026
|
-
|
2027
|
-
|
2028
|
-
|
2029
|
-
|
2030
|
-
|
2031
|
-
|
2032
|
-
|
2033
|
-
|
2034
|
-
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
|
2042
|
-
|
2043
|
-
|
2044
|
-
|
2045
|
-
|
2046
|
-
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
|
2054
|
-
|
2055
|
-
|
2056
|
-
|
2057
|
-
|
2058
|
-
|
2059
|
-
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
2091
|
-
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
2095
|
-
|
2096
|
-
|
2097
|
-
|
2098
|
-
|
2099
|
-
|
2100
|
-
|
2101
|
-
|
2102
|
-
|
2103
|
-
|
2104
|
-
|
2105
|
-
|
2106
|
-
|
2107
|
-
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2113
|
-
|
2114
|
-
|
2115
|
-
|
2116
|
-
|
2117
|
-
|
2118
|
-
|
2119
|
-
|
2120
|
-
|
2121
|
-
|
2122
|
-
|
2123
|
-
|
2124
|
-
|
2125
|
-
|
2126
|
-
|
2127
|
-
|
2128
|
-
|
2129
|
-
|
2130
|
-
|
2131
|
-
|
2132
|
-
|
2133
|
-
|
2134
|
-
|
2135
|
-
|
2136
|
-
|
2137
|
-
|
2138
|
-
|
2139
|
-
|
2140
|
-
|
2141
|
-
|
2142
|
-
|
2143
|
-
|
2144
|
-
|
2145
|
-
|
2146
|
-
|
2147
|
-
|
2148
|
-
|
2149
|
-
|
2150
|
-
|
2151
|
-
|
2152
|
-
|
2153
|
-
|
2154
|
-
|
2155
|
-
|
2156
|
-
|
2157
|
-
|
2158
|
-
|
2159
|
-
|
2160
|
-
|
2161
|
-
|
2162
|
-
{
|
2163
|
-
|
2164
|
-
}
|
2165
|
-
|
2166
|
-
|
2167
|
-
|
2168
|
-
|
2169
|
-
|
2170
|
-
|
2171
|
-
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
|
2177
|
-
|
2178
|
-
|
2179
|
-
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2184
|
-
|
2185
|
-
|
2186
|
-
|
2187
|
-
|
2188
|
-
|
2189
|
-
|
2190
|
-
|
2191
|
-
|
2192
|
-
|
2193
|
-
|
2194
|
-
|
2195
|
-
|
2196
|
-
|
2197
|
-
|
2198
|
-
|
2199
|
-
|
2200
|
-
|
2201
|
-
|
2202
|
-
*
|
2203
|
-
*
|
2204
|
-
|
2205
|
-
|
2206
|
-
|
2207
|
-
|
2208
|
-
|
2209
|
-
|
2210
|
-
|
2211
|
-
|
2212
|
-
*
|
2213
|
-
|
2214
|
-
|
2215
|
-
|
2216
|
-
|
2217
|
-
|
2218
|
-
|
2219
|
-
|
2220
|
-
|
2221
|
-
|
2222
|
-
|
2223
|
-
|
2224
|
-
|
2225
|
-
|
2226
|
-
|
2227
|
-
|
2228
|
-
|
2229
|
-
*
|
2230
|
-
*
|
2231
|
-
*/
|
2232
|
-
static VALUE
|
2233
|
-
{
|
2234
|
-
|
2235
|
-
|
2236
|
-
|
2237
|
-
|
2238
|
-
|
2239
|
-
*
|
2240
|
-
|
2241
|
-
|
2242
|
-
|
2243
|
-
|
2244
|
-
|
2245
|
-
|
2246
|
-
|
2247
|
-
|
2248
|
-
|
2249
|
-
|
2250
|
-
|
2251
|
-
|
2252
|
-
|
2253
|
-
|
2254
|
-
|
2255
|
-
|
2256
|
-
|
2257
|
-
|
2258
|
-
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2266
|
-
|
2267
|
-
|
2268
|
-
|
2269
|
-
|
2270
|
-
|
2271
|
-
|
2272
|
-
|
2273
|
-
|
2274
|
-
|
2275
|
-
|
2276
|
-
|
2277
|
-
|
2278
|
-
|
2279
|
-
|
2280
|
-
|
2281
|
-
|
2282
|
-
|
2283
|
-
|
2284
|
-
|
2285
|
-
|
2286
|
-
|
2287
|
-
|
2288
|
-
|
2289
|
-
|
2290
|
-
|
2291
|
-
|
2292
|
-
|
2293
|
-
|
2294
|
-
|
2295
|
-
|
2296
|
-
}
|
1
|
+
/*
|
2
|
+
* This file contains implementation of classes for Ruby nodes
|
3
|
+
* marshalization (i.e. loading and saving them from disk)
|
4
|
+
*
|
5
|
+
* (C) 2015-2017 Alexey Voskov
|
6
|
+
* License: BSD-2-Clause
|
7
|
+
*/
|
8
|
+
#define __STDC_FORMAT_MACROS
|
9
|
+
#include <stdio.h>
|
10
|
+
#include <stdlib.h>
|
11
|
+
#include <inttypes.h>
|
12
|
+
#include <ruby.h>
|
13
|
+
#include <ruby/version.h>
|
14
|
+
|
15
|
+
/*
|
16
|
+
* Some global variables
|
17
|
+
*/
|
18
|
+
static VALUE cNodeObjAddresses, cNodeInfo;
|
19
|
+
|
20
|
+
/*
|
21
|
+
* Part 1. .H files: nodedump functions + parts of Ruby internals
|
22
|
+
*/
|
23
|
+
#include "nodedump.h"
|
24
|
+
|
25
|
+
#ifdef WITH_CUSTOM_RB_GLOBAL_ENTRY
|
26
|
+
/* Custom (and slow) implementation of rb_global_entry internal API for Ruby 2.3
|
27
|
+
(original rb_global_entry API was opened before Ruby 2.3)
|
28
|
+
It uses a hack with the node creation. The main idea of the hack is
|
29
|
+
to create a node from the expression containing only a name of the global variable
|
30
|
+
and extract global entry address from NODE_GVAR u3 "leaf" */
|
31
|
+
static struct rb_global_entry *rb_global_entry(ID id)
|
32
|
+
{
|
33
|
+
NODE *node, *gvar_node;
|
34
|
+
struct rb_global_entry *gentry;
|
35
|
+
/* a) Step 1: create node from the expression consisting only from
|
36
|
+
our global variable */
|
37
|
+
node = rb_compile_string("<compiled>", rb_id2str(id), NUM2INT(1));
|
38
|
+
if (nd_type(node) != NODE_SCOPE)
|
39
|
+
{
|
40
|
+
return NULL;
|
41
|
+
}
|
42
|
+
/* b) Trace the node to the NODE_GVAR */
|
43
|
+
gvar_node = node->u2.node;
|
44
|
+
if (nd_type(gvar_node) == NODE_PRELUDE) /* Present only in 2.3 */
|
45
|
+
{
|
46
|
+
gvar_node = gvar_node->u2.node;
|
47
|
+
}
|
48
|
+
if (nd_type(gvar_node) != NODE_GVAR) /* Error: no GVAR found */
|
49
|
+
{
|
50
|
+
return NULL;
|
51
|
+
}
|
52
|
+
/* c) Get the global entry address and return its address */
|
53
|
+
gentry = gvar_node->u3.entry;
|
54
|
+
return gentry;
|
55
|
+
}
|
56
|
+
#endif
|
57
|
+
|
58
|
+
|
59
|
+
/*
|
60
|
+
* Part 2. Information about the nodes
|
61
|
+
*
|
62
|
+
*/
|
63
|
+
|
64
|
+
// Pre-2.0 Ruby versions don't use this version
|
65
|
+
#if RUBY_API_VERSION_MAJOR == 2
|
66
|
+
#define USE_RB_ARGS_INFO 1
|
67
|
+
#endif
|
68
|
+
|
69
|
+
#if RUBY_API_VERSION_MAJOR == 1
|
70
|
+
#define RESET_GC_FLAGS 1
|
71
|
+
#endif
|
72
|
+
|
73
|
+
|
74
|
+
// Some generic utilities
|
75
|
+
int is_value_in_heap(VALUE val)
|
76
|
+
{
|
77
|
+
if (val == Qfalse || val == Qtrue ||
|
78
|
+
val == Qnil || val == Qundef ||
|
79
|
+
(val & FIXNUM_FLAG)
|
80
|
+
#ifdef FLONUM_MASK
|
81
|
+
|| ((val & FLONUM_MASK) == FLONUM_FLAG) // This memory trick with floats is present only in 2.x
|
82
|
+
#endif
|
83
|
+
)
|
84
|
+
{
|
85
|
+
return 0;
|
86
|
+
}
|
87
|
+
else
|
88
|
+
return 1;
|
89
|
+
}
|
90
|
+
|
91
|
+
|
92
|
+
/*
|
93
|
+
* Converts Ruby string with hexadecimal number
|
94
|
+
* to the Ruby VALUE
|
95
|
+
*/
|
96
|
+
VALUE str_to_value(VALUE str)
|
97
|
+
{
|
98
|
+
intptr_t ans = (intptr_t) Qnil;
|
99
|
+
sscanf(RSTRING_PTR(str), "%"PRIxPTR, &ans);
|
100
|
+
return (VALUE) ans;
|
101
|
+
}
|
102
|
+
|
103
|
+
|
104
|
+
/*
|
105
|
+
* Converts Ruby VALUE (i.e. machine address) to the
|
106
|
+
* hexadecimal Ruby string
|
107
|
+
*/
|
108
|
+
VALUE value_to_str(VALUE val)
|
109
|
+
{
|
110
|
+
char str[16];
|
111
|
+
sprintf(str, "%" PRIxPTR, (intptr_t) val);
|
112
|
+
return rb_str_new2(str);
|
113
|
+
}
|
114
|
+
|
115
|
+
/*
|
116
|
+
* Converts VALUE to the sequence of bytes using big-endian
|
117
|
+
* standard. Returns number of non-zero bytes
|
118
|
+
*
|
119
|
+
* Inputs
|
120
|
+
* val -- input value
|
121
|
+
* buf -- pointer to the output buffer
|
122
|
+
* Returns
|
123
|
+
* number of written bytes
|
124
|
+
*/
|
125
|
+
int value_to_bin(VALUE val, unsigned char *buf)
|
126
|
+
{
|
127
|
+
int i, len = 0;
|
128
|
+
unsigned char byte;
|
129
|
+
for (i = sizeof(VALUE) - 1; i >= 0; i--)
|
130
|
+
{
|
131
|
+
byte = (unsigned char) ((val >> (i * 8)) & 0xFF);
|
132
|
+
if (len > 0 || byte != 0)
|
133
|
+
{
|
134
|
+
*buf++ = byte;
|
135
|
+
len++;
|
136
|
+
}
|
137
|
+
}
|
138
|
+
return len;
|
139
|
+
}
|
140
|
+
|
141
|
+
/*
|
142
|
+
* Converts sequence of bytes (big-endian standard) to the VALUE.
|
143
|
+
*
|
144
|
+
* Inputs
|
145
|
+
* buf -- poiner to the input buffer
|
146
|
+
* len -- number of bytes
|
147
|
+
* Returns
|
148
|
+
* VALUE
|
149
|
+
*/
|
150
|
+
VALUE bin_to_value(unsigned char *buf, int len)
|
151
|
+
{
|
152
|
+
VALUE val = (VALUE) 0;
|
153
|
+
int i;
|
154
|
+
for (i = len - 1; i >= 0; i--)
|
155
|
+
val |= ((VALUE) *buf++) << (i * 8);
|
156
|
+
return val;
|
157
|
+
}
|
158
|
+
|
159
|
+
#define NODES_CTBL_SIZE 256
|
160
|
+
static int nodes_ctbl[NODES_CTBL_SIZE * 3];
|
161
|
+
|
162
|
+
|
163
|
+
/*
|
164
|
+
* Part 3. Functions for node marshalization
|
165
|
+
*/
|
166
|
+
|
167
|
+
/*
|
168
|
+
* Keeps the information about node elements position
|
169
|
+
* in the memory and its IDs/ordinals for export to the file
|
170
|
+
*/
|
171
|
+
typedef struct {
|
172
|
+
VALUE vals; // values: key=>val Hash
|
173
|
+
VALUE ids; // identifiers: key=>id Hash
|
174
|
+
VALUE pos; // free identifier
|
175
|
+
} LeafTableInfo;
|
176
|
+
|
177
|
+
void LeafTableInfo_init(LeafTableInfo *lti)
|
178
|
+
{
|
179
|
+
lti->vals = rb_hash_new();
|
180
|
+
lti->ids = rb_hash_new();
|
181
|
+
lti->pos = 0;
|
182
|
+
}
|
183
|
+
|
184
|
+
void LeafTableInfo_mark(LeafTableInfo *lti)
|
185
|
+
{
|
186
|
+
rb_gc_mark(lti->vals);
|
187
|
+
rb_gc_mark(lti->ids);
|
188
|
+
}
|
189
|
+
|
190
|
+
|
191
|
+
int LeafTableInfo_addEntry(LeafTableInfo *lti, VALUE key, VALUE value)
|
192
|
+
{
|
193
|
+
VALUE v_id = rb_hash_aref(lti->ids, key);
|
194
|
+
if (v_id == Qnil)
|
195
|
+
{
|
196
|
+
int id = lti->pos++;
|
197
|
+
rb_hash_aset(lti->vals, key, value);
|
198
|
+
rb_hash_aset(lti->ids, key, INT2FIX(id));
|
199
|
+
return id;
|
200
|
+
}
|
201
|
+
else
|
202
|
+
{
|
203
|
+
return FIX2INT(v_id);
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
/*
|
208
|
+
* Adds Ruby ID data type as the entry to the LeafTableInfo struct.
|
209
|
+
* Main features:
|
210
|
+
* 1) ID will be converted to Fixnum
|
211
|
+
* 2) If ID can be converted to string by rb_id2str it will be saved as
|
212
|
+
String object. Otherwise it will be converted to Fixnum.
|
213
|
+
*/
|
214
|
+
int LeafTableInfo_addIDEntry(LeafTableInfo *lti, ID id)
|
215
|
+
{
|
216
|
+
VALUE r_idval = rb_id2str(id);
|
217
|
+
if (TYPE(r_idval) != T_STRING)
|
218
|
+
{
|
219
|
+
r_idval = INT2FIX(id);
|
220
|
+
}
|
221
|
+
return LeafTableInfo_addEntry(lti, INT2FIX(id), r_idval);
|
222
|
+
}
|
223
|
+
|
224
|
+
VALUE LeafTableInfo_getLeavesTable(LeafTableInfo *lti)
|
225
|
+
{
|
226
|
+
VALUE key, keys = rb_funcall(lti->vals, rb_intern("keys"), 0);
|
227
|
+
unsigned int i;
|
228
|
+
VALUE val;
|
229
|
+
for (i = 0; i < lti->pos; i++)
|
230
|
+
{
|
231
|
+
key = RARRAY_PTR(keys)[i];
|
232
|
+
val = rb_hash_aref(lti->vals, key);
|
233
|
+
rb_ary_store(keys, i, val);
|
234
|
+
}
|
235
|
+
return keys;
|
236
|
+
}
|
237
|
+
|
238
|
+
int LeafTableInfo_keyToID(LeafTableInfo *lti, VALUE key)
|
239
|
+
{
|
240
|
+
VALUE id = rb_hash_aref(lti->ids, key);
|
241
|
+
return (id == Qnil) ? -1 : FIX2INT(id);
|
242
|
+
}
|
243
|
+
|
244
|
+
VALUE LeafTableInfo_keyToValue(LeafTableInfo *lti, VALUE key)
|
245
|
+
{
|
246
|
+
return rb_hash_aref(lti->vals, key);
|
247
|
+
}
|
248
|
+
|
249
|
+
/* The structure keeps information about the node
|
250
|
+
that is required for its dumping to the file
|
251
|
+
(mainly hashes with relocatable identifiers) */
|
252
|
+
typedef struct {
|
253
|
+
LeafTableInfo syms; // Node symbols
|
254
|
+
LeafTableInfo lits; // Node literals
|
255
|
+
LeafTableInfo idtabs; // Table of identifiers
|
256
|
+
#ifdef USE_RB_ARGS_INFO
|
257
|
+
LeafTableInfo args; // Table of arguments
|
258
|
+
#endif
|
259
|
+
LeafTableInfo gentries; // Global variables table
|
260
|
+
LeafTableInfo nodes; // Table of nodes
|
261
|
+
LeafTableInfo pnodes; // Table of parent nodes
|
262
|
+
} NODEInfo;
|
263
|
+
|
264
|
+
void NODEInfo_init(NODEInfo *info)
|
265
|
+
{
|
266
|
+
LeafTableInfo_init(&(info->syms));
|
267
|
+
LeafTableInfo_init(&(info->lits));
|
268
|
+
LeafTableInfo_init(&(info->idtabs));
|
269
|
+
#ifdef USE_RB_ARGS_INFO
|
270
|
+
LeafTableInfo_init(&(info->args));
|
271
|
+
#endif
|
272
|
+
LeafTableInfo_init(&(info->gentries));
|
273
|
+
LeafTableInfo_init(&(info->nodes));
|
274
|
+
LeafTableInfo_init(&(info->pnodes));
|
275
|
+
}
|
276
|
+
|
277
|
+
void NODEInfo_mark(NODEInfo *info)
|
278
|
+
{
|
279
|
+
LeafTableInfo_mark(&(info->syms));
|
280
|
+
LeafTableInfo_mark(&(info->lits));
|
281
|
+
LeafTableInfo_mark(&(info->idtabs));
|
282
|
+
#ifdef USE_RB_ARGS_INFO
|
283
|
+
LeafTableInfo_mark(&(info->args));
|
284
|
+
#endif
|
285
|
+
LeafTableInfo_mark(&(info->gentries));
|
286
|
+
LeafTableInfo_mark(&(info->nodes));
|
287
|
+
LeafTableInfo_mark(&(info->pnodes));
|
288
|
+
}
|
289
|
+
|
290
|
+
void NODEInfo_free(NODEInfo *info)
|
291
|
+
{
|
292
|
+
xfree(info);
|
293
|
+
}
|
294
|
+
|
295
|
+
LeafTableInfo *NODEInfo_getTableByID(NODEInfo *info, int id)
|
296
|
+
{
|
297
|
+
switch (id)
|
298
|
+
{
|
299
|
+
case NT_ID:
|
300
|
+
return &info->syms;
|
301
|
+
case NT_VALUE:
|
302
|
+
return &info->lits;
|
303
|
+
case NT_IDTABLE:
|
304
|
+
return &info->idtabs;
|
305
|
+
#ifdef USE_RB_ARGS_INFO
|
306
|
+
case NT_ARGS:
|
307
|
+
return &info->args;
|
308
|
+
#endif
|
309
|
+
case NT_ENTRY:
|
310
|
+
return &info->gentries;
|
311
|
+
case NT_NODE:
|
312
|
+
return &info->nodes;
|
313
|
+
default:
|
314
|
+
return NULL;
|
315
|
+
}
|
316
|
+
}
|
317
|
+
|
318
|
+
/*
|
319
|
+
* Converts node value to the binary data
|
320
|
+
* Input parameters:
|
321
|
+
* info -- current NODEInfo structure
|
322
|
+
* node -- parent node (that contains the value)
|
323
|
+
* ptr -- pointer to the output memory buffer
|
324
|
+
* type -- type of the entry (NT_...)
|
325
|
+
* value -- node->u?.value VALUE
|
326
|
+
* child_id -- child node number (1,2,3)
|
327
|
+
* Returns:
|
328
|
+
* Byte that contains the next information
|
329
|
+
* a) upper half-byte: VL_... data type (for node loader)
|
330
|
+
* b) lower half-byte: number of bytes written to the buffer
|
331
|
+
*/
|
332
|
+
#define DUMP_RAW_VALUE(vl_ans, vl) (vl_ans | (value_to_bin(vl, (unsigned char *) ptr) << 4))
|
333
|
+
static int dump_node_value(NODEInfo *info, char *ptr, NODE *node, int type, VALUE value, int child_id)
|
334
|
+
{
|
335
|
+
if (type == NT_NULL || type == NT_LONG)
|
336
|
+
{
|
337
|
+
return DUMP_RAW_VALUE(VL_RAW, value);
|
338
|
+
}
|
339
|
+
else if (type == NT_NODE)
|
340
|
+
{
|
341
|
+
if (value == 0)
|
342
|
+
{ // Variant a: empty node
|
343
|
+
return DUMP_RAW_VALUE(VL_RAW, value);
|
344
|
+
}
|
345
|
+
else if (nd_type(node) == NODE_ATTRASGN && value == 1 && child_id == 1)
|
346
|
+
{ // Special case: "self"
|
347
|
+
return DUMP_RAW_VALUE(VL_RAW, value);
|
348
|
+
}
|
349
|
+
else if (TYPE(value) != T_NODE)
|
350
|
+
{
|
351
|
+
rb_raise(rb_eArgError, "dump_node_value, parent node %s (ADR 0x%s): child node %d (ADR 0x%s): is not a node\n"
|
352
|
+
" Type: %s (%d), Value: %s",
|
353
|
+
ruby_node_name(nd_type(node)), RSTRING_PTR(value_to_str((VALUE) node)),
|
354
|
+
child_id, RSTRING_PTR(value_to_str(value)),
|
355
|
+
RSTRING_PTR(rb_funcall(rb_funcall(value, rb_intern("class"), 0), rb_intern("to_s"), 0)),
|
356
|
+
TYPE(value),
|
357
|
+
RSTRING_PTR(rb_funcall(value, rb_intern("to_s"), 0)) );
|
358
|
+
}
|
359
|
+
else
|
360
|
+
{ // Variant b: not empty node
|
361
|
+
VALUE id = LeafTableInfo_keyToID(&info->nodes, value_to_str(value));
|
362
|
+
if (id == (VALUE) -1)
|
363
|
+
{
|
364
|
+
rb_raise(rb_eArgError, "dump_node_value, parent node %s (ADR 0x%s): child node %d (ADR 0x%s) not found",
|
365
|
+
ruby_node_name(nd_type(node)), RSTRING_PTR(value_to_str((VALUE) node)),
|
366
|
+
child_id, RSTRING_PTR(value_to_str(value)));
|
367
|
+
return VL_RAW;
|
368
|
+
}
|
369
|
+
else
|
370
|
+
{
|
371
|
+
return DUMP_RAW_VALUE(VL_NODE, id);
|
372
|
+
}
|
373
|
+
return VL_NODE;
|
374
|
+
}
|
375
|
+
}
|
376
|
+
else if (type == NT_VALUE)
|
377
|
+
{
|
378
|
+
if (!is_value_in_heap(value))
|
379
|
+
{ // a) value that is inside VALUE
|
380
|
+
return DUMP_RAW_VALUE(VL_RAW, value);
|
381
|
+
}
|
382
|
+
else
|
383
|
+
{ // b) value that requires reference to literals table
|
384
|
+
VALUE id = LeafTableInfo_keyToID(&info->lits, value_to_str(value));
|
385
|
+
if (id == (VALUE) -1)
|
386
|
+
rb_raise(rb_eArgError, "Cannot find literal");
|
387
|
+
else
|
388
|
+
return DUMP_RAW_VALUE(VL_LIT, id);
|
389
|
+
}
|
390
|
+
}
|
391
|
+
else if (type == NT_ID)
|
392
|
+
{
|
393
|
+
ID sym = (VALUE) value; // We are working with RAW data from RAM!
|
394
|
+
VALUE id = LeafTableInfo_keyToID(&info->syms, INT2FIX(sym));
|
395
|
+
if (id == (VALUE) -1)
|
396
|
+
{
|
397
|
+
rb_raise(rb_eArgError, "Cannot find symbol ID %d (%s) (parent node %s, line %d)",
|
398
|
+
(int) sym, RSTRING_PTR(rb_id2str(ID2SYM(sym))),
|
399
|
+
ruby_node_name(nd_type(node)), nd_line(node));
|
400
|
+
return VL_RAW;
|
401
|
+
}
|
402
|
+
else
|
403
|
+
{
|
404
|
+
return DUMP_RAW_VALUE(VL_ID, id);
|
405
|
+
}
|
406
|
+
}
|
407
|
+
else if (type == NT_ENTRY || type == NT_ARGS || type == NT_IDTABLE)
|
408
|
+
{
|
409
|
+
VALUE key = value_to_str(value);
|
410
|
+
LeafTableInfo *lti = NODEInfo_getTableByID(info, type);
|
411
|
+
VALUE id = LeafTableInfo_keyToID(lti, key);
|
412
|
+
if (id == (VALUE) -1)
|
413
|
+
{
|
414
|
+
rb_raise(rb_eArgError, "Cannot find some entry");
|
415
|
+
return VL_RAW;
|
416
|
+
}
|
417
|
+
else
|
418
|
+
{
|
419
|
+
switch(type)
|
420
|
+
{
|
421
|
+
case NT_ENTRY: return DUMP_RAW_VALUE(VL_GVAR, id);
|
422
|
+
case NT_IDTABLE: return DUMP_RAW_VALUE(VL_IDTABLE, id);
|
423
|
+
case NT_ARGS: return DUMP_RAW_VALUE(VL_ARGS, id);
|
424
|
+
default: rb_raise(rb_eArgError, "Internal error");
|
425
|
+
}
|
426
|
+
}
|
427
|
+
}
|
428
|
+
else
|
429
|
+
{
|
430
|
+
rb_raise(rb_eArgError, "Unknown child node type %d", type);
|
431
|
+
}
|
432
|
+
}
|
433
|
+
|
434
|
+
/*
|
435
|
+
* Converts information about nodes to the binary string.
|
436
|
+
* It uses dump_node_value function for the low-level conversion
|
437
|
+
* of node "leaves" to the actual binary data.
|
438
|
+
*
|
439
|
+
* See load_nodes_from_str for the descrpition of the binary string format.
|
440
|
+
*/
|
441
|
+
static VALUE dump_nodes(NODEInfo *info)
|
442
|
+
{
|
443
|
+
int node_size = sizeof(int) + sizeof(VALUE) * 4;
|
444
|
+
int i, nt, flags_len;
|
445
|
+
NODE *node;
|
446
|
+
char *bin, *ptr, *rtypes;
|
447
|
+
VALUE nodes_ary = rb_funcall(info->nodes.vals, rb_intern("keys"), 0);
|
448
|
+
VALUE nodes_bin = rb_str_new(NULL, RARRAY_LEN(nodes_ary) * node_size);
|
449
|
+
VALUE ut[3];
|
450
|
+
bin = RSTRING_PTR(nodes_bin);
|
451
|
+
|
452
|
+
for (i = 0, ptr = bin; i < RARRAY_LEN(nodes_ary); i++)
|
453
|
+
{
|
454
|
+
node = RNODE(str_to_value(RARRAY_PTR(nodes_ary)[i]));
|
455
|
+
nt = nd_type(node);
|
456
|
+
rtypes = (char *) ptr; ptr += sizeof(int);
|
457
|
+
flags_len = value_to_bin(node->flags >> 5, (unsigned char *) ptr); ptr += flags_len;
|
458
|
+
|
459
|
+
ut[0] = nodes_ctbl[nt * 3];
|
460
|
+
ut[1] = nodes_ctbl[nt * 3 + 1];
|
461
|
+
ut[2] = nodes_ctbl[nt * 3 + 2];
|
462
|
+
if ((nt == NODE_LASGN || nt == NODE_DASGN_CURR) && (void *) node->u2.value == (void *) -1) {
|
463
|
+
ut[1] = NT_LONG;
|
464
|
+
}
|
465
|
+
if (nt == NODE_OP_ASGN2 && LeafTableInfo_keyToID(&info->syms, INT2FIX(node->u1.value)) != -1)
|
466
|
+
{
|
467
|
+
ut[0] = NT_ID; ut[1] = NT_ID; ut[2] = NT_ID;
|
468
|
+
}
|
469
|
+
|
470
|
+
if (nt == NODE_ARGS_AUX)
|
471
|
+
{
|
472
|
+
ut[0] = NT_ID; ut[1] = NT_LONG; ut[2] = NT_NODE;
|
473
|
+
if (LeafTableInfo_keyToID(&info->syms, INT2FIX(node->u2.value)) != -1)
|
474
|
+
{
|
475
|
+
ut[1] = NT_ID;
|
476
|
+
}
|
477
|
+
else
|
478
|
+
{
|
479
|
+
ut[1] = NT_LONG;
|
480
|
+
}
|
481
|
+
if (node->u1.value == 0) ut[0] = NT_NULL;
|
482
|
+
if (node->u2.value == 0) ut[1] = NT_NULL;
|
483
|
+
if (node->u3.value == 0) ut[2] = NT_NULL;
|
484
|
+
}
|
485
|
+
|
486
|
+
if (nt = NODE_ARRAY)
|
487
|
+
{
|
488
|
+
/* Special undocumented cases:
|
489
|
+
* 1) the second child of the second element of an array
|
490
|
+
* contains reference to the last element (NT_NODE) not
|
491
|
+
* length (NT_LONG)
|
492
|
+
* 2) NODE_HASH: every second element in NODE_ARRAY chain
|
493
|
+
* contains pointers to NODES (instead of lengths)
|
494
|
+
* 3) NODE_DSTR: first node in NODE_ARRAY chain contains
|
495
|
+
* pointer to NODE (instead of lengths) */
|
496
|
+
NODE *pnode1, *pnode2;
|
497
|
+
pnode1 = (NODE *) str_to_value(LeafTableInfo_keyToValue(&info->pnodes, value_to_str((VALUE) node)));
|
498
|
+
if (pnode1 != NULL && nd_type(pnode1) == NODE_ARRAY &&
|
499
|
+
(NODE *) pnode1->u3.value == node)
|
500
|
+
{
|
501
|
+
int nt2;
|
502
|
+
pnode2 = (NODE *) str_to_value(LeafTableInfo_keyToValue(&info->pnodes, value_to_str((VALUE) pnode1)));
|
503
|
+
nt2 = nd_type(pnode2);
|
504
|
+
if ( (nt2 != NODE_ARRAY && nt2 != NODE_DSTR) ||
|
505
|
+
(NODE *) pnode2->u1.value == pnode1 )
|
506
|
+
{
|
507
|
+
ut[1] = NT_NODE;
|
508
|
+
}
|
509
|
+
else if (pnode1->u2.value == 2 && node == (NODE *) node->u2.value)
|
510
|
+
{
|
511
|
+
ut[1] = NT_NODE;
|
512
|
+
}
|
513
|
+
}
|
514
|
+
else if (pnode1 != NULL && nd_type(pnode1) == NODE_DSTR)
|
515
|
+
{
|
516
|
+
ut[1] = NT_NODE;
|
517
|
+
}
|
518
|
+
}
|
519
|
+
|
520
|
+
rtypes[0] = dump_node_value(info, ptr, node, ut[0], node->u1.value, 1);
|
521
|
+
ptr += (rtypes[0] & 0xF0) >> 4;
|
522
|
+
rtypes[1] = dump_node_value(info, ptr, node, ut[1], node->u2.value, 2);
|
523
|
+
ptr += (rtypes[1] & 0xF0) >> 4;
|
524
|
+
rtypes[2] = dump_node_value(info, ptr, node, ut[2], node->u3.value, 3);
|
525
|
+
ptr += (rtypes[2] & 0xF0) >> 4;
|
526
|
+
rtypes[3] = flags_len;
|
527
|
+
}
|
528
|
+
rb_str_resize(nodes_bin, (int) (ptr - bin) + 1);
|
529
|
+
return nodes_bin;
|
530
|
+
}
|
531
|
+
|
532
|
+
|
533
|
+
/*
|
534
|
+
* Transforms preprocessed node to Ruby hash that can be used
|
535
|
+
* to load the node from disk.
|
536
|
+
*
|
537
|
+
* See m_nodedump_to_hash function for output hash format details
|
538
|
+
*/
|
539
|
+
VALUE NODEInfo_toHash(NODEInfo *info)
|
540
|
+
{
|
541
|
+
VALUE ans = rb_hash_new();
|
542
|
+
VALUE idtbl, idtabs = LeafTableInfo_getLeavesTable(&info->idtabs);
|
543
|
+
VALUE syms = LeafTableInfo_getLeavesTable(&info->syms);
|
544
|
+
VALUE args;
|
545
|
+
int i, j, id;
|
546
|
+
// Add some signatures
|
547
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("MAGIC")), rb_str_new2(NODEMARSHAL_MAGIC));
|
548
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("RUBY_PLATFORM")),
|
549
|
+
rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM")));
|
550
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("RUBY_VERSION")),
|
551
|
+
rb_const_get(rb_cObject, rb_intern("RUBY_VERSION")));
|
552
|
+
// Write literals, symbols and global_entries arrays: they don't need to be corrected
|
553
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("literals")), LeafTableInfo_getLeavesTable(&info->lits));
|
554
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("symbols")), syms);
|
555
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("global_entries")), LeafTableInfo_getLeavesTable(&info->gentries));
|
556
|
+
// Replace RAM IDs to disk IDs in id_tables
|
557
|
+
for (i = 0; i < RARRAY_LEN(idtabs); i++)
|
558
|
+
{
|
559
|
+
idtbl = RARRAY_PTR(idtabs)[i];
|
560
|
+
for (j = 0; j < RARRAY_LEN(idtbl); j++)
|
561
|
+
{
|
562
|
+
id = LeafTableInfo_keyToID(&info->syms, RARRAY_PTR(idtbl)[j]);
|
563
|
+
|
564
|
+
if (id == -1)
|
565
|
+
{
|
566
|
+
ID sym = FIX2INT(RARRAY_PTR(idtbl)[j]);
|
567
|
+
rb_raise(rb_eArgError, "Cannot find the symbol ID %d", (int) sym);
|
568
|
+
}
|
569
|
+
else
|
570
|
+
{
|
571
|
+
rb_ary_store(idtbl, j, INT2FIX(id));
|
572
|
+
}
|
573
|
+
|
574
|
+
}
|
575
|
+
}
|
576
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("id_tables")), idtabs);
|
577
|
+
// Replace RAM IDs to disk IDs in args tables
|
578
|
+
#ifdef USE_RB_ARGS_INFO
|
579
|
+
args = LeafTableInfo_getLeavesTable(&info->args);
|
580
|
+
for (i = 0; i < RARRAY_LEN(args); i++)
|
581
|
+
{
|
582
|
+
VALUE args_entry = RARRAY_PTR(args)[i];
|
583
|
+
VALUE *eptr = RARRAY_PTR(args_entry);
|
584
|
+
int args_vals[5] = {0, 1, 7, 8, 9};
|
585
|
+
int args_ids[3] = {4, 5, 6};
|
586
|
+
if (RARRAY_LEN(args_entry) != 10)
|
587
|
+
rb_raise(rb_eArgError, "Corrupted args entry");
|
588
|
+
// Pointer to nodes to be replaced:
|
589
|
+
// a) VALUES
|
590
|
+
// (0) pre_init, (1) post_init,
|
591
|
+
// (7) kw_args, (8) kw_rest_arg, (9) opt_args
|
592
|
+
for (j = 0; j < 5; j++)
|
593
|
+
{
|
594
|
+
int ind = args_vals[j];
|
595
|
+
VALUE key = eptr[ind];
|
596
|
+
if (!strcmp(RSTRING_PTR(key), "0"))
|
597
|
+
eptr[ind] = INT2FIX(-1);
|
598
|
+
else
|
599
|
+
{
|
600
|
+
eptr[ind] = INT2FIX(LeafTableInfo_keyToID(&info->nodes, key));
|
601
|
+
if (FIX2INT(eptr[ind]) == -1)
|
602
|
+
rb_raise(rb_eArgError, "Unknown NODE in args tables");
|
603
|
+
}
|
604
|
+
}
|
605
|
+
// b) IDs (symbols)
|
606
|
+
// (4) first_post_arg (5) rest_arg (6) block_arg
|
607
|
+
for (j = 0; j < 3; j++)
|
608
|
+
{
|
609
|
+
int ind = args_ids[j];
|
610
|
+
VALUE key = eptr[ind];
|
611
|
+
if (FIX2INT(key) != 0)
|
612
|
+
{
|
613
|
+
eptr[ind] = INT2FIX(LeafTableInfo_keyToID(&info->syms, key));
|
614
|
+
if (FIX2INT(eptr[ind]) == -1)
|
615
|
+
rb_raise(rb_eArgError, "Unknown symbolic ID in args tables");
|
616
|
+
}
|
617
|
+
else
|
618
|
+
eptr[ind] = INT2FIX(-1);
|
619
|
+
}
|
620
|
+
}
|
621
|
+
#else
|
622
|
+
args = rb_ary_new();
|
623
|
+
#endif
|
624
|
+
|
625
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("args")), args);
|
626
|
+
// Special case: NODES. Nodes are kept as binary string
|
627
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("nodes")), dump_nodes(info));
|
628
|
+
return ans;
|
629
|
+
}
|
630
|
+
|
631
|
+
|
632
|
+
static void NODEInfo_addValue(NODEInfo *info, VALUE value)
|
633
|
+
{
|
634
|
+
if (is_value_in_heap(value))
|
635
|
+
{
|
636
|
+
VALUE lkey = value_to_str(value);
|
637
|
+
LeafTableInfo_addEntry(&info->lits, lkey, value);
|
638
|
+
}
|
639
|
+
}
|
640
|
+
|
641
|
+
/*
|
642
|
+
* Adds the information about Ruby NODE to the NODEInfo struct.
|
643
|
+
* It keeps the addresses of the node and its parents
|
644
|
+
*/
|
645
|
+
static void NODEInfo_addNode(NODEInfo *info, NODE *node, NODE *pnode)
|
646
|
+
{
|
647
|
+
VALUE node_adr = value_to_str((VALUE) node);
|
648
|
+
VALUE pnode_adr = value_to_str((VALUE) pnode);
|
649
|
+
LeafTableInfo_addEntry(&info->nodes, node_adr, node_adr);
|
650
|
+
LeafTableInfo_addEntry(&info->pnodes, node_adr, pnode_adr);
|
651
|
+
}
|
652
|
+
|
653
|
+
/*
|
654
|
+
* Returns ID of the node using its address (VALUE)
|
655
|
+
* It is used during the process of dumping Ruby AST to disk
|
656
|
+
* for replacing of memory addresses into ordinals
|
657
|
+
*/
|
658
|
+
static int NODEInfo_nodeAdrToID(NODEInfo *info, VALUE adr)
|
659
|
+
{
|
660
|
+
return LeafTableInfo_keyToID(&info->nodes, adr);
|
661
|
+
}
|
662
|
+
|
663
|
+
/*
|
664
|
+
* Function counts number of nodes and fills NODEInfo struct
|
665
|
+
* that is neccessary for the node saving to the HDD
|
666
|
+
*/
|
667
|
+
static int count_num_of_nodes(NODE *node, NODE *parent, NODEInfo *info)
|
668
|
+
{
|
669
|
+
int ut[3], num, offset;
|
670
|
+
if (node == 0)
|
671
|
+
{
|
672
|
+
return 0;
|
673
|
+
}
|
674
|
+
else if (TYPE((VALUE) node) != T_NODE)
|
675
|
+
{
|
676
|
+
rb_raise(rb_eArgError, "count_num_of_nodes: parent node %s: child node (ADR 0x%s) is not a node; Type: %d (%s)",
|
677
|
+
ruby_node_name(nd_type(parent)), RSTRING_PTR(value_to_str((VALUE) node)), TYPE((VALUE) node),
|
678
|
+
RSTRING_PTR(rb_funcall(rb_funcall((VALUE) node, rb_intern("class"), 0), rb_intern("to_s"), 0))
|
679
|
+
);
|
680
|
+
return 0;
|
681
|
+
}
|
682
|
+
else
|
683
|
+
{
|
684
|
+
offset = nd_type(node) * 3;
|
685
|
+
ut[0] = nodes_ctbl[offset++];
|
686
|
+
ut[1] = nodes_ctbl[offset++];
|
687
|
+
ut[2] = nodes_ctbl[offset];
|
688
|
+
|
689
|
+
/* Special case: part of NODE_KW_ARG syntax in Ruby 2.x, e.g. def func(foo:, bar: 'default) */
|
690
|
+
if ((nd_type(node) == NODE_LASGN || nd_type(node) == NODE_DASGN_CURR) && (void *) node->u2.value == (void *) -1) {
|
691
|
+
ut[1] = NT_LONG; /* To keep -1 correctly */
|
692
|
+
}
|
693
|
+
|
694
|
+
/* Some another special cases */
|
695
|
+
if (nd_type(node) == NODE_OP_ASGN2 && nd_type(parent) == NODE_OP_ASGN2)
|
696
|
+
{
|
697
|
+
ut[0] = NT_ID;
|
698
|
+
ut[1] = NT_ID;
|
699
|
+
ut[2] = NT_ID;
|
700
|
+
}
|
701
|
+
|
702
|
+
/* Some Ruby 1.9.3 style function arguments (without rb_args_info) */
|
703
|
+
if (nd_type(node) == NODE_ARGS_AUX)
|
704
|
+
{
|
705
|
+
ut[0] = NT_ID;
|
706
|
+
ut[1] = (nd_type(parent) == NODE_ARGS_AUX) ? NT_LONG : NT_ID;
|
707
|
+
ut[2] = NT_NODE;
|
708
|
+
|
709
|
+
if (node->u1.value == 0) ut[0] = NT_NULL;
|
710
|
+
if (node->u2.value == 0) ut[1] = NT_NULL;
|
711
|
+
if (node->u3.value == 0) ut[2] = NT_NULL;
|
712
|
+
}
|
713
|
+
/* Some Ruby 1.9.3-specific code for NODE_ATTRASGN */
|
714
|
+
if (nd_type(node) == NODE_ATTRASGN)
|
715
|
+
{
|
716
|
+
if (node->u1.value == 1) ut[0] = NT_LONG;
|
717
|
+
}
|
718
|
+
/* Check if there is information about child nodes types */
|
719
|
+
if (ut[0] == NT_UNKNOWN || ut[1] == NT_UNKNOWN || ut[2] == NT_UNKNOWN)
|
720
|
+
{
|
721
|
+
rb_raise(rb_eArgError, "Cannot interpret node %d (%s)", nd_type(node), ruby_node_name(nd_type(node)));
|
722
|
+
}
|
723
|
+
/* Save the ID of the node */
|
724
|
+
num = 1;
|
725
|
+
NODEInfo_addNode(info, node, parent);
|
726
|
+
/* Analyze node childs */
|
727
|
+
/* a) child 1 */
|
728
|
+
if (ut[0] == NT_NODE)
|
729
|
+
{
|
730
|
+
num += count_num_of_nodes(node->u1.node, node, info);
|
731
|
+
}
|
732
|
+
else if (ut[0] == NT_ID)
|
733
|
+
{
|
734
|
+
LeafTableInfo_addIDEntry(&info->syms, node->u1.id);
|
735
|
+
}
|
736
|
+
else if (ut[0] == NT_VALUE)
|
737
|
+
{
|
738
|
+
if (TYPE(node->u1.value) == T_NODE)
|
739
|
+
rb_raise(rb_eArgError, "NODE instead of VALUE in child 1 of node %s", ruby_node_name(nd_type(node)));
|
740
|
+
NODEInfo_addValue(info, node->u1.value);
|
741
|
+
}
|
742
|
+
else if (ut[0] == NT_IDTABLE)
|
743
|
+
{
|
744
|
+
VALUE tkey = value_to_str(node->u1.value);
|
745
|
+
VALUE idtbl_ary = rb_ary_new();
|
746
|
+
ID *idtbl = (ID *) node->u1.value;
|
747
|
+
int i, size = (node->u1.value) ? *idtbl++ : 0;
|
748
|
+
for (i = 0; i < size; i++)
|
749
|
+
{
|
750
|
+
ID sym = *idtbl++;
|
751
|
+
rb_ary_push(idtbl_ary, INT2FIX(sym));
|
752
|
+
LeafTableInfo_addIDEntry(&info->syms, sym);
|
753
|
+
}
|
754
|
+
LeafTableInfo_addEntry(&info->idtabs, tkey, idtbl_ary);
|
755
|
+
}
|
756
|
+
else if (ut[0] != NT_LONG && ut[0] != NT_NULL)
|
757
|
+
{
|
758
|
+
rb_raise(rb_eArgError, "1!");
|
759
|
+
}
|
760
|
+
/* b) child 2 */
|
761
|
+
if (ut[1] == NT_NODE)
|
762
|
+
{
|
763
|
+
num += count_num_of_nodes(node->u2.node, node, info);
|
764
|
+
}
|
765
|
+
else if (ut[1] == NT_ID)
|
766
|
+
{
|
767
|
+
LeafTableInfo_addIDEntry(&info->syms, node->u2.id);
|
768
|
+
}
|
769
|
+
else if (ut[1] == NT_VALUE)
|
770
|
+
{
|
771
|
+
if (TYPE(node->u2.value) == T_NODE)
|
772
|
+
rb_raise(rb_eArgError, "NODE instead of VALUE in child 2 of node %s", ruby_node_name(nd_type(node)));
|
773
|
+
NODEInfo_addValue(info, node->u2.value);
|
774
|
+
}
|
775
|
+
else if (ut[1] != NT_LONG && ut[1] != NT_NULL)
|
776
|
+
{
|
777
|
+
rb_raise(rb_eArgError, "2!");
|
778
|
+
}
|
779
|
+
|
780
|
+
/* c) child 3 */
|
781
|
+
if (ut[2] == NT_NODE)
|
782
|
+
{
|
783
|
+
num += count_num_of_nodes(node->u3.node, node, info);
|
784
|
+
}
|
785
|
+
else if (ut[2] == NT_ID)
|
786
|
+
{
|
787
|
+
LeafTableInfo_addIDEntry(&info->syms, node->u3.id);
|
788
|
+
}
|
789
|
+
else if (ut[2] == NT_ARGS)
|
790
|
+
{
|
791
|
+
#ifdef USE_RB_ARGS_INFO
|
792
|
+
VALUE varg = Qtrue;
|
793
|
+
struct rb_args_info *ainfo;
|
794
|
+
ID asym;
|
795
|
+
ainfo = node->u3.args;
|
796
|
+
// Save child nodes
|
797
|
+
num += count_num_of_nodes(ainfo->pre_init, node, info);
|
798
|
+
num += count_num_of_nodes(ainfo->post_init, node, info);
|
799
|
+
num += count_num_of_nodes(ainfo->kw_args, node, info);
|
800
|
+
num += count_num_of_nodes(ainfo->kw_rest_arg, node, info);
|
801
|
+
num += count_num_of_nodes(ainfo->opt_args, node, info);
|
802
|
+
// Save rb_args_info structure content
|
803
|
+
varg = rb_ary_new();
|
804
|
+
rb_ary_push(varg, value_to_str((VALUE) ainfo->pre_init));
|
805
|
+
rb_ary_push(varg, value_to_str((VALUE) ainfo->post_init));
|
806
|
+
rb_ary_push(varg, INT2FIX(ainfo->pre_args_num));
|
807
|
+
rb_ary_push(varg, INT2FIX(ainfo->post_args_num));
|
808
|
+
|
809
|
+
asym = ainfo->first_post_arg; rb_ary_push(varg, INT2FIX(asym)); // ID
|
810
|
+
if (asym != 0)
|
811
|
+
LeafTableInfo_addIDEntry(&info->syms, asym);
|
812
|
+
|
813
|
+
asym = ainfo->rest_arg; rb_ary_push(varg, INT2FIX(asym)); // ID
|
814
|
+
if (asym != 0)
|
815
|
+
LeafTableInfo_addIDEntry(&info->syms, asym);
|
816
|
+
|
817
|
+
asym = ainfo->block_arg; rb_ary_push(varg, INT2FIX(asym)); // ID
|
818
|
+
if (asym != 0)
|
819
|
+
LeafTableInfo_addIDEntry(&info->syms, asym);
|
820
|
+
rb_ary_push(varg, value_to_str((VALUE) ainfo->kw_args));
|
821
|
+
rb_ary_push(varg, value_to_str((VALUE) ainfo->kw_rest_arg));
|
822
|
+
rb_ary_push(varg, value_to_str((VALUE) ainfo->opt_args));
|
823
|
+
|
824
|
+
LeafTableInfo_addEntry(&info->args, value_to_str((VALUE) ainfo), varg);
|
825
|
+
#else
|
826
|
+
rb_raise(rb_eArgError, "NT_ARGS entry without USE_RB_ARGS_INFO");
|
827
|
+
#endif
|
828
|
+
}
|
829
|
+
else if (ut[2] == NT_ENTRY)
|
830
|
+
{
|
831
|
+
ID gsym = node->u3.entry->id;
|
832
|
+
// Save symbol to the symbol table
|
833
|
+
int newid = LeafTableInfo_addIDEntry(&info->syms, gsym);
|
834
|
+
LeafTableInfo_addEntry(&info->gentries, value_to_str(node->u3.value), INT2FIX(newid));
|
835
|
+
}
|
836
|
+
else if (ut[2] != NT_LONG && ut[2] != NT_NULL)
|
837
|
+
{
|
838
|
+
rb_raise(rb_eArgError, "Invalid child node 3 of node %s: TYPE %d, VALUE %"PRIxPTR,
|
839
|
+
ruby_node_name(nd_type(node)), ut[2], (uintptr_t) (node->u3.value));
|
840
|
+
}
|
841
|
+
|
842
|
+
return num;
|
843
|
+
}
|
844
|
+
}
|
845
|
+
|
846
|
+
|
847
|
+
|
848
|
+
//-------------------------------------------------------------------------
|
849
|
+
|
850
|
+
/*
|
851
|
+
* Part 4. Functions for loading marshalled nodes
|
852
|
+
*/
|
853
|
+
typedef struct {
|
854
|
+
ID *syms_adr; // Table of symbols
|
855
|
+
int syms_len;
|
856
|
+
|
857
|
+
VALUE *lits_adr; // Table of literals
|
858
|
+
int lits_len;
|
859
|
+
|
860
|
+
ID **idtbls_adr; // Table of symbols tables
|
861
|
+
int idtbls_len;
|
862
|
+
|
863
|
+
struct rb_global_entry **gvars_adr; // Table of global variables entries
|
864
|
+
int gvars_len;
|
865
|
+
|
866
|
+
NODE **nodes_adr; // Table of nodes
|
867
|
+
int nodes_len;
|
868
|
+
#ifdef USE_RB_ARGS_INFO
|
869
|
+
struct rb_args_info **args_adr; // Table of code blocks arguments
|
870
|
+
int args_len;
|
871
|
+
#endif
|
872
|
+
} NODEObjAddresses;
|
873
|
+
|
874
|
+
|
875
|
+
void NODEObjAddresses_free(NODEObjAddresses *obj)
|
876
|
+
{
|
877
|
+
xfree(obj->syms_adr);
|
878
|
+
xfree(obj->idtbls_adr);
|
879
|
+
xfree(obj->gvars_adr);
|
880
|
+
xfree(obj->nodes_adr);
|
881
|
+
#ifdef USE_RB_ARGS_INFO
|
882
|
+
xfree(obj->args_adr);
|
883
|
+
#endif
|
884
|
+
xfree(obj);
|
885
|
+
}
|
886
|
+
|
887
|
+
|
888
|
+
|
889
|
+
void rbstr_printf(VALUE str, const char *fmt, ...)
|
890
|
+
{
|
891
|
+
char buf[1024];
|
892
|
+
va_list ptr;
|
893
|
+
|
894
|
+
va_start(ptr, fmt);
|
895
|
+
vsprintf(buf, fmt, ptr);
|
896
|
+
rb_str_append(str, rb_str_new2(buf));
|
897
|
+
va_end(ptr);
|
898
|
+
}
|
899
|
+
|
900
|
+
const char *symid_to_cstr(ID symid)
|
901
|
+
{
|
902
|
+
const char *str_null = "<NULL>", *str_intern = "<NONAME>";
|
903
|
+
const char *str_sym;
|
904
|
+
|
905
|
+
if (symid == 0)
|
906
|
+
str_sym = str_null;
|
907
|
+
else
|
908
|
+
{
|
909
|
+
VALUE rbstr_sym = rb_id2str(symid);
|
910
|
+
if (TYPE(rbstr_sym) == T_STRING)
|
911
|
+
str_sym = RSTRING_PTR(rb_id2str(symid));
|
912
|
+
else
|
913
|
+
str_sym = str_intern;
|
914
|
+
}
|
915
|
+
return str_sym;
|
916
|
+
}
|
917
|
+
|
918
|
+
#define PRINT_NODE_TAB for (j = 0; j < tab; j++) rbstr_printf(str, " ");
|
919
|
+
/*
|
920
|
+
* Recursively transforms node into Ruby string
|
921
|
+
* str -- output Ruby string
|
922
|
+
* node -- input Ruby NODE
|
923
|
+
* tab -- number of tabulations during print
|
924
|
+
* show_offsets -- 0/1 show/hide addresses and symbol IDs
|
925
|
+
*/
|
926
|
+
static void print_node(VALUE str, NODE *node, int tab, int show_offsets)
|
927
|
+
{
|
928
|
+
int i, j, type, ut[3];
|
929
|
+
VALUE uref[3];
|
930
|
+
|
931
|
+
PRINT_NODE_TAB
|
932
|
+
if (node == NULL)
|
933
|
+
{
|
934
|
+
rbstr_printf(str, "(NULL)\n");
|
935
|
+
return;
|
936
|
+
}
|
937
|
+
type = nd_type(node);
|
938
|
+
|
939
|
+
if (show_offsets)
|
940
|
+
{
|
941
|
+
rbstr_printf(str, "@ %s | %16"PRIxPTR " | %16"PRIxPTR " %16"PRIxPTR " %16"PRIxPTR " (line %d)\n",
|
942
|
+
ruby_node_name(type),
|
943
|
+
(intptr_t) node,
|
944
|
+
(intptr_t) node->u1.value, (intptr_t) node->u2.value, (intptr_t) node->u3.value,
|
945
|
+
nd_line(node));
|
946
|
+
}
|
947
|
+
else
|
948
|
+
{
|
949
|
+
rbstr_printf(str, "@ %s (line %d)\n", ruby_node_name(type), nd_line(node));
|
950
|
+
}
|
951
|
+
|
952
|
+
ut[0] = nodes_ctbl[type * 3];
|
953
|
+
ut[1] = nodes_ctbl[type * 3 + 1];
|
954
|
+
ut[2] = nodes_ctbl[type * 3 + 2];
|
955
|
+
|
956
|
+
uref[0] = node->u1.value;
|
957
|
+
uref[1] = node->u2.value;
|
958
|
+
uref[2] = node->u3.value;
|
959
|
+
|
960
|
+
if ((type == NODE_LASGN || type == NODE_DASGN_CURR) && (void *) node->u2.value == (void *) -1)
|
961
|
+
{
|
962
|
+
ut[1] = NT_LONG;
|
963
|
+
}
|
964
|
+
|
965
|
+
for (i = 0; i < 3; i++)
|
966
|
+
{
|
967
|
+
|
968
|
+
if (ut[i] == NT_NODE)
|
969
|
+
{
|
970
|
+
if (nd_type(node) != NODE_OP_ASGN2 || i != 2)
|
971
|
+
print_node(str, RNODE(uref[i]), tab + 1, show_offsets);
|
972
|
+
else
|
973
|
+
{
|
974
|
+
if (ut[i] != 0 && TYPE(ut[i]) != T_NODE)
|
975
|
+
rb_raise(rb_eArgError, "print_node: broken node 0x%s", RSTRING_PTR(value_to_str(ut[i])));
|
976
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
977
|
+
rbstr_printf(str, "%"PRIxPTR " %"PRIxPTR " %"PRIxPTR"\n",
|
978
|
+
(intptr_t) RNODE(uref[i])->u1.value,
|
979
|
+
(intptr_t) RNODE(uref[i])->u2.value,
|
980
|
+
(intptr_t) RNODE(uref[i])->u3.value);
|
981
|
+
}
|
982
|
+
}
|
983
|
+
else if (ut[i] == NT_VALUE)
|
984
|
+
{
|
985
|
+
char *class_name = RSTRING_PTR(rb_funcall(rb_funcall(uref[i], rb_intern("class"), 0), rb_intern("to_s"), 0));
|
986
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
987
|
+
if (show_offsets)
|
988
|
+
{
|
989
|
+
rbstr_printf(str, ">| ADR: %"PRIxPTR"; CLASS: %s (TYPE %d); VALUE: %s\n",
|
990
|
+
(intptr_t) uref[i],
|
991
|
+
class_name, TYPE(uref[i]),
|
992
|
+
RSTRING_PTR(rb_funcall(uref[i], rb_intern("to_s"), 0)));
|
993
|
+
}
|
994
|
+
else
|
995
|
+
{
|
996
|
+
rbstr_printf(str, ">| CLASS: %s (TYPE %d); VALUE: %s\n",
|
997
|
+
class_name, TYPE(uref[i]),
|
998
|
+
RSTRING_PTR(rb_funcall(uref[i], rb_intern("to_s"), 0)));
|
999
|
+
}
|
1000
|
+
}
|
1001
|
+
else if (ut[i] == NT_ID)
|
1002
|
+
{
|
1003
|
+
const char *str_sym = symid_to_cstr(uref[i]);
|
1004
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
1005
|
+
if (show_offsets)
|
1006
|
+
rbstr_printf(str, ">| ID: %d; SYMBOL: :%s\n", (ID) uref[i], str_sym);
|
1007
|
+
else
|
1008
|
+
rbstr_printf(str, ">| SYMBOL: :%s\n", str_sym);
|
1009
|
+
}
|
1010
|
+
else if (ut[i] == NT_LONG)
|
1011
|
+
{
|
1012
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
1013
|
+
rbstr_printf(str, ">| %"PRIxPTR "\n", (intptr_t) uref[i]);
|
1014
|
+
}
|
1015
|
+
else if (ut[i] == NT_NULL)
|
1016
|
+
{
|
1017
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
1018
|
+
rbstr_printf(str, ">| (NULL)\n");
|
1019
|
+
}
|
1020
|
+
else if (ut[i] == NT_ARGS)
|
1021
|
+
{
|
1022
|
+
#ifdef USE_RB_ARGS_INFO
|
1023
|
+
struct rb_args_info *ainfo;
|
1024
|
+
#endif
|
1025
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
1026
|
+
rbstr_printf(str, ">| ARGS\n");
|
1027
|
+
#ifdef USE_RB_ARGS_INFO
|
1028
|
+
ainfo = node->u3.args;
|
1029
|
+
/* Print generic info about the structure */
|
1030
|
+
PRINT_NODE_TAB; rbstr_printf(str, " PRE_INIT: %16" PRIxPTR "\n", ainfo->pre_init);
|
1031
|
+
PRINT_NODE_TAB; rbstr_printf(str, " POST_INIT: %16" PRIxPTR "\n", ainfo->post_init);
|
1032
|
+
PRINT_NODE_TAB; rbstr_printf(str, " KW_ARGS: %16" PRIxPTR "\n", ainfo->kw_args);
|
1033
|
+
PRINT_NODE_TAB; rbstr_printf(str, " KW_REST_ARG: %16" PRIxPTR "\n", ainfo->kw_rest_arg);
|
1034
|
+
PRINT_NODE_TAB; rbstr_printf(str, " OPT_ARGS: %16" PRIxPTR "\n", ainfo->opt_args);
|
1035
|
+
PRINT_NODE_TAB; rbstr_printf(str, " pre_args_num: %d\n", ainfo->pre_args_num);
|
1036
|
+
PRINT_NODE_TAB; rbstr_printf(str, " post_args_num: %d\n", ainfo->post_args_num);
|
1037
|
+
/* Print information about symbols */
|
1038
|
+
if (show_offsets)
|
1039
|
+
{
|
1040
|
+
PRINT_NODE_TAB; rbstr_printf(str, " first_post_arg: %s (ID %X)\n",
|
1041
|
+
symid_to_cstr(ainfo->first_post_arg), ainfo->first_post_arg);
|
1042
|
+
PRINT_NODE_TAB; rbstr_printf(str, " rest_arg: %s (ID %X)\n",
|
1043
|
+
symid_to_cstr(ainfo->rest_arg), ainfo->rest_arg);
|
1044
|
+
PRINT_NODE_TAB; rbstr_printf(str, " block_arg: %s (ID %X)\n",
|
1045
|
+
symid_to_cstr(ainfo->block_arg), ainfo->block_arg);
|
1046
|
+
}
|
1047
|
+
else
|
1048
|
+
{
|
1049
|
+
PRINT_NODE_TAB; rbstr_printf(str, " first_post_arg: %s\n",
|
1050
|
+
symid_to_cstr(ainfo->first_post_arg));
|
1051
|
+
PRINT_NODE_TAB; rbstr_printf(str, " rest_arg: %s\n",
|
1052
|
+
symid_to_cstr(ainfo->rest_arg));
|
1053
|
+
PRINT_NODE_TAB; rbstr_printf(str, " block_arg: %s\n",
|
1054
|
+
symid_to_cstr(ainfo->block_arg));
|
1055
|
+
}
|
1056
|
+
/* Print information about child nodes */
|
1057
|
+
print_node(str, RNODE(ainfo->pre_init), tab + 2, show_offsets);
|
1058
|
+
print_node(str, RNODE(ainfo->post_init), tab + 2, show_offsets);
|
1059
|
+
print_node(str, RNODE(ainfo->kw_args), tab + 2, show_offsets);
|
1060
|
+
print_node(str, RNODE(ainfo->kw_rest_arg), tab + 2, show_offsets);
|
1061
|
+
print_node(str, RNODE(ainfo->opt_args), tab + 2, show_offsets);
|
1062
|
+
#endif
|
1063
|
+
}
|
1064
|
+
else if (ut[i] == NT_IDTABLE)
|
1065
|
+
{
|
1066
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
1067
|
+
rbstr_printf(str, ">| IDTABLE\n");
|
1068
|
+
}
|
1069
|
+
else if (ut[i] == NT_ENTRY)
|
1070
|
+
{
|
1071
|
+
struct rb_global_entry *gentry;
|
1072
|
+
gentry = (struct rb_global_entry *) uref[i];
|
1073
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
1074
|
+
rbstr_printf(str, ">| [GLOBAL ENTRY PTR=0x%"PRIxPTR" ID=%X]\n", (uintptr_t) gentry->var, gentry->id);
|
1075
|
+
}
|
1076
|
+
else
|
1077
|
+
{
|
1078
|
+
PRINT_NODE_TAB; rbstr_printf(str, " ");
|
1079
|
+
rbstr_printf(str, ">| [UNKNOWN]\n");
|
1080
|
+
}
|
1081
|
+
}
|
1082
|
+
}
|
1083
|
+
|
1084
|
+
|
1085
|
+
|
1086
|
+
void resolve_syms_ords(VALUE data, NODEObjAddresses *relocs)
|
1087
|
+
{
|
1088
|
+
VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("symbols")));
|
1089
|
+
int i;
|
1090
|
+
if (tbl_val == Qnil)
|
1091
|
+
{
|
1092
|
+
rb_raise(rb_eArgError, "Cannot find symbols table");
|
1093
|
+
}
|
1094
|
+
if (TYPE(tbl_val) != T_ARRAY)
|
1095
|
+
{
|
1096
|
+
rb_raise(rb_eArgError, "Symbols table is not an array");
|
1097
|
+
}
|
1098
|
+
relocs->syms_len = RARRAY_LEN(tbl_val);
|
1099
|
+
relocs->syms_adr = ALLOC_N(ID, relocs->syms_len);
|
1100
|
+
for (i = 0; i < relocs->syms_len; i++)
|
1101
|
+
{
|
1102
|
+
VALUE r_sym = RARRAY_PTR(tbl_val)[i];
|
1103
|
+
if (TYPE(r_sym) == T_STRING)
|
1104
|
+
{ /* Created symbol will be immune to garbage collector */
|
1105
|
+
relocs->syms_adr[i] = rb_intern(RSTRING_PTR(r_sym));
|
1106
|
+
}
|
1107
|
+
else if (TYPE(r_sym) == T_FIXNUM)
|
1108
|
+
{
|
1109
|
+
relocs->syms_adr[i] = (ID) FIX2INT(r_sym);
|
1110
|
+
}
|
1111
|
+
else
|
1112
|
+
{
|
1113
|
+
rb_raise(rb_eArgError, "Symbols table is corrupted");
|
1114
|
+
}
|
1115
|
+
}
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
void resolve_lits_ords(VALUE data, NODEObjAddresses *relocs)
|
1119
|
+
{
|
1120
|
+
VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("literals")));
|
1121
|
+
int i;
|
1122
|
+
if (tbl_val == Qnil)
|
1123
|
+
{
|
1124
|
+
rb_raise(rb_eArgError, "Cannot find literals table");
|
1125
|
+
}
|
1126
|
+
if (TYPE(tbl_val) != T_ARRAY)
|
1127
|
+
{
|
1128
|
+
rb_raise(rb_eArgError, "Literals table is not an array");
|
1129
|
+
}
|
1130
|
+
relocs->lits_adr = RARRAY_PTR(tbl_val);
|
1131
|
+
relocs->lits_len = RARRAY_LEN(tbl_val);
|
1132
|
+
/* Mark all symbols as "immortal" (i.e. not collectable
|
1133
|
+
by Ruby GC): some of them can be used in the syntax tree!
|
1134
|
+
See the presentation of Narihiro Nakamura, author of
|
1135
|
+
symbol GC in Ruby 2.x for details
|
1136
|
+
http://www.slideshare.net/authorNari/symbol-gc */
|
1137
|
+
for (i = 0; i < relocs->lits_len; i++)
|
1138
|
+
{
|
1139
|
+
if (TYPE(relocs->lits_adr[i]) == T_SYMBOL)
|
1140
|
+
{
|
1141
|
+
SYM2ID(relocs->lits_adr[i]);
|
1142
|
+
}
|
1143
|
+
}
|
1144
|
+
}
|
1145
|
+
|
1146
|
+
void resolve_gvars_ords(VALUE data, NODEObjAddresses *relocs)
|
1147
|
+
{
|
1148
|
+
VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("global_entries")));
|
1149
|
+
int i;
|
1150
|
+
|
1151
|
+
if (tbl_val == Qnil)
|
1152
|
+
{
|
1153
|
+
rb_raise(rb_eArgError, "Cannot find global entries table");
|
1154
|
+
}
|
1155
|
+
if (TYPE(tbl_val) != T_ARRAY)
|
1156
|
+
{
|
1157
|
+
rb_raise(rb_eArgError, "Global entries table should be an array");
|
1158
|
+
}
|
1159
|
+
relocs->gvars_len = RARRAY_LEN(tbl_val);
|
1160
|
+
relocs->gvars_adr = ALLOC_N(struct rb_global_entry *, relocs->gvars_len);
|
1161
|
+
for (i = 0; i < relocs->gvars_len; i++)
|
1162
|
+
{
|
1163
|
+
int ind = FIX2INT(RARRAY_PTR(tbl_val)[i]);
|
1164
|
+
ID sym = relocs->syms_adr[ind];
|
1165
|
+
relocs->gvars_adr[i] = rb_global_entry(sym);
|
1166
|
+
}
|
1167
|
+
}
|
1168
|
+
|
1169
|
+
|
1170
|
+
void resolve_idtbls_ords(VALUE data, NODEObjAddresses *relocs)
|
1171
|
+
{
|
1172
|
+
VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("id_tables")));
|
1173
|
+
int i, j, idnum;
|
1174
|
+
|
1175
|
+
if (tbl_val == Qnil)
|
1176
|
+
{
|
1177
|
+
rb_raise(rb_eArgError, "Cannot find id_tables entries");
|
1178
|
+
}
|
1179
|
+
relocs->idtbls_len = RARRAY_LEN(tbl_val);
|
1180
|
+
relocs->idtbls_adr = ALLOC_N(ID *, relocs->idtbls_len);
|
1181
|
+
for (i = 0; i < relocs->idtbls_len; i++)
|
1182
|
+
{
|
1183
|
+
VALUE idtbl = RARRAY_PTR(tbl_val)[i];
|
1184
|
+
idnum = RARRAY_LEN(idtbl);
|
1185
|
+
if (idnum == 0)
|
1186
|
+
{ // Empty table: NULL pointer in the address table
|
1187
|
+
relocs->idtbls_adr[i] = NULL;
|
1188
|
+
}
|
1189
|
+
else
|
1190
|
+
{ // Filled table: pointer to dynamic memory
|
1191
|
+
relocs->idtbls_adr[i] = ALLOC_N(ID, idnum + 1);
|
1192
|
+
relocs->idtbls_adr[i][0] = idnum;
|
1193
|
+
for (j = 0; j < idnum; j++)
|
1194
|
+
{
|
1195
|
+
int ind = FIX2INT(RARRAY_PTR(idtbl)[j]);
|
1196
|
+
relocs->idtbls_adr[i][j+1] = relocs->syms_adr[ind];
|
1197
|
+
}
|
1198
|
+
}
|
1199
|
+
}
|
1200
|
+
}
|
1201
|
+
|
1202
|
+
void resolve_nodes_ords(VALUE data, int num_of_nodes, NODEObjAddresses *relocs)
|
1203
|
+
{
|
1204
|
+
int i;
|
1205
|
+
VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("nodes")));
|
1206
|
+
if (tbl_val == Qnil)
|
1207
|
+
{
|
1208
|
+
rb_raise(rb_eArgError, "Cannot find nodes entries");
|
1209
|
+
}
|
1210
|
+
if (TYPE(tbl_val) != T_STRING)
|
1211
|
+
{
|
1212
|
+
rb_raise(rb_eArgError, "Nodes description must be a string");
|
1213
|
+
}
|
1214
|
+
relocs->nodes_adr = ALLOC_N(NODE *, num_of_nodes);
|
1215
|
+
relocs->nodes_len = num_of_nodes;
|
1216
|
+
for (i = 0; i < num_of_nodes; i++)
|
1217
|
+
{
|
1218
|
+
relocs->nodes_adr[i] = (NODE *) NEW_NODE((enum node_type) 0, 0, 0, 0);
|
1219
|
+
}
|
1220
|
+
}
|
1221
|
+
|
1222
|
+
#ifdef USE_RB_ARGS_INFO
|
1223
|
+
void resolve_args_ords(VALUE data, NODEObjAddresses *relocs)
|
1224
|
+
{
|
1225
|
+
int i;
|
1226
|
+
VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("args")));
|
1227
|
+
|
1228
|
+
if (tbl_val == Qnil)
|
1229
|
+
{
|
1230
|
+
rb_raise(rb_eArgError, "Cannot find args entries table");
|
1231
|
+
}
|
1232
|
+
if (TYPE(tbl_val) != T_ARRAY)
|
1233
|
+
{
|
1234
|
+
rb_raise(rb_eArgError, "args description must be an array");
|
1235
|
+
}
|
1236
|
+
relocs->args_len = RARRAY_LEN(tbl_val);
|
1237
|
+
relocs->args_adr = ALLOC_N(struct rb_args_info *, relocs->args_len);
|
1238
|
+
for (i = 0; i < relocs->args_len; i++)
|
1239
|
+
{
|
1240
|
+
int ord;
|
1241
|
+
VALUE ainfo_val, *aiptr;
|
1242
|
+
struct rb_args_info *ainfo;
|
1243
|
+
|
1244
|
+
relocs->args_adr[i] = ALLOC(struct rb_args_info);
|
1245
|
+
ainfo_val = RARRAY_PTR(tbl_val)[i];
|
1246
|
+
aiptr = RARRAY_PTR(ainfo_val);
|
1247
|
+
ainfo = relocs->args_adr[i];
|
1248
|
+
|
1249
|
+
if (TYPE(ainfo_val) != T_ARRAY || RARRAY_LEN(ainfo_val) != 10)
|
1250
|
+
{
|
1251
|
+
rb_raise(rb_eArgError, "args entry %d is corrupted", i);
|
1252
|
+
}
|
1253
|
+
// Load unresolved values
|
1254
|
+
ainfo->pre_init = (NODE *) (uintptr_t) FIX2LONG(aiptr[0]); // Node ordinal
|
1255
|
+
ainfo->post_init = (NODE *) (uintptr_t) FIX2LONG(aiptr[1]); // Node ordinal
|
1256
|
+
ainfo->pre_args_num = FIX2INT(aiptr[2]); // No ordinal resolving
|
1257
|
+
ainfo->post_args_num = FIX2INT(aiptr[3]); // No ordinal resolving
|
1258
|
+
ainfo->first_post_arg = FIX2INT(aiptr[4]); // Symbolic ordinal
|
1259
|
+
ainfo->rest_arg = FIX2INT(aiptr[5]); // Symbolic ordinal
|
1260
|
+
ainfo->block_arg = FIX2INT(aiptr[6]); // Symbolic ordinal
|
1261
|
+
ainfo->kw_args = (NODE *) (uintptr_t) FIX2LONG(aiptr[7]); // Node ordinal
|
1262
|
+
ainfo->kw_rest_arg = (NODE *) (uintptr_t) FIX2LONG(aiptr[8]); // Node ordinal
|
1263
|
+
ainfo->opt_args = (NODE *) (uintptr_t) FIX2LONG(aiptr[9]); // Node ordinal
|
1264
|
+
// Resolve nodes
|
1265
|
+
ord = (int) (((VALUE) ainfo->pre_init) & 0xFFFFFFFF);
|
1266
|
+
if (ord < -1 || ord >= relocs->nodes_len)
|
1267
|
+
rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
|
1268
|
+
ainfo->pre_init = (ord == -1) ? NULL : relocs->nodes_adr[ord];
|
1269
|
+
|
1270
|
+
ord = (int) (((VALUE) ainfo->post_init) & 0xFFFFFFFF);
|
1271
|
+
if (ord < -1 || ord >= relocs->nodes_len)
|
1272
|
+
rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
|
1273
|
+
ainfo->post_init = (ord == -1) ? NULL : relocs->nodes_adr[ord];
|
1274
|
+
|
1275
|
+
ord = (int) (((VALUE) ainfo->kw_args) & 0xFFFFFFFF);
|
1276
|
+
if (ord < -1 || ord >= relocs->nodes_len)
|
1277
|
+
rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
|
1278
|
+
ainfo->kw_args = (ord == -1) ? NULL : relocs->nodes_adr[ord];
|
1279
|
+
|
1280
|
+
ord = (int) (((VALUE) ainfo->kw_rest_arg) & 0xFFFFFFFF);
|
1281
|
+
if (ord < -1 || ord >= relocs->nodes_len)
|
1282
|
+
rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
|
1283
|
+
ainfo->kw_rest_arg = (ord == -1) ? NULL : relocs->nodes_adr[ord];
|
1284
|
+
|
1285
|
+
ord = (int) (((VALUE) ainfo->opt_args) & 0xFFFFFFFF);
|
1286
|
+
if (ord < -1 || ord >= relocs->nodes_len)
|
1287
|
+
rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
|
1288
|
+
ainfo->opt_args = (ord == -1) ? NULL : relocs->nodes_adr[ord];
|
1289
|
+
// Resolve symbolic ordinals
|
1290
|
+
ord = ainfo->first_post_arg;
|
1291
|
+
if (ord < -1 || ord >= relocs->syms_len)
|
1292
|
+
rb_raise(rb_eArgError, "1- Invalid symbol ID ordinal %d", ord);
|
1293
|
+
ainfo->first_post_arg = (ord == -1) ? 0 : relocs->syms_adr[ord];
|
1294
|
+
|
1295
|
+
ord = ainfo->rest_arg;
|
1296
|
+
if (ord < -1 || ord >= relocs->syms_len)
|
1297
|
+
rb_raise(rb_eArgError, "2- Invalid symbol ID ordinal %d", ord);
|
1298
|
+
ainfo->rest_arg = (ord == -1) ? 0 : relocs->syms_adr[ord];
|
1299
|
+
|
1300
|
+
ord = ainfo->block_arg;
|
1301
|
+
if (ord < -1 || ord >= relocs->syms_len)
|
1302
|
+
rb_raise(rb_eArgError, "3- Invalid symbol ID ordinal %d", ord);
|
1303
|
+
ainfo->block_arg = (ord == -1) ? 0 : relocs->syms_adr[ord];
|
1304
|
+
}
|
1305
|
+
}
|
1306
|
+
#endif
|
1307
|
+
|
1308
|
+
/*
|
1309
|
+
* Transforms binary data with nodes descriptions into Ruby AST (i.e.
|
1310
|
+
* ternary tree of nodes). Each node is represented in the next binary format:
|
1311
|
+
*
|
1312
|
+
* [4 bytes -- pointers info] [node flags] [child ORD1] [child ORD2] [child ORD3]
|
1313
|
+
*
|
1314
|
+
* Pointers info:
|
1315
|
+
* BYTE -- child 1 info (bits 7..4 -- ordinal type, bits 3..0 -- ordinal size, bytes)
|
1316
|
+
* BYTE -- child 2 info
|
1317
|
+
* BYTE -- child 3 info
|
1318
|
+
* BYTE -- node flags length, bytes
|
1319
|
+
* Node flags:
|
1320
|
+
* node->flags field packed by bin_to_value function
|
1321
|
+
* child ORDi Ordinal of ith node child packed by bin_to_value_function
|
1322
|
+
* (it will be transformed to the real address in memory, i.e. pointer
|
1323
|
+
* or symbol ID during data loading)
|
1324
|
+
*/
|
1325
|
+
void load_nodes_from_str(VALUE data, NODEObjAddresses *relocs)
|
1326
|
+
{
|
1327
|
+
int i, j;
|
1328
|
+
VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("nodes")));
|
1329
|
+
unsigned char *bin = (unsigned char *) RSTRING_PTR(tbl_val);
|
1330
|
+
NODE *node = NULL;
|
1331
|
+
for (i = 0; i < relocs->nodes_len; i++)
|
1332
|
+
{
|
1333
|
+
int rtypes[4];
|
1334
|
+
VALUE u[3], flags;
|
1335
|
+
// Read data structure info
|
1336
|
+
for (j = 0; j < 4; j++)
|
1337
|
+
rtypes[j] = *bin++;
|
1338
|
+
flags = bin_to_value(bin, rtypes[3]); bin += rtypes[3];
|
1339
|
+
for (j = 0; j < 3; j++)
|
1340
|
+
{
|
1341
|
+
int val_len = (rtypes[j] & 0xF0) >> 4;
|
1342
|
+
u[j] = bin_to_value(bin, val_len);
|
1343
|
+
bin += val_len;
|
1344
|
+
rtypes[j] &= 0x0F;
|
1345
|
+
|
1346
|
+
}
|
1347
|
+
if ((char *)bin - RSTRING_PTR(tbl_val) > RSTRING_LEN(tbl_val))
|
1348
|
+
rb_raise(rb_eArgError, "Nodes binary dump is too short");
|
1349
|
+
// Resolving all addresses
|
1350
|
+
for (j = 0; j < 3; j++)
|
1351
|
+
{
|
1352
|
+
switch(rtypes[j])
|
1353
|
+
{
|
1354
|
+
case VL_RAW: // Do nothing: it is raw data
|
1355
|
+
break;
|
1356
|
+
case VL_NODE:
|
1357
|
+
if (u[j] >= (unsigned int) relocs->nodes_len)
|
1358
|
+
rb_raise(rb_eArgError, "Cannot resolve VL_NODE entry %d", (int) u[j]);
|
1359
|
+
u[j] = (VALUE) relocs->nodes_adr[u[j]];
|
1360
|
+
if (TYPE(u[j]) != T_NODE)
|
1361
|
+
rb_raise(rb_eArgError, "load_nodes_from_str: nodes memory corrupted");
|
1362
|
+
break;
|
1363
|
+
case VL_ID:
|
1364
|
+
if (u[j] >= (unsigned int) relocs->syms_len)
|
1365
|
+
rb_raise(rb_eArgError, "Cannot resolve VL_ID entry %d", (int) u[j]);
|
1366
|
+
u[j] = relocs->syms_adr[u[j]];
|
1367
|
+
break;
|
1368
|
+
case VL_GVAR:
|
1369
|
+
if (u[j] >= (unsigned int) relocs->gvars_len)
|
1370
|
+
rb_raise(rb_eArgError, "Cannot resolve VL_GVAR entry %d", (int) u[j]);
|
1371
|
+
u[j] = (VALUE) relocs->gvars_adr[u[j]];
|
1372
|
+
break;
|
1373
|
+
case VL_IDTABLE:
|
1374
|
+
if (u[j] >= (unsigned int) relocs->idtbls_len)
|
1375
|
+
rb_raise(rb_eArgError, "Cannot resolve VL_IDTABLE entry %d", (int) u[j]);
|
1376
|
+
u[j] = (VALUE) relocs->idtbls_adr[u[j]];
|
1377
|
+
break;
|
1378
|
+
#ifdef USE_RB_ARGS_INFO
|
1379
|
+
case VL_ARGS:
|
1380
|
+
if (u[j] >= (unsigned int) relocs->args_len)
|
1381
|
+
rb_raise(rb_eArgError, "Cannot resolve VL_ARGS entry %d", (int) u[j]);
|
1382
|
+
u[j] = (VALUE) relocs->args_adr[u[j]];
|
1383
|
+
break;
|
1384
|
+
#endif
|
1385
|
+
case VL_LIT:
|
1386
|
+
if (u[j] >= (unsigned int) relocs->lits_len)
|
1387
|
+
rb_raise(rb_eArgError, "Cannot resolve VL_LIT entry %d", (int) u[j]);
|
1388
|
+
u[j] = (VALUE) relocs->lits_adr[u[j]];
|
1389
|
+
break;
|
1390
|
+
default:
|
1391
|
+
rb_raise(rb_eArgError, "Unknown RTYPE %d", rtypes[j]);
|
1392
|
+
}
|
1393
|
+
}
|
1394
|
+
|
1395
|
+
// Fill classic node structure
|
1396
|
+
node = relocs->nodes_adr[i];
|
1397
|
+
#ifdef RESET_GC_FLAGS
|
1398
|
+
flags = flags & (~0x3); // Ruby 1.9.x -- specific thing
|
1399
|
+
#endif
|
1400
|
+
node->flags = (flags << 5) | T_NODE;
|
1401
|
+
node->nd_reserved = 0;
|
1402
|
+
node->u1.value = u[0];
|
1403
|
+
node->u2.value = u[1];
|
1404
|
+
node->u3.value = u[2];
|
1405
|
+
}
|
1406
|
+
}
|
1407
|
+
|
1408
|
+
/*
|
1409
|
+
* Returns the value of string hash field using symbolic key
|
1410
|
+
*/
|
1411
|
+
static VALUE get_hash_strfield(VALUE hash, const char *idtxt)
|
1412
|
+
{
|
1413
|
+
VALUE str = rb_hash_aref(hash, ID2SYM(rb_intern(idtxt)));
|
1414
|
+
if (TYPE(str) != T_STRING)
|
1415
|
+
{
|
1416
|
+
rb_raise(rb_eArgError, "Hash field %s is not a string", idtxt);
|
1417
|
+
return Qnil;
|
1418
|
+
}
|
1419
|
+
else
|
1420
|
+
{
|
1421
|
+
return str;
|
1422
|
+
}
|
1423
|
+
}
|
1424
|
+
|
1425
|
+
/*
|
1426
|
+
* Check validity of node hash representation signatures ("magic" values)
|
1427
|
+
*/
|
1428
|
+
static VALUE check_hash_magic(VALUE data)
|
1429
|
+
{
|
1430
|
+
VALUE val, refval;
|
1431
|
+
// MAGIC signature must be valid
|
1432
|
+
val = get_hash_strfield(data, "MAGIC");
|
1433
|
+
if (strcmp(NODEMARSHAL_MAGIC, RSTRING_PTR(val)))
|
1434
|
+
rb_raise(rb_eArgError, "Bad value of MAGIC signature");
|
1435
|
+
// RUBY_PLATFORM signature must match the current platform
|
1436
|
+
val = get_hash_strfield(data, "RUBY_PLATFORM");
|
1437
|
+
refval = rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM"));
|
1438
|
+
if (strcmp(RSTRING_PTR(refval), RSTRING_PTR(val)))
|
1439
|
+
rb_raise(rb_eArgError, "Incompatible RUBY_PLATFORM value %s", RSTRING_PTR(val));
|
1440
|
+
// RUBY_VERSION signature must match the used Ruby interpreter
|
1441
|
+
val = get_hash_strfield(data, "RUBY_VERSION");
|
1442
|
+
refval = rb_const_get(rb_cObject, rb_intern("RUBY_VERSION"));
|
1443
|
+
if (strcmp(RSTRING_PTR(refval), RSTRING_PTR(val)))
|
1444
|
+
rb_raise(rb_eArgError, "Incompatible RUBY_VERSION value %s", RSTRING_PTR(val));
|
1445
|
+
return Qtrue;
|
1446
|
+
}
|
1447
|
+
|
1448
|
+
/*
|
1449
|
+
* Part 5. C-to-Ruby interface
|
1450
|
+
*
|
1451
|
+
*/
|
1452
|
+
|
1453
|
+
/*
|
1454
|
+
* Restore Ruby node from the binary blob (dump)
|
1455
|
+
*/
|
1456
|
+
static VALUE m_nodedump_from_memory(VALUE self, VALUE dump)
|
1457
|
+
{
|
1458
|
+
VALUE cMarshal, data, val, val_relocs;
|
1459
|
+
VALUE gc_was_disabled;
|
1460
|
+
int num_of_nodes;
|
1461
|
+
NODEObjAddresses *relocs;
|
1462
|
+
/* DISABLE GARBAGE COLLECTOR (required for stable loading
|
1463
|
+
of large node trees */
|
1464
|
+
gc_was_disabled = rb_gc_disable();
|
1465
|
+
/* Wrap struct for relocations */
|
1466
|
+
val_relocs = Data_Make_Struct(cNodeObjAddresses, NODEObjAddresses,
|
1467
|
+
NULL, NODEObjAddresses_free, relocs); // This data envelope cannot exist without NODE
|
1468
|
+
/* Load and unpack our dump */
|
1469
|
+
cMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
|
1470
|
+
data = rb_funcall(cMarshal, rb_intern("load"), 1, dump);
|
1471
|
+
if (TYPE(data) != T_HASH)
|
1472
|
+
{
|
1473
|
+
rb_raise(rb_eArgError, "Input dump is corrupted");
|
1474
|
+
}
|
1475
|
+
val = rb_hash_aref(data, ID2SYM(rb_intern("num_of_nodes")));
|
1476
|
+
if (val == Qnil)
|
1477
|
+
{
|
1478
|
+
rb_raise(rb_eArgError, "num_of_nodes not found");
|
1479
|
+
}
|
1480
|
+
else
|
1481
|
+
{
|
1482
|
+
num_of_nodes = FIX2INT(val);
|
1483
|
+
}
|
1484
|
+
/* Check "magic" signature and platform identifiers */
|
1485
|
+
check_hash_magic(data);
|
1486
|
+
/* Get the information about the source file that was compiled to the node */
|
1487
|
+
// a) node name
|
1488
|
+
val = rb_hash_aref(data, ID2SYM(rb_intern("nodename")));
|
1489
|
+
if (val == Qnil || TYPE(val) == T_STRING)
|
1490
|
+
rb_iv_set(self, "@nodename", val);
|
1491
|
+
else
|
1492
|
+
rb_raise(rb_eArgError, "nodename value is corrupted");
|
1493
|
+
// b) file name
|
1494
|
+
val = rb_hash_aref(data, ID2SYM(rb_intern("filename")));
|
1495
|
+
if (val == Qnil || TYPE(val) == T_STRING)
|
1496
|
+
rb_iv_set(self, "@filename", val);
|
1497
|
+
else
|
1498
|
+
rb_raise(rb_eArgError, "filename value is corrupted");
|
1499
|
+
// c) file path
|
1500
|
+
val = rb_hash_aref(data, ID2SYM(rb_intern("filepath")));
|
1501
|
+
if (val == Qnil || TYPE(val) == T_STRING)
|
1502
|
+
rb_iv_set(self, "@filepath", val);
|
1503
|
+
else
|
1504
|
+
rb_raise(rb_eArgError, "filepath value is corrupted");
|
1505
|
+
/* Load all required data */
|
1506
|
+
resolve_syms_ords(data, relocs); // Symbols
|
1507
|
+
resolve_lits_ords(data, relocs); // Literals
|
1508
|
+
resolve_gvars_ords(data, relocs); // Global entries (with symbol ID resolving)
|
1509
|
+
resolve_idtbls_ords(data, relocs); // Identifiers tables (with symbol ID resolving)
|
1510
|
+
resolve_nodes_ords(data, num_of_nodes, relocs); // Allocate memory for all nodes
|
1511
|
+
#ifdef USE_RB_ARGS_INFO
|
1512
|
+
resolve_args_ords(data, relocs); // Load args entries with symbols ID and nodes resolving
|
1513
|
+
#endif
|
1514
|
+
load_nodes_from_str(data, relocs);
|
1515
|
+
/* Save the loaded node tree and collect garbage */
|
1516
|
+
rb_iv_set(self, "@node", (VALUE) relocs->nodes_adr[0]);
|
1517
|
+
rb_iv_set(self, "@num_of_nodes", INT2FIX(num_of_nodes));
|
1518
|
+
rb_iv_set(self, "@obj_addresses", val_relocs);
|
1519
|
+
if (gc_was_disabled == Qfalse)
|
1520
|
+
{
|
1521
|
+
rb_gc_enable();
|
1522
|
+
rb_gc_start();
|
1523
|
+
}
|
1524
|
+
return self;
|
1525
|
+
}
|
1526
|
+
|
1527
|
+
|
1528
|
+
/*
|
1529
|
+
* call-seq:
|
1530
|
+
* obj.symbols
|
1531
|
+
*
|
1532
|
+
* Return array with the list of symbols
|
1533
|
+
*/
|
1534
|
+
static VALUE m_nodedump_symbols(VALUE self)
|
1535
|
+
{
|
1536
|
+
int i;
|
1537
|
+
VALUE val_relocs, val_nodeinfo, syms;
|
1538
|
+
// Variant 1: node loaded from file
|
1539
|
+
val_relocs = rb_iv_get(self, "@obj_addresses");
|
1540
|
+
if (val_relocs != Qnil)
|
1541
|
+
{
|
1542
|
+
NODEObjAddresses *relocs;
|
1543
|
+
Data_Get_Struct(val_relocs, NODEObjAddresses, relocs);
|
1544
|
+
syms = rb_ary_new();
|
1545
|
+
for (i = 0; i < relocs->syms_len; i++)
|
1546
|
+
rb_ary_push(syms, ID2SYM(relocs->syms_adr[i]));
|
1547
|
+
return syms;
|
1548
|
+
}
|
1549
|
+
// Variant 2: node saved to file (parsed from memory)
|
1550
|
+
val_nodeinfo = rb_iv_get(self, "@nodeinfo");
|
1551
|
+
if (val_nodeinfo != Qnil)
|
1552
|
+
{
|
1553
|
+
NODEInfo *ninfo;
|
1554
|
+
VALUE *ary;
|
1555
|
+
Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
|
1556
|
+
syms = rb_funcall(ninfo->syms.vals, rb_intern("values"), 0);
|
1557
|
+
ary = RARRAY_PTR(syms);
|
1558
|
+
for (i = 0; i < RARRAY_LEN(syms); i++)
|
1559
|
+
{
|
1560
|
+
ary[i] = rb_funcall(ary[i], rb_intern("to_sym"), 0);
|
1561
|
+
}
|
1562
|
+
return syms;
|
1563
|
+
}
|
1564
|
+
rb_raise(rb_eArgError, "Symbol information not initialized. Run to_hash before reading.");
|
1565
|
+
}
|
1566
|
+
|
1567
|
+
/*
|
1568
|
+
* call-seq:
|
1569
|
+
* obj.change_symbol(old_sym, new_sym)
|
1570
|
+
*
|
1571
|
+
* Replace one symbol by another (to be used for code obfuscation)
|
1572
|
+
* - +old_sym+ -- String that contains symbol name to be replaced
|
1573
|
+
* - +new_sym+ -- String that contains new name of the symbol
|
1574
|
+
*/
|
1575
|
+
static VALUE m_nodedump_change_symbol(VALUE self, VALUE old_sym, VALUE new_sym)
|
1576
|
+
{
|
1577
|
+
VALUE val_nodehash = rb_iv_get(self, "@nodehash");
|
1578
|
+
VALUE syms, key;
|
1579
|
+
// Check if node is position-independent
|
1580
|
+
// (i.e. with initialized NODEInfo structure that contains
|
1581
|
+
// relocations for symbols)
|
1582
|
+
if (val_nodehash == Qnil)
|
1583
|
+
rb_raise(rb_eArgError, "This node is not preparsed into Hash");
|
1584
|
+
// Check data types of the input array
|
1585
|
+
if (TYPE(old_sym) != T_STRING)
|
1586
|
+
{
|
1587
|
+
rb_raise(rb_eArgError, "old_sym argument must be a string");
|
1588
|
+
}
|
1589
|
+
if (TYPE(new_sym) != T_STRING)
|
1590
|
+
{
|
1591
|
+
rb_raise(rb_eArgError, "new_sym argument must be a string");
|
1592
|
+
}
|
1593
|
+
// Get the symbol table from the Hash
|
1594
|
+
syms = rb_hash_aref(val_nodehash, ID2SYM(rb_intern("symbols")));
|
1595
|
+
if (syms == Qnil)
|
1596
|
+
rb_raise(rb_eArgError, "Preparsed hash has no :symbols field");
|
1597
|
+
// Check if new_sym is present in the symbol table
|
1598
|
+
key = rb_funcall(syms, rb_intern("find_index"), 1, new_sym);
|
1599
|
+
if (key != Qnil)
|
1600
|
+
{
|
1601
|
+
rb_raise(rb_eArgError, "new_sym value must be absent in table of symbols");
|
1602
|
+
}
|
1603
|
+
// Change the symbol in the preparsed Hash
|
1604
|
+
key = rb_funcall(syms, rb_intern("find_index"), 1, old_sym);
|
1605
|
+
if (key == Qnil)
|
1606
|
+
return Qnil;
|
1607
|
+
RARRAY_PTR(syms)[FIX2INT(key)] = new_sym;
|
1608
|
+
return self;
|
1609
|
+
}
|
1610
|
+
|
1611
|
+
/*
|
1612
|
+
* Return array with the list of literals
|
1613
|
+
*/
|
1614
|
+
static VALUE m_nodedump_literals(VALUE self)
|
1615
|
+
{
|
1616
|
+
int i;
|
1617
|
+
VALUE val_relocs, val_nodeinfo, lits;
|
1618
|
+
// Variant 1: node loaded from file. It uses NODEObjAddresses struct
|
1619
|
+
// with the results of Ruby NODE structure parsing.
|
1620
|
+
val_relocs = rb_iv_get(self, "@obj_addresses");
|
1621
|
+
if (val_relocs != Qnil)
|
1622
|
+
{
|
1623
|
+
NODEObjAddresses *relocs;
|
1624
|
+
|
1625
|
+
Data_Get_Struct(val_relocs, NODEObjAddresses, relocs);
|
1626
|
+
lits = rb_ary_new();
|
1627
|
+
for (i = 0; i < relocs->lits_len; i++)
|
1628
|
+
{
|
1629
|
+
VALUE val = relocs->lits_adr[i];
|
1630
|
+
int t = TYPE(val);
|
1631
|
+
if (t != T_SYMBOL && t != T_FLOAT && t != T_FIXNUM)
|
1632
|
+
val = rb_funcall(val, rb_intern("dup"), 0);
|
1633
|
+
rb_ary_push(lits, val);
|
1634
|
+
}
|
1635
|
+
return lits;
|
1636
|
+
}
|
1637
|
+
// Variant 2: node saved to file (parsed from memory). It uses
|
1638
|
+
// NODEInfo struct that is initialized during node dump parsing.
|
1639
|
+
val_nodeinfo = rb_iv_get(self, "@nodeinfo");
|
1640
|
+
if (val_nodeinfo != Qnil)
|
1641
|
+
{
|
1642
|
+
NODEInfo *ninfo;
|
1643
|
+
VALUE *ary;
|
1644
|
+
Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
|
1645
|
+
lits = rb_funcall(ninfo->lits.vals, rb_intern("values"), 0);
|
1646
|
+
ary = RARRAY_PTR(lits);
|
1647
|
+
for (i = 0; i < RARRAY_LEN(lits); i++)
|
1648
|
+
{
|
1649
|
+
int t = TYPE(ary[i]);
|
1650
|
+
if (t != T_SYMBOL && t != T_FLOAT && t != T_FIXNUM)
|
1651
|
+
ary[i] = rb_funcall(ary[i], rb_intern("dup"), 0);
|
1652
|
+
}
|
1653
|
+
return lits;
|
1654
|
+
}
|
1655
|
+
rb_raise(rb_eArgError, "Literals information not initialized. Run to_hash before reading.");
|
1656
|
+
}
|
1657
|
+
|
1658
|
+
/*
|
1659
|
+
* Update the array with the list of literals
|
1660
|
+
* (to be used for code obfuscation)
|
1661
|
+
* Warning! This function is a stub!
|
1662
|
+
*/
|
1663
|
+
static VALUE m_nodedump_change_literal(VALUE self, VALUE old_lit, VALUE new_lit)
|
1664
|
+
{
|
1665
|
+
/* TO BE IMPLEMENTED */
|
1666
|
+
return self;
|
1667
|
+
}
|
1668
|
+
|
1669
|
+
|
1670
|
+
/*
|
1671
|
+
* call-seq:
|
1672
|
+
* obj.compile
|
1673
|
+
*
|
1674
|
+
* Creates the RubyVM::InstructionSequence object from the node
|
1675
|
+
*/
|
1676
|
+
static VALUE m_nodedump_compile(VALUE self)
|
1677
|
+
{
|
1678
|
+
NODE *node = RNODE(rb_iv_get(self, "@node"));
|
1679
|
+
VALUE nodename = rb_iv_get(self, "@nodename");
|
1680
|
+
VALUE filename = rb_iv_get(self, "@filename");
|
1681
|
+
VALUE filepath = rb_iv_get(self, "@filepath");
|
1682
|
+
#ifndef WITH_RB_ISEQW_NEW
|
1683
|
+
/* For Pre-2.3 */
|
1684
|
+
return rb_iseq_new_top(node, nodename, filename, filepath, Qfalse);
|
1685
|
+
#else
|
1686
|
+
/* For Ruby 2.3 */
|
1687
|
+
return rb_iseqw_new(rb_iseq_new_top(node, nodename, filename, filepath, Qfalse));
|
1688
|
+
#endif
|
1689
|
+
}
|
1690
|
+
|
1691
|
+
/*
|
1692
|
+
* Parses Ruby file with the source code and saves the node
|
1693
|
+
*/
|
1694
|
+
static VALUE m_nodedump_from_source(VALUE self, VALUE file)
|
1695
|
+
{
|
1696
|
+
VALUE line = INT2FIX(1), f, node, filepath, gc_was_disabled;
|
1697
|
+
const char *fname;
|
1698
|
+
|
1699
|
+
gc_was_disabled = rb_gc_disable();
|
1700
|
+
rb_secure(1);
|
1701
|
+
FilePathValue(file);
|
1702
|
+
fname = StringValueCStr(file);
|
1703
|
+
/* Remember information about the file */
|
1704
|
+
rb_iv_set(self, "@nodename", rb_str_new2("<main>"));
|
1705
|
+
rb_iv_set(self, "@filename", file);
|
1706
|
+
filepath = rb_funcall(rb_cFile, rb_intern("realpath"), 1, file); // Envelope for rb_realpath_internal
|
1707
|
+
rb_iv_set(self, "@filepath", filepath);
|
1708
|
+
/* Create node from the source */
|
1709
|
+
f = rb_file_open_str(file, "r");
|
1710
|
+
node = (VALUE) rb_compile_file(fname, f, NUM2INT(line));
|
1711
|
+
rb_iv_set(self, "@node", node);
|
1712
|
+
if ((void *) node == NULL)
|
1713
|
+
{
|
1714
|
+
rb_raise(rb_eArgError, "Error during string parsing");
|
1715
|
+
}
|
1716
|
+
if (gc_was_disabled == Qfalse)
|
1717
|
+
{
|
1718
|
+
rb_gc_enable();
|
1719
|
+
}
|
1720
|
+
return self;
|
1721
|
+
}
|
1722
|
+
|
1723
|
+
/*
|
1724
|
+
* Parses Ruby string with the source code and saves the node
|
1725
|
+
*/
|
1726
|
+
static VALUE m_nodedump_from_string(VALUE self, VALUE str)
|
1727
|
+
{
|
1728
|
+
VALUE line = INT2FIX(1), node, gc_was_disabled;
|
1729
|
+
const char *fname = "STRING";
|
1730
|
+
Check_Type(str, T_STRING);
|
1731
|
+
gc_was_disabled = rb_gc_disable();
|
1732
|
+
rb_secure(1);
|
1733
|
+
/* Create empty information about the file */
|
1734
|
+
rb_iv_set(self, "@nodename", rb_str_new2("<main>"));
|
1735
|
+
if (RUBY_API_VERSION_MAJOR == 1)
|
1736
|
+
{ /* For Ruby 1.9.x */
|
1737
|
+
rb_iv_set(self, "@filename", Qnil);
|
1738
|
+
rb_iv_set(self, "@filepath", Qnil);
|
1739
|
+
}
|
1740
|
+
else
|
1741
|
+
{ /* For Ruby 2.x */
|
1742
|
+
rb_iv_set(self, "@filename", rb_str_new2("<compiled>"));
|
1743
|
+
rb_iv_set(self, "@filepath", rb_str_new2("<compiled>"));
|
1744
|
+
}
|
1745
|
+
/* Create node from the string */
|
1746
|
+
node = (VALUE) rb_compile_string(fname, str, NUM2INT(line));
|
1747
|
+
rb_iv_set(self, "@node", node);
|
1748
|
+
if (gc_was_disabled == Qfalse)
|
1749
|
+
{
|
1750
|
+
rb_gc_enable();
|
1751
|
+
rb_gc_start();
|
1752
|
+
}
|
1753
|
+
if ((void *) node == NULL)
|
1754
|
+
{
|
1755
|
+
rb_raise(rb_eArgError, "Error during string parsing");
|
1756
|
+
}
|
1757
|
+
return self;
|
1758
|
+
}
|
1759
|
+
|
1760
|
+
/*
|
1761
|
+
* call-seq:
|
1762
|
+
* obj.new(:srcfile, filename) # Will load source file from the disk
|
1763
|
+
* obj.new(:binfile, filename) # Will load file with node binary dump from the disk
|
1764
|
+
* obj.new(:srcmemory, srcstr) # Will load source code from the string
|
1765
|
+
* obj.new(:binmemory, binstr) # Will load node binary dump from the string
|
1766
|
+
*
|
1767
|
+
* Creates NodeMarshal class example from the source code or dumped
|
1768
|
+
* syntax tree (NODEs), i.e. preparsed and packed source code. Created
|
1769
|
+
* object can be used either for code execution or for saving it
|
1770
|
+
* in the preparsed form (useful for code obfuscation/protection)
|
1771
|
+
*/
|
1772
|
+
static VALUE m_nodedump_init(VALUE self, VALUE source, VALUE info)
|
1773
|
+
{
|
1774
|
+
ID id_usr;
|
1775
|
+
rb_iv_set(self, "@show_offsets", Qfalse);
|
1776
|
+
Check_Type(source, T_SYMBOL);
|
1777
|
+
id_usr = SYM2ID(source);
|
1778
|
+
if (id_usr == rb_intern("srcfile"))
|
1779
|
+
{
|
1780
|
+
return m_nodedump_from_source(self, info);
|
1781
|
+
}
|
1782
|
+
else if (id_usr == rb_intern("srcmemory"))
|
1783
|
+
{
|
1784
|
+
return m_nodedump_from_string(self, info);
|
1785
|
+
}
|
1786
|
+
else if (id_usr == rb_intern("binmemory"))
|
1787
|
+
{
|
1788
|
+
return m_nodedump_from_memory(self, info);
|
1789
|
+
}
|
1790
|
+
else if (id_usr == rb_intern("binfile"))
|
1791
|
+
{
|
1792
|
+
VALUE cFile = rb_const_get(rb_cObject, rb_intern("File"));
|
1793
|
+
VALUE bin = rb_funcall(cFile, rb_intern("binread"), 1, info);
|
1794
|
+
return m_nodedump_from_memory(self, bin);
|
1795
|
+
}
|
1796
|
+
else
|
1797
|
+
{
|
1798
|
+
rb_raise(rb_eArgError, "Invalid source type (it must be :srcfile, :srcmemory, :binmemory of :binfile)");
|
1799
|
+
}
|
1800
|
+
return Qnil;
|
1801
|
+
}
|
1802
|
+
|
1803
|
+
/*
|
1804
|
+
* call-seq:
|
1805
|
+
* obj.dump_tree
|
1806
|
+
*
|
1807
|
+
* Transforms Ruby syntax tree (NODE) to the String using
|
1808
|
+
* +rb_parser_dump_tree+ function from +node.c+ (see Ruby source code).
|
1809
|
+
*/
|
1810
|
+
static VALUE m_nodedump_parser_dump_tree(VALUE self)
|
1811
|
+
{
|
1812
|
+
NODE *node = RNODE(rb_iv_get(self, "@node"));
|
1813
|
+
return rb_parser_dump_tree(node, 0);
|
1814
|
+
}
|
1815
|
+
|
1816
|
+
/*
|
1817
|
+
* call-seq:
|
1818
|
+
* obj.dump_tree_short
|
1819
|
+
*
|
1820
|
+
* Transforms Ruby syntax tree (NODE) to the String using custom function
|
1821
|
+
* instead of +rb_parser_dump_tree+ function.
|
1822
|
+
*
|
1823
|
+
* See also #show_offsets, #show_offsets=
|
1824
|
+
*/
|
1825
|
+
static VALUE m_nodedump_dump_tree_short(VALUE self)
|
1826
|
+
{
|
1827
|
+
VALUE str = rb_str_new2(""); // Output string
|
1828
|
+
NODE *node = RNODE(rb_iv_get(self, "@node"));
|
1829
|
+
int show_offsets = (rb_iv_get(self, "@show_offsets") == Qtrue) ? 1 : 0;
|
1830
|
+
print_node(str, node, 0, show_offsets);
|
1831
|
+
return str;
|
1832
|
+
}
|
1833
|
+
|
1834
|
+
/*
|
1835
|
+
* call-seq:
|
1836
|
+
* obj.show_offsets
|
1837
|
+
*
|
1838
|
+
* Returns show_offsets property (used by NodeMarshal#dump_tree_short)
|
1839
|
+
* It can be either true or false
|
1840
|
+
*/
|
1841
|
+
static VALUE m_nodedump_show_offsets(VALUE self)
|
1842
|
+
{
|
1843
|
+
return rb_iv_get(self, "@show_offsets");
|
1844
|
+
}
|
1845
|
+
|
1846
|
+
/*
|
1847
|
+
* call-seq:
|
1848
|
+
* obj.show_offsets=
|
1849
|
+
*
|
1850
|
+
* Sets show_offsets property (used by NodeMarshal#dump_tree_short)
|
1851
|
+
* It can be either true or false
|
1852
|
+
*/
|
1853
|
+
static VALUE m_nodedump_set_show_offsets(VALUE self, VALUE value)
|
1854
|
+
{
|
1855
|
+
if (value != Qtrue && value != Qfalse)
|
1856
|
+
{
|
1857
|
+
rb_raise(rb_eArgError, "show_offsets property must be either true or false");
|
1858
|
+
}
|
1859
|
+
return rb_iv_set(self, "@show_offsets", value);
|
1860
|
+
}
|
1861
|
+
|
1862
|
+
|
1863
|
+
/*
|
1864
|
+
* call-seq:
|
1865
|
+
* obj.to_hash
|
1866
|
+
*
|
1867
|
+
* Converts NodeMarshal class example to the hash that contains full
|
1868
|
+
* and independent from data structures memory addresses information.
|
1869
|
+
* Format of the obtained hash depends on used platform (especially
|
1870
|
+
* size of the pointer) and Ruby version.
|
1871
|
+
*
|
1872
|
+
* <b>Format of the hash</b>
|
1873
|
+
*
|
1874
|
+
* <i>Part 1: Signatures</i>
|
1875
|
+
*
|
1876
|
+
* - <tt>MAGIC</tt> -- NODEMARSHAL11
|
1877
|
+
* - <tt>RUBY_PLATFORM</tt> -- saved <tt>RUBY_PLATFORM</tt> constant value
|
1878
|
+
* - <tt>RUBY_VERSION</tt> -- saved <tt>RUBY_VERSION</tt> constant value
|
1879
|
+
*
|
1880
|
+
* <i>Part 2: Program loadable elements.</i>
|
1881
|
+
*
|
1882
|
+
* All loadable elements are arrays. Index of the array element means
|
1883
|
+
* its identifier that is used in the node tree.
|
1884
|
+
*
|
1885
|
+
* - <tt>literals</tt> -- program literals (strings, ranges etc.)
|
1886
|
+
* - <tt>symbols</tt> -- program symbols (values have either String or Fixnum
|
1887
|
+
* data type; numbers are used for symbols that cannot be represented as strings)
|
1888
|
+
* - <tt>global_entries</tt> -- global variables information
|
1889
|
+
* - <tt>id_tables</tt> -- array of arrays. Each array contains symbols IDs
|
1890
|
+
* - <tt>args</tt> -- information about code block argument(s)
|
1891
|
+
*
|
1892
|
+
* <i>Part 3: Nodes information</i>
|
1893
|
+
* - <tt>nodes</tt> -- string that contains binary encoded information
|
1894
|
+
* about the nodes
|
1895
|
+
* - <tt>num_of_nodes</tt> -- number of nodes in the <tt>nodes</tt> field
|
1896
|
+
* - <tt>nodename</tt> -- name of the node (usually "<main>")
|
1897
|
+
* - <tt>filename</tt> -- name (without path) of .rb file used for the node generation
|
1898
|
+
* - <tt>filepath</tt> -- name (with full path) of .rb file used for the node generation
|
1899
|
+
*/
|
1900
|
+
static VALUE m_nodedump_to_hash(VALUE self)
|
1901
|
+
{
|
1902
|
+
NODE *node = RNODE(rb_iv_get(self, "@node"));
|
1903
|
+
NODEInfo *info;
|
1904
|
+
VALUE ans, num, val_info, gc_was_disabled;
|
1905
|
+
// DISABLE GARBAGE COLLECTOR (important for dumping)
|
1906
|
+
gc_was_disabled = rb_gc_disable();
|
1907
|
+
// Convert the node to the form with relocs (i.e. the information about node)
|
1908
|
+
// if such form is not present
|
1909
|
+
val_info = rb_iv_get(self, "@nodeinfo");
|
1910
|
+
if (val_info == Qnil)
|
1911
|
+
{
|
1912
|
+
val_info = Data_Make_Struct(cNodeInfo, NODEInfo,
|
1913
|
+
NODEInfo_mark, NODEInfo_free, info); // This data envelope cannot exist without NODE
|
1914
|
+
NODEInfo_init(info);
|
1915
|
+
rb_iv_set(self, "@nodeinfo", val_info);
|
1916
|
+
num = INT2FIX(count_num_of_nodes(node, node, info));
|
1917
|
+
rb_iv_set(self, "@nodeinfo_num_of_nodes", num);
|
1918
|
+
// Convert node to NODEInfo structure
|
1919
|
+
ans = NODEInfo_toHash(info);
|
1920
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("num_of_nodes")), num);
|
1921
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("nodename")), rb_iv_get(self, "@nodename"));
|
1922
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("filename")), rb_iv_get(self, "@filename"));
|
1923
|
+
rb_hash_aset(ans, ID2SYM(rb_intern("filepath")), rb_iv_get(self, "@filepath"));
|
1924
|
+
rb_iv_set(self, "@nodehash", ans);
|
1925
|
+
}
|
1926
|
+
else
|
1927
|
+
{
|
1928
|
+
ans = rb_iv_get(self, "@nodehash");
|
1929
|
+
}
|
1930
|
+
// ENABLE GARBAGE COLLECTOR (important for dumping)
|
1931
|
+
if (gc_was_disabled == Qfalse)
|
1932
|
+
{
|
1933
|
+
rb_gc_enable();
|
1934
|
+
}
|
1935
|
+
return ans;
|
1936
|
+
}
|
1937
|
+
|
1938
|
+
|
1939
|
+
VALUE m_node_to_ary(NODE *node)
|
1940
|
+
{
|
1941
|
+
int i, type, ut[3];
|
1942
|
+
VALUE uref[3];
|
1943
|
+
VALUE entry = rb_ary_new();
|
1944
|
+
/* Special case: NULL node */
|
1945
|
+
if (node == NULL)
|
1946
|
+
{
|
1947
|
+
return Qnil;
|
1948
|
+
}
|
1949
|
+
/* Save node name */
|
1950
|
+
type = nd_type(node);
|
1951
|
+
rb_ary_push(entry, ID2SYM(rb_intern(ruby_node_name(type))));
|
1952
|
+
|
1953
|
+
ut[0] = nodes_ctbl[type * 3];
|
1954
|
+
ut[1] = nodes_ctbl[type * 3 + 1];
|
1955
|
+
ut[2] = nodes_ctbl[type * 3 + 2];
|
1956
|
+
|
1957
|
+
uref[0] = node->u1.value;
|
1958
|
+
uref[1] = node->u2.value;
|
1959
|
+
uref[2] = node->u3.value;
|
1960
|
+
|
1961
|
+
|
1962
|
+
for (i = 0; i < 3; i++)
|
1963
|
+
{
|
1964
|
+
if (ut[i] == NT_NODE)
|
1965
|
+
{
|
1966
|
+
if (nd_type(node) != NODE_OP_ASGN2 || i != 2)
|
1967
|
+
{
|
1968
|
+
rb_ary_push(entry, m_node_to_ary(RNODE(uref[i])));
|
1969
|
+
}
|
1970
|
+
else
|
1971
|
+
{
|
1972
|
+
VALUE child = rb_ary_new();
|
1973
|
+
if (ut[i] != 0 && TYPE(ut[i]) != T_NODE)
|
1974
|
+
rb_raise(rb_eArgError, "print_node: broken node 0x%s", RSTRING_PTR(value_to_str(ut[i])));
|
1975
|
+
rb_ary_push(child, ID2SYM(rb_intern("NODE_OP_ASGN2")));
|
1976
|
+
rb_ary_push(child, LONG2NUM((intptr_t) RNODE(uref[i])->u1.value));
|
1977
|
+
rb_ary_push(child, LONG2NUM((intptr_t) RNODE(uref[i])->u2.value));
|
1978
|
+
rb_ary_push(child, LONG2NUM((intptr_t) RNODE(uref[i])->u3.value));
|
1979
|
+
rb_ary_push(entry, child);
|
1980
|
+
}
|
1981
|
+
}
|
1982
|
+
else if (ut[i] == NT_VALUE)
|
1983
|
+
{
|
1984
|
+
rb_ary_push(entry, uref[i]);
|
1985
|
+
}
|
1986
|
+
else if (ut[i] == NT_ID)
|
1987
|
+
{
|
1988
|
+
rb_ary_push(entry, ID2SYM( (ID) uref[i]));
|
1989
|
+
}
|
1990
|
+
else if (ut[i] == NT_LONG)
|
1991
|
+
{
|
1992
|
+
rb_ary_push(entry, LONG2NUM( (intptr_t) uref[i]));
|
1993
|
+
}
|
1994
|
+
else if (ut[i] == NT_NULL)
|
1995
|
+
{
|
1996
|
+
rb_ary_push(entry, Qnil);
|
1997
|
+
}
|
1998
|
+
else if (ut[i] == NT_ARGS)
|
1999
|
+
{
|
2000
|
+
VALUE rargs = rb_hash_new();
|
2001
|
+
VALUE rargs_env = rb_ary_new();
|
2002
|
+
#ifdef USE_RB_ARGS_INFO
|
2003
|
+
ID id;
|
2004
|
+
struct rb_args_info *args = (void *) uref[i];
|
2005
|
+
|
2006
|
+
rb_hash_aset(rargs, ID2SYM(rb_intern("pre_init")), m_node_to_ary(args->pre_init));
|
2007
|
+
rb_hash_aset(rargs, ID2SYM(rb_intern("post_init")), m_node_to_ary(args->post_init));
|
2008
|
+
|
2009
|
+
id = args->first_post_arg;
|
2010
|
+
rb_hash_aset(rargs, ID2SYM(rb_intern("first_post_arg")), (id) ? ID2SYM(id) : Qnil);
|
2011
|
+
id = args->rest_arg;
|
2012
|
+
rb_hash_aset(rargs, ID2SYM(rb_intern("rest_arg")), (id) ? ID2SYM(id) : Qnil);
|
2013
|
+
id = args->block_arg;
|
2014
|
+
rb_hash_aset(rargs, ID2SYM(rb_intern("block_arg")), (id) ? ID2SYM(id) : Qnil);
|
2015
|
+
|
2016
|
+
rb_hash_aset(rargs, ID2SYM(rb_intern("kw_args")), m_node_to_ary(args->kw_args));
|
2017
|
+
rb_hash_aset(rargs, ID2SYM(rb_intern("kw_rest_arg")), m_node_to_ary(args->kw_rest_arg));
|
2018
|
+
rb_hash_aset(rargs, ID2SYM(rb_intern("opt_args")), m_node_to_ary(args->opt_args));
|
2019
|
+
#endif
|
2020
|
+
rb_ary_push(rargs_env, ID2SYM(rb_intern("ARGS")));
|
2021
|
+
rb_ary_push(rargs_env, rargs);
|
2022
|
+
rb_ary_push(entry, rargs_env);
|
2023
|
+
}
|
2024
|
+
else if (ut[i] == NT_IDTABLE)
|
2025
|
+
{
|
2026
|
+
VALUE ridtbl = rb_ary_new();
|
2027
|
+
VALUE idtbl_ary = rb_ary_new();
|
2028
|
+
int j, len;
|
2029
|
+
|
2030
|
+
ID *idtbl = (ID *) uref[i];
|
2031
|
+
len = (uref[i]) ? *idtbl++ : 0;
|
2032
|
+
for (j = 0; j < len; j++)
|
2033
|
+
{
|
2034
|
+
ID sym = *idtbl++;
|
2035
|
+
VALUE val = ID2SYM(sym);
|
2036
|
+
rb_ary_push(idtbl_ary, val);
|
2037
|
+
}
|
2038
|
+
rb_ary_push(ridtbl, ID2SYM(rb_intern("IDTABLE")));
|
2039
|
+
rb_ary_push(ridtbl, idtbl_ary);
|
2040
|
+
rb_ary_push(entry, ridtbl);
|
2041
|
+
}
|
2042
|
+
else if (ut[i] == NT_ENTRY)
|
2043
|
+
{
|
2044
|
+
struct rb_global_entry *gentry;
|
2045
|
+
gentry = (struct rb_global_entry *) uref[i];
|
2046
|
+
rb_ary_push(entry, ID2SYM(gentry->id));
|
2047
|
+
}
|
2048
|
+
else
|
2049
|
+
{
|
2050
|
+
rb_ary_push(entry, ID2SYM(rb_intern("UNKNOWN")));
|
2051
|
+
}
|
2052
|
+
}
|
2053
|
+
return entry;
|
2054
|
+
}
|
2055
|
+
|
2056
|
+
/*
|
2057
|
+
* call-seq:
|
2058
|
+
* obj.to_a
|
2059
|
+
*
|
2060
|
+
* Converts node to the array (mainly to allow exploration of AST
|
2061
|
+
* by the user). It shows information about rb_args_info and
|
2062
|
+
* ID *tbl that are not displayed by NodeMarshal#dump_tree and
|
2063
|
+
* NodeMarshal#dump_tree_short.
|
2064
|
+
*/
|
2065
|
+
static VALUE m_nodedump_to_a(VALUE self)
|
2066
|
+
{
|
2067
|
+
NODE *node = RNODE(rb_iv_get(self, "@node"));
|
2068
|
+
VALUE gc_was_disabled = rb_gc_disable();
|
2069
|
+
VALUE ary = m_node_to_ary(node);
|
2070
|
+
if (gc_was_disabled == Qfalse)
|
2071
|
+
{
|
2072
|
+
rb_gc_enable();
|
2073
|
+
}
|
2074
|
+
return ary;
|
2075
|
+
}
|
2076
|
+
|
2077
|
+
|
2078
|
+
/*
|
2079
|
+
* call-seq:
|
2080
|
+
* obj.to_bin
|
2081
|
+
*
|
2082
|
+
* Converts NodeMarshal class example to the binary string that
|
2083
|
+
* can be saved to the file and used for loading the node from the file.
|
2084
|
+
* Format of the obtained binary dump depends on used platform (especially
|
2085
|
+
* size of the pointer) and Ruby version.
|
2086
|
+
*/
|
2087
|
+
static VALUE m_nodedump_to_bin(VALUE self)
|
2088
|
+
{
|
2089
|
+
VALUE hash = m_nodedump_to_hash(self);
|
2090
|
+
VALUE cMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
|
2091
|
+
return rb_funcall(cMarshal, rb_intern("dump"), 1, hash);
|
2092
|
+
}
|
2093
|
+
|
2094
|
+
/*
|
2095
|
+
* Gives the information about the node
|
2096
|
+
*/
|
2097
|
+
static VALUE m_nodedump_inspect(VALUE self)
|
2098
|
+
{
|
2099
|
+
static char str[1024], buf[512];
|
2100
|
+
VALUE num_of_nodes, nodename, filepath, filename;
|
2101
|
+
VALUE val_obj_addresses, val_nodeinfo;
|
2102
|
+
// Get generic information about node
|
2103
|
+
num_of_nodes = rb_iv_get(self, "@num_of_nodes");
|
2104
|
+
nodename = rb_iv_get(self, "@nodename");
|
2105
|
+
filepath = rb_iv_get(self, "@filepath");
|
2106
|
+
filename = rb_iv_get(self, "@filename");
|
2107
|
+
// Generate string with generic information about node
|
2108
|
+
sprintf(str,
|
2109
|
+
"----- NodeMarshal:0x%"PRIxPTR"\n"
|
2110
|
+
" num_of_nodes: %d\n nodename: %s\n filepath: %s\n filename: %s\n",
|
2111
|
+
(uintptr_t) (self),
|
2112
|
+
(num_of_nodes == Qnil) ? -1 : FIX2INT(num_of_nodes),
|
2113
|
+
(nodename == Qnil) ? "nil" : RSTRING_PTR(nodename),
|
2114
|
+
(filepath == Qnil) ? "nil" : RSTRING_PTR(filepath),
|
2115
|
+
(filename == Qnil) ? "nil" : RSTRING_PTR(filename)
|
2116
|
+
);
|
2117
|
+
// Check if the information about node struct is available
|
2118
|
+
val_nodeinfo = rb_iv_get(self, "@nodeinfo");
|
2119
|
+
val_obj_addresses = rb_iv_get(self, "@obj_addresses");
|
2120
|
+
if (val_nodeinfo == Qnil && val_obj_addresses == Qnil)
|
2121
|
+
{
|
2122
|
+
m_nodedump_to_hash(self);
|
2123
|
+
val_nodeinfo = rb_iv_get(self, "@nodeinfo");
|
2124
|
+
}
|
2125
|
+
// Information about preparsed node
|
2126
|
+
// a) NODEInfo struct
|
2127
|
+
if (val_nodeinfo == Qnil)
|
2128
|
+
{
|
2129
|
+
sprintf(buf, " NODEInfo struct is empty\n");
|
2130
|
+
}
|
2131
|
+
else
|
2132
|
+
{
|
2133
|
+
NODEInfo *ninfo;
|
2134
|
+
Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
|
2135
|
+
sprintf(buf,
|
2136
|
+
" NODEInfo struct:\n"
|
2137
|
+
" syms hash len (Symbols): %d\n"
|
2138
|
+
" lits hash len (Literals): %d\n"
|
2139
|
+
" idtabs hash len (ID tables): %d\n"
|
2140
|
+
" gentries hash len (Global vars): %d\n"
|
2141
|
+
" nodes hash len (Nodes): %d\n"
|
2142
|
+
" pnodes hash len (Parent nodes): %d\n"
|
2143
|
+
#ifdef USE_RB_ARGS_INFO
|
2144
|
+
" args hash len (args info): %d\n"
|
2145
|
+
#endif
|
2146
|
+
,
|
2147
|
+
FIX2INT(rb_funcall(ninfo->syms.vals, rb_intern("length"), 0)),
|
2148
|
+
FIX2INT(rb_funcall(ninfo->lits.vals, rb_intern("length"), 0)),
|
2149
|
+
FIX2INT(rb_funcall(ninfo->idtabs.vals, rb_intern("length"), 0)),
|
2150
|
+
FIX2INT(rb_funcall(ninfo->gentries.vals, rb_intern("length"), 0)),
|
2151
|
+
FIX2INT(rb_funcall(ninfo->nodes.vals, rb_intern("length"), 0)),
|
2152
|
+
FIX2INT(rb_funcall(ninfo->pnodes.vals, rb_intern("length"), 0))
|
2153
|
+
#ifdef USE_RB_ARGS_INFO
|
2154
|
+
,
|
2155
|
+
FIX2INT(rb_funcall(ninfo->args.vals, rb_intern("length"), 0))
|
2156
|
+
#endif
|
2157
|
+
);
|
2158
|
+
}
|
2159
|
+
strcat(str, buf);
|
2160
|
+
// b) NODEObjAddresses struct
|
2161
|
+
if (val_obj_addresses == Qnil)
|
2162
|
+
{
|
2163
|
+
sprintf(buf, " NODEObjAddresses struct is empty\n");
|
2164
|
+
}
|
2165
|
+
else
|
2166
|
+
{
|
2167
|
+
NODEObjAddresses *objadr;
|
2168
|
+
Data_Get_Struct(val_obj_addresses, NODEObjAddresses, objadr);
|
2169
|
+
sprintf(buf,
|
2170
|
+
" NODEObjAddresses struct:\n"
|
2171
|
+
" syms_len (Num of symbols): %d\n"
|
2172
|
+
" lits_len (Num of literals): %d\n"
|
2173
|
+
" idtbls_len (Num of ID tables): %d\n"
|
2174
|
+
" gvars_len (Num of global vars): %d\n"
|
2175
|
+
" nodes_len (Num of nodes): %d\n"
|
2176
|
+
#ifdef USE_RB_ARGS_INFO
|
2177
|
+
" args_len: (Num of args info): %d\n"
|
2178
|
+
#endif
|
2179
|
+
, objadr->syms_len, objadr->lits_len,
|
2180
|
+
objadr->idtbls_len, objadr->gvars_len,
|
2181
|
+
objadr->nodes_len
|
2182
|
+
#ifdef USE_RB_ARGS_INFO
|
2183
|
+
, objadr->args_len
|
2184
|
+
#endif
|
2185
|
+
);
|
2186
|
+
}
|
2187
|
+
strcat(str, buf);
|
2188
|
+
strcat(str, "------------------\n");
|
2189
|
+
// Generate output string
|
2190
|
+
return rb_str_new2(str);
|
2191
|
+
}
|
2192
|
+
|
2193
|
+
/*
|
2194
|
+
* Returns node name (usually <main>)
|
2195
|
+
*/
|
2196
|
+
static VALUE m_nodedump_nodename(VALUE self)
|
2197
|
+
{
|
2198
|
+
return rb_funcall(rb_iv_get(self, "@nodename"), rb_intern("dup"), 0);
|
2199
|
+
}
|
2200
|
+
|
2201
|
+
/*
|
2202
|
+
* Returns name of file that was used for node generation and will be used
|
2203
|
+
* by YARV (or nil/<compiled> if a string of code was used)
|
2204
|
+
*/
|
2205
|
+
static VALUE m_nodedump_filename(VALUE self)
|
2206
|
+
{
|
2207
|
+
return rb_funcall(rb_iv_get(self, "@filename"), rb_intern("dup"), 0);
|
2208
|
+
}
|
2209
|
+
|
2210
|
+
/*
|
2211
|
+
* Sets name of file that was used for node generation and will be used
|
2212
|
+
* by YARV (or nil/<compiled> if a string of code was used)
|
2213
|
+
*/
|
2214
|
+
static VALUE m_nodedump_set_filename(VALUE self, VALUE val)
|
2215
|
+
{
|
2216
|
+
if (val != Qnil)
|
2217
|
+
{
|
2218
|
+
Check_Type(val, T_STRING);
|
2219
|
+
rb_iv_set(self, "@filename", rb_funcall(val, rb_intern("dup"), 0));
|
2220
|
+
}
|
2221
|
+
else
|
2222
|
+
{
|
2223
|
+
rb_iv_set(self, "@filename", Qnil);
|
2224
|
+
}
|
2225
|
+
return self;
|
2226
|
+
}
|
2227
|
+
|
2228
|
+
/*
|
2229
|
+
* Returns path of file that was used for node generation and will be used
|
2230
|
+
* by YARV (or nil/<compiled> if a string of code was used)
|
2231
|
+
*/
|
2232
|
+
static VALUE m_nodedump_filepath(VALUE self)
|
2233
|
+
{
|
2234
|
+
return rb_funcall(rb_iv_get(self, "@filepath"), rb_intern("dup"), 0);
|
2235
|
+
}
|
2236
|
+
|
2237
|
+
/*
|
2238
|
+
* call-seq:
|
2239
|
+
* obj.filepath=value
|
2240
|
+
*
|
2241
|
+
* Sets the path of file that was used for node generation and will
|
2242
|
+
* be used by YARV (or nil/<compiled> if a string of code was used)
|
2243
|
+
*/
|
2244
|
+
static VALUE m_nodedump_set_filepath(VALUE self, VALUE val)
|
2245
|
+
{
|
2246
|
+
if (val != Qnil)
|
2247
|
+
{
|
2248
|
+
Check_Type(val, T_STRING);
|
2249
|
+
rb_iv_set(self, "@filepath", rb_funcall(val, rb_intern("dup"), 0));
|
2250
|
+
}
|
2251
|
+
else
|
2252
|
+
{
|
2253
|
+
rb_iv_set(self, "@filepath", Qnil);
|
2254
|
+
}
|
2255
|
+
return self;
|
2256
|
+
}
|
2257
|
+
|
2258
|
+
/*
|
2259
|
+
* call-seq:
|
2260
|
+
* NodeMarshal.base85r_encode(input) -> output
|
2261
|
+
*
|
2262
|
+
* Encode arbitrary binary string to the ASCII string
|
2263
|
+
* using modified version of BASE85 (useful for obfuscation
|
2264
|
+
* of .rb source files)
|
2265
|
+
*/
|
2266
|
+
static VALUE m_base85r_encode(VALUE obj, VALUE input)
|
2267
|
+
{
|
2268
|
+
return base85r_encode(input);
|
2269
|
+
}
|
2270
|
+
|
2271
|
+
/*
|
2272
|
+
* call-seq:
|
2273
|
+
* NodeMarshal.base85r_decode(input) -> output
|
2274
|
+
*
|
2275
|
+
* Decode ASCII string in the modified BASE85 format
|
2276
|
+
* to the binary string (useful for obfuscation of .rb
|
2277
|
+
* source files)
|
2278
|
+
*/
|
2279
|
+
static VALUE m_base85r_decode(VALUE obj, VALUE input)
|
2280
|
+
{
|
2281
|
+
return base85r_decode(input);
|
2282
|
+
}
|
2283
|
+
|
2284
|
+
/* call-seq:
|
2285
|
+
* obj.to_text
|
2286
|
+
*
|
2287
|
+
* Converts NodeMarshal class example to the text string (modified Base85 encoding) that
|
2288
|
+
* can be saved to the file and used for loading the node from the file.
|
2289
|
+
* Format of the obtained binary dump depends on used platform (especially
|
2290
|
+
* size of the pointer) and Ruby version.
|
2291
|
+
*/
|
2292
|
+
static VALUE m_nodedump_to_text(VALUE self)
|
2293
|
+
{
|
2294
|
+
VALUE bin = m_nodedump_to_bin(self);
|
2295
|
+
return base85r_encode(bin);
|
2296
|
+
}
|
2297
|
+
|
2298
|
+
/*
|
2299
|
+
* Returns node object
|
2300
|
+
*/
|
2301
|
+
static VALUE m_nodedump_node(VALUE self)
|
2302
|
+
{
|
2303
|
+
return rb_iv_get(self, "@node");
|
2304
|
+
}
|
2305
|
+
|
2306
|
+
/*
|
2307
|
+
* This class can load and save Ruby code in the form of the
|
2308
|
+
* platform-dependent syntax tree (made of NODEs). Such function
|
2309
|
+
* allows to hide the source code from users. Main features:
|
2310
|
+
*
|
2311
|
+
* - Irreversible transformation of Ruby source code to the syntax tree
|
2312
|
+
* - Representation of syntax tree in binary form dependent from the platform and Ruby version
|
2313
|
+
* - Simple options for node inspection
|
2314
|
+
* - Ruby 1.9.3, 2.2.x and 2.3.x support
|
2315
|
+
* - Subroutines for custom code obfuscation
|
2316
|
+
*/
|
2317
|
+
void Init_nodemarshal()
|
2318
|
+
{
|
2319
|
+
static VALUE cNodeMarshal;
|
2320
|
+
init_nodes_table(nodes_ctbl, NODES_CTBL_SIZE);
|
2321
|
+
base85r_init_tables();
|
2322
|
+
|
2323
|
+
cNodeMarshal = rb_define_class("NodeMarshal", rb_cObject);
|
2324
|
+
rb_define_singleton_method(cNodeMarshal, "base85r_encode", RUBY_METHOD_FUNC(m_base85r_encode), 1);
|
2325
|
+
rb_define_singleton_method(cNodeMarshal, "base85r_decode", RUBY_METHOD_FUNC(m_base85r_decode), 1);
|
2326
|
+
|
2327
|
+
rb_define_method(cNodeMarshal, "initialize", RUBY_METHOD_FUNC(m_nodedump_init), 2);
|
2328
|
+
rb_define_method(cNodeMarshal, "to_hash", RUBY_METHOD_FUNC(m_nodedump_to_hash), 0);
|
2329
|
+
rb_define_method(cNodeMarshal, "to_h", RUBY_METHOD_FUNC(m_nodedump_to_hash), 0);
|
2330
|
+
rb_define_method(cNodeMarshal, "to_bin", RUBY_METHOD_FUNC(m_nodedump_to_bin), 0);
|
2331
|
+
rb_define_method(cNodeMarshal, "to_text", RUBY_METHOD_FUNC(m_nodedump_to_text), 0);
|
2332
|
+
rb_define_method(cNodeMarshal, "to_a", RUBY_METHOD_FUNC(m_nodedump_to_a), 0);
|
2333
|
+
rb_define_method(cNodeMarshal, "to_ary", RUBY_METHOD_FUNC(m_nodedump_to_a), 0);
|
2334
|
+
rb_define_method(cNodeMarshal, "dump_tree", RUBY_METHOD_FUNC(m_nodedump_parser_dump_tree), 0);
|
2335
|
+
rb_define_method(cNodeMarshal, "dump_tree_short", RUBY_METHOD_FUNC(m_nodedump_dump_tree_short), 0);
|
2336
|
+
rb_define_method(cNodeMarshal, "compile", RUBY_METHOD_FUNC(m_nodedump_compile), 0);
|
2337
|
+
rb_define_method(cNodeMarshal, "show_offsets", RUBY_METHOD_FUNC(m_nodedump_show_offsets), 0);
|
2338
|
+
rb_define_method(cNodeMarshal, "show_offsets=", RUBY_METHOD_FUNC(m_nodedump_set_show_offsets), 1);
|
2339
|
+
// Methods for working with the information about the node
|
2340
|
+
// a) literals, symbols, generic information
|
2341
|
+
rb_define_method(cNodeMarshal, "symbols", RUBY_METHOD_FUNC(m_nodedump_symbols), 0);
|
2342
|
+
rb_define_method(cNodeMarshal, "change_symbol", RUBY_METHOD_FUNC(m_nodedump_change_symbol), 2);
|
2343
|
+
rb_define_method(cNodeMarshal, "literals", RUBY_METHOD_FUNC(m_nodedump_literals), 0);
|
2344
|
+
rb_define_method(cNodeMarshal, "change_literal", RUBY_METHOD_FUNC(m_nodedump_change_literal), 2);
|
2345
|
+
rb_define_method(cNodeMarshal, "inspect", RUBY_METHOD_FUNC(m_nodedump_inspect), 0);
|
2346
|
+
rb_define_method(cNodeMarshal, "node", RUBY_METHOD_FUNC(m_nodedump_node), 0);
|
2347
|
+
// b) node and file names
|
2348
|
+
rb_define_method(cNodeMarshal, "nodename", RUBY_METHOD_FUNC(m_nodedump_nodename), 0);
|
2349
|
+
rb_define_method(cNodeMarshal, "filename", RUBY_METHOD_FUNC(m_nodedump_filename), 0);
|
2350
|
+
rb_define_method(cNodeMarshal, "filename=", RUBY_METHOD_FUNC(m_nodedump_set_filename), 1);
|
2351
|
+
rb_define_method(cNodeMarshal, "filepath", RUBY_METHOD_FUNC(m_nodedump_filepath), 0);
|
2352
|
+
rb_define_method(cNodeMarshal, "filepath=", RUBY_METHOD_FUNC(m_nodedump_set_filepath), 1);
|
2353
|
+
// C structure wrappers
|
2354
|
+
cNodeObjAddresses = rb_define_class("NodeObjAddresses", rb_cObject);
|
2355
|
+
cNodeInfo = rb_define_class("NodeInfo", rb_cObject);
|
2356
|
+
}
|