node-marshal 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,338 @@
1
+ /*
2
+ * Simpilfied node.h file from Ruby 2.2.1 source code
3
+ * Copyright (C) 1993-2007 Yukihiro Matsumoto
4
+ */
5
+ #ifndef RUBY_NODE_H
6
+ #define RUBY_NODE_H 1
7
+
8
+ #if defined(__cplusplus)
9
+ extern "C" {
10
+ #if 0
11
+ } /* satisfy cc-mode */
12
+ #endif
13
+ #endif
14
+
15
+ enum node_type {
16
+ NODE_SCOPE,
17
+ #define NODE_SCOPE NODE_SCOPE
18
+ NODE_BLOCK,
19
+ #define NODE_BLOCK NODE_BLOCK
20
+ NODE_IF,
21
+ #define NODE_IF NODE_IF
22
+ NODE_CASE,
23
+ #define NODE_CASE NODE_CASE
24
+ NODE_WHEN,
25
+ #define NODE_WHEN NODE_WHEN
26
+ NODE_OPT_N,
27
+ #define NODE_OPT_N NODE_OPT_N
28
+ NODE_WHILE,
29
+ #define NODE_WHILE NODE_WHILE
30
+ NODE_UNTIL,
31
+ #define NODE_UNTIL NODE_UNTIL
32
+ NODE_ITER,
33
+ #define NODE_ITER NODE_ITER
34
+ NODE_FOR,
35
+ #define NODE_FOR NODE_FOR
36
+ NODE_BREAK,
37
+ #define NODE_BREAK NODE_BREAK
38
+ NODE_NEXT,
39
+ #define NODE_NEXT NODE_NEXT
40
+ NODE_REDO,
41
+ #define NODE_REDO NODE_REDO
42
+ NODE_RETRY,
43
+ #define NODE_RETRY NODE_RETRY
44
+ NODE_BEGIN,
45
+ #define NODE_BEGIN NODE_BEGIN
46
+ NODE_RESCUE,
47
+ #define NODE_RESCUE NODE_RESCUE
48
+ NODE_RESBODY,
49
+ #define NODE_RESBODY NODE_RESBODY
50
+ NODE_ENSURE,
51
+ #define NODE_ENSURE NODE_ENSURE
52
+ NODE_AND,
53
+ #define NODE_AND NODE_AND
54
+ NODE_OR,
55
+ #define NODE_OR NODE_OR
56
+ NODE_MASGN,
57
+ #define NODE_MASGN NODE_MASGN
58
+ NODE_LASGN,
59
+ #define NODE_LASGN NODE_LASGN
60
+ NODE_DASGN,
61
+ #define NODE_DASGN NODE_DASGN
62
+ NODE_DASGN_CURR,
63
+ #define NODE_DASGN_CURR NODE_DASGN_CURR
64
+ NODE_GASGN,
65
+ #define NODE_GASGN NODE_GASGN
66
+ NODE_IASGN,
67
+ #define NODE_IASGN NODE_IASGN
68
+ NODE_IASGN2,
69
+ #define NODE_IASGN2 NODE_IASGN2
70
+ NODE_CDECL,
71
+ #define NODE_CDECL NODE_CDECL
72
+ NODE_CVASGN,
73
+ #define NODE_CVASGN NODE_CVASGN
74
+ NODE_CVDECL,
75
+ #define NODE_CVDECL NODE_CVDECL
76
+ NODE_OP_ASGN1,
77
+ #define NODE_OP_ASGN1 NODE_OP_ASGN1
78
+ NODE_OP_ASGN2,
79
+ #define NODE_OP_ASGN2 NODE_OP_ASGN2
80
+ NODE_OP_ASGN_AND,
81
+ #define NODE_OP_ASGN_AND NODE_OP_ASGN_AND
82
+ NODE_OP_ASGN_OR,
83
+ #define NODE_OP_ASGN_OR NODE_OP_ASGN_OR
84
+ NODE_OP_CDECL,
85
+ #define NODE_OP_CDECL NODE_OP_CDECL
86
+ NODE_CALL,
87
+ #define NODE_CALL NODE_CALL
88
+ NODE_FCALL,
89
+ #define NODE_FCALL NODE_FCALL
90
+ NODE_VCALL,
91
+ #define NODE_VCALL NODE_VCALL
92
+ NODE_SUPER,
93
+ #define NODE_SUPER NODE_SUPER
94
+ NODE_ZSUPER,
95
+ #define NODE_ZSUPER NODE_ZSUPER
96
+ NODE_ARRAY,
97
+ #define NODE_ARRAY NODE_ARRAY
98
+ NODE_ZARRAY,
99
+ #define NODE_ZARRAY NODE_ZARRAY
100
+ NODE_VALUES,
101
+ #define NODE_VALUES NODE_VALUES
102
+ NODE_HASH,
103
+ #define NODE_HASH NODE_HASH
104
+ NODE_RETURN,
105
+ #define NODE_RETURN NODE_RETURN
106
+ NODE_YIELD,
107
+ #define NODE_YIELD NODE_YIELD
108
+ NODE_LVAR,
109
+ #define NODE_LVAR NODE_LVAR
110
+ NODE_DVAR,
111
+ #define NODE_DVAR NODE_DVAR
112
+ NODE_GVAR,
113
+ #define NODE_GVAR NODE_GVAR
114
+ NODE_IVAR,
115
+ #define NODE_IVAR NODE_IVAR
116
+ NODE_CONST,
117
+ #define NODE_CONST NODE_CONST
118
+ NODE_CVAR,
119
+ #define NODE_CVAR NODE_CVAR
120
+ NODE_NTH_REF,
121
+ #define NODE_NTH_REF NODE_NTH_REF
122
+ NODE_BACK_REF,
123
+ #define NODE_BACK_REF NODE_BACK_REF
124
+ NODE_MATCH,
125
+ #define NODE_MATCH NODE_MATCH
126
+ NODE_MATCH2,
127
+ #define NODE_MATCH2 NODE_MATCH2
128
+ NODE_MATCH3,
129
+ #define NODE_MATCH3 NODE_MATCH3
130
+ NODE_LIT,
131
+ #define NODE_LIT NODE_LIT
132
+ NODE_STR,
133
+ #define NODE_STR NODE_STR
134
+ NODE_DSTR,
135
+ #define NODE_DSTR NODE_DSTR
136
+ NODE_XSTR,
137
+ #define NODE_XSTR NODE_XSTR
138
+ NODE_DXSTR,
139
+ #define NODE_DXSTR NODE_DXSTR
140
+ NODE_EVSTR,
141
+ #define NODE_EVSTR NODE_EVSTR
142
+ NODE_DREGX,
143
+ #define NODE_DREGX NODE_DREGX
144
+ NODE_DREGX_ONCE,
145
+ #define NODE_DREGX_ONCE NODE_DREGX_ONCE
146
+ NODE_ARGS,
147
+ #define NODE_ARGS NODE_ARGS
148
+ NODE_ARGS_AUX,
149
+ #define NODE_ARGS_AUX NODE_ARGS_AUX
150
+ NODE_OPT_ARG,
151
+ #define NODE_OPT_ARG NODE_OPT_ARG
152
+ NODE_KW_ARG,
153
+ #define NODE_KW_ARG NODE_KW_ARG
154
+ NODE_POSTARG,
155
+ #define NODE_POSTARG NODE_POSTARG
156
+ NODE_ARGSCAT,
157
+ #define NODE_ARGSCAT NODE_ARGSCAT
158
+ NODE_ARGSPUSH,
159
+ #define NODE_ARGSPUSH NODE_ARGSPUSH
160
+ NODE_SPLAT,
161
+ #define NODE_SPLAT NODE_SPLAT
162
+ NODE_TO_ARY,
163
+ #define NODE_TO_ARY NODE_TO_ARY
164
+ NODE_BLOCK_ARG,
165
+ #define NODE_BLOCK_ARG NODE_BLOCK_ARG
166
+ NODE_BLOCK_PASS,
167
+ #define NODE_BLOCK_PASS NODE_BLOCK_PASS
168
+ NODE_DEFN,
169
+ #define NODE_DEFN NODE_DEFN
170
+ NODE_DEFS,
171
+ #define NODE_DEFS NODE_DEFS
172
+ NODE_ALIAS,
173
+ #define NODE_ALIAS NODE_ALIAS
174
+ NODE_VALIAS,
175
+ #define NODE_VALIAS NODE_VALIAS
176
+ NODE_UNDEF,
177
+ #define NODE_UNDEF NODE_UNDEF
178
+ NODE_CLASS,
179
+ #define NODE_CLASS NODE_CLASS
180
+ NODE_MODULE,
181
+ #define NODE_MODULE NODE_MODULE
182
+ NODE_SCLASS,
183
+ #define NODE_SCLASS NODE_SCLASS
184
+ NODE_COLON2,
185
+ #define NODE_COLON2 NODE_COLON2
186
+ NODE_COLON3,
187
+ #define NODE_COLON3 NODE_COLON3
188
+ NODE_CREF,
189
+ #define NODE_CREF NODE_CREF
190
+ NODE_DOT2,
191
+ #define NODE_DOT2 NODE_DOT2
192
+ NODE_DOT3,
193
+ #define NODE_DOT3 NODE_DOT3
194
+ NODE_FLIP2,
195
+ #define NODE_FLIP2 NODE_FLIP2
196
+ NODE_FLIP3,
197
+ #define NODE_FLIP3 NODE_FLIP3
198
+ NODE_SELF,
199
+ #define NODE_SELF NODE_SELF
200
+ NODE_NIL,
201
+ #define NODE_NIL NODE_NIL
202
+ NODE_TRUE,
203
+ #define NODE_TRUE NODE_TRUE
204
+ NODE_FALSE,
205
+ #define NODE_FALSE NODE_FALSE
206
+ NODE_ERRINFO,
207
+ #define NODE_ERRINFO NODE_ERRINFO
208
+ NODE_DEFINED,
209
+ #define NODE_DEFINED NODE_DEFINED
210
+ NODE_POSTEXE,
211
+ #define NODE_POSTEXE NODE_POSTEXE
212
+ NODE_ALLOCA,
213
+ #define NODE_ALLOCA NODE_ALLOCA
214
+ NODE_BMETHOD,
215
+ #define NODE_BMETHOD NODE_BMETHOD
216
+ NODE_MEMO,
217
+ #define NODE_MEMO NODE_MEMO
218
+ NODE_IFUNC,
219
+ #define NODE_IFUNC NODE_IFUNC
220
+ NODE_DSYM,
221
+ #define NODE_DSYM NODE_DSYM
222
+ NODE_ATTRASGN,
223
+ #define NODE_ATTRASGN NODE_ATTRASGN
224
+ NODE_PRELUDE,
225
+ #define NODE_PRELUDE NODE_PRELUDE
226
+ NODE_LAMBDA,
227
+ #define NODE_LAMBDA NODE_LAMBDA
228
+ NODE_LAST
229
+ #define NODE_LAST NODE_LAST
230
+ };
231
+
232
+ typedef struct RNode {
233
+ VALUE flags;
234
+ VALUE nd_reserved; /* ex nd_file */
235
+ union {
236
+ struct RNode *node;
237
+ ID id;
238
+ VALUE value;
239
+ VALUE (*cfunc)(ANYARGS);
240
+ ID *tbl;
241
+ } u1;
242
+ union {
243
+ struct RNode *node;
244
+ ID id;
245
+ long argc;
246
+ VALUE value;
247
+ } u2;
248
+ union {
249
+ struct RNode *node;
250
+ ID id;
251
+ long state;
252
+ struct rb_global_entry *entry;
253
+ struct rb_args_info *args;
254
+ long cnt;
255
+ VALUE value;
256
+ } u3;
257
+ } NODE;
258
+
259
+ #define RNODE(obj) (R_CAST(RNode)(obj))
260
+
261
+ /* FL : 0..4: T_TYPES, 5: KEEP_WB, 6: PROMOTED, 7: FINALIZE, 8: TAINT, 9: UNTRUSTERD, 10: EXIVAR, 11: FREEZE */
262
+ /* NODE_FL: 0..4: T_TYPES, 5: KEEP_WB, 6: PROMOTED, 7: NODE_FL_NEWLINE|NODE_FL_CREF_PUSHED_BY_EVAL,
263
+ * 8..14: nd_type,
264
+ * 15..: nd_line or
265
+ * 15: NODE_FL_CREF_PUSHED_BY_EVAL
266
+ * 16: NODE_FL_CREF_OMOD_SHARED
267
+ */
268
+ #define NODE_FL_NEWLINE (((VALUE)1)<<7)
269
+ #define NODE_FL_CREF_PUSHED_BY_EVAL (((VALUE)1)<<15)
270
+ #define NODE_FL_CREF_OMOD_SHARED (((VALUE)1)<<16)
271
+
272
+ #define NODE_TYPESHIFT 8
273
+ #define NODE_TYPEMASK (((VALUE)0x7f)<<NODE_TYPESHIFT)
274
+
275
+ #define nd_type(n) ((int) (((RNODE(n))->flags & NODE_TYPEMASK)>>NODE_TYPESHIFT))
276
+ #define nd_set_type(n,t) \
277
+ RNODE(n)->flags=((RNODE(n)->flags&~NODE_TYPEMASK)|((((unsigned long)(t))<<NODE_TYPESHIFT)&NODE_TYPEMASK))
278
+
279
+ #define NODE_LSHIFT (NODE_TYPESHIFT+7)
280
+ #define NODE_LMASK (((SIGNED_VALUE)1<<(sizeof(VALUE)*CHAR_BIT-NODE_LSHIFT))-1)
281
+ #define nd_line(n) (int)(RNODE(n)->flags>>NODE_LSHIFT)
282
+ #define nd_set_line(n,l) \
283
+ RNODE(n)->flags=((RNODE(n)->flags&~((VALUE)(-1)<<NODE_LSHIFT))|((VALUE)((l)&NODE_LMASK)<<NODE_LSHIFT))
284
+
285
+ #define NEW_NODE(t,a0,a1,a2) rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
286
+ RUBY_SYMBOL_EXPORT_BEGIN
287
+
288
+ VALUE rb_parser_dump_tree(NODE *node, int comment);
289
+
290
+ NODE *rb_compile_cstr(const char*, const char*, int, int);
291
+ NODE *rb_compile_string(const char*, VALUE, int);
292
+ NODE *rb_compile_file(const char*, VALUE, int);
293
+
294
+ NODE *rb_node_newnode(enum node_type,VALUE,VALUE,VALUE);
295
+ NODE *rb_node_newnode_longlife(enum node_type,VALUE,VALUE,VALUE);
296
+ void rb_gc_free_node(VALUE obj);
297
+ size_t rb_node_memsize(VALUE obj);
298
+ VALUE rb_gc_mark_node(NODE *obj);
299
+
300
+ struct rb_global_entry {
301
+ struct rb_global_variable *var;
302
+ ID id;
303
+ };
304
+
305
+ struct rb_global_entry *rb_global_entry(ID);
306
+ VALUE rb_gvar_get(struct rb_global_entry *);
307
+ VALUE rb_gvar_set(struct rb_global_entry *, VALUE);
308
+ VALUE rb_gvar_defined(struct rb_global_entry *);
309
+ const struct kwtable *rb_reserved_word(const char *, unsigned int);
310
+
311
+ struct rb_args_info {
312
+ NODE *pre_init;
313
+ NODE *post_init;
314
+
315
+ int pre_args_num; /* count of mandatory pre-arguments */
316
+ int post_args_num; /* count of mandatory post-arguments */
317
+
318
+ ID first_post_arg;
319
+
320
+ ID rest_arg;
321
+ ID block_arg;
322
+
323
+ NODE *kw_args;
324
+ NODE *kw_rest_arg;
325
+
326
+ NODE *opt_args;
327
+ };
328
+
329
+ RUBY_SYMBOL_EXPORT_END
330
+
331
+ #if defined(__cplusplus)
332
+ #if 0
333
+ { /* satisfy cc-mode */
334
+ #endif
335
+ } /* extern "C" { */
336
+ #endif
337
+
338
+ #endif /* RUBY_NODE_H */
@@ -0,0 +1,1804 @@
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 Alexey Voskov
6
+ * License: 2-clause BSD
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. "Citations" from Ruby 2.2.1 internal include files
22
+ */
23
+ #include "nodedump.h"
24
+
25
+
26
+ /*
27
+ * Part 2. Information about the nodes
28
+ *
29
+ */
30
+
31
+ // Pre-2.0 Ruby versions don't use this version
32
+ #if RUBY_API_VERSION_MAJOR == 2
33
+ #define USE_RB_ARGS_INFO 1
34
+ #endif
35
+
36
+ #if RUBY_API_VERSION_MAJOR == 1
37
+ #define RESET_GC_FLAGS 1
38
+ #endif
39
+
40
+
41
+ // Some generic utilities
42
+ int is_value_in_heap(VALUE val)
43
+ {
44
+ if (val == Qfalse || val == Qtrue ||
45
+ val == Qnil || val == Qundef ||
46
+ (val & FIXNUM_FLAG)
47
+ #ifdef FLONUM_MASK
48
+ || ((val & FLONUM_MASK) == FLONUM_FLAG) // This memory trick with floats is present only in 2.x
49
+ #endif
50
+ )
51
+ {
52
+ return 0;
53
+ }
54
+ else
55
+ return 1;
56
+ }
57
+
58
+
59
+ /*
60
+ * Converts Ruby string with hexadecimal number
61
+ * to the Ruby VALUE
62
+ */
63
+ VALUE str_to_value(VALUE str)
64
+ {
65
+ intptr_t ans = (intptr_t) Qnil;
66
+ sscanf(RSTRING_PTR(str), "%"PRIxPTR, &ans);
67
+ return (VALUE) ans;
68
+ }
69
+
70
+
71
+ /*
72
+ * Converts Ruby VALUE (i.e. machine address) to the
73
+ * hexadecimal Ruby string
74
+ */
75
+ VALUE value_to_str(VALUE val)
76
+ {
77
+ char str[16];
78
+ sprintf(str, "%" PRIxPTR, (intptr_t) val);
79
+ return rb_str_new2(str);
80
+ }
81
+
82
+ /*
83
+ * Converts VALUE to the sequence of bytes using big-endian
84
+ * standard. Returns number of non-zero bytes
85
+ *
86
+ * Inputs
87
+ * val -- input value
88
+ * buf -- pointer to the output buffer
89
+ * Returns
90
+ * number of written bytes
91
+ */
92
+ int value_to_bin(VALUE val, unsigned char *buf)
93
+ {
94
+ int i, len = 0;
95
+ unsigned char byte;
96
+ for (i = sizeof(VALUE) - 1; i >= 0; i--)
97
+ {
98
+ byte = (unsigned char) ((val >> (i * 8)) & 0xFF);
99
+ if (len > 0 || byte != 0)
100
+ {
101
+ *buf++ = byte;
102
+ len++;
103
+ }
104
+ }
105
+ return len;
106
+ }
107
+
108
+ /*
109
+ * Converts sequence of bytes (big-endian standard) to the VALUE.
110
+ *
111
+ * Inputs
112
+ * buf -- poiner to the input buffer
113
+ * len -- number of bytes
114
+ * Returns
115
+ * VALUE
116
+ */
117
+ VALUE bin_to_value(unsigned char *buf, int len)
118
+ {
119
+ VALUE val = (VALUE) 0;
120
+ int i;
121
+ for (i = len - 1; i >= 0; i--)
122
+ val |= ((VALUE) *buf++) << (i * 8);
123
+ return val;
124
+ }
125
+
126
+ #define NODES_CTBL_SIZE 256
127
+ static int nodes_ctbl[NODES_CTBL_SIZE * 3];
128
+
129
+
130
+ /*
131
+ * Part 3. Functions for node marshalization
132
+ */
133
+
134
+ /*
135
+ * Keeps the information about node elements position
136
+ * in the memory and its IDs/ordinals for export to the file
137
+ */
138
+ typedef struct {
139
+ VALUE vals; // values: key=>val Hash
140
+ VALUE ids; // identifiers: key=>id Hash
141
+ VALUE pos; // free identifier
142
+ } LeafTableInfo;
143
+
144
+ void LeafTableInfo_init(LeafTableInfo *lti)
145
+ {
146
+ lti->vals = rb_hash_new();
147
+ lti->ids = rb_hash_new();
148
+ lti->pos = 0;
149
+ }
150
+
151
+ void LeafTableInfo_mark(LeafTableInfo *lti)
152
+ {
153
+ rb_gc_mark(lti->vals);
154
+ rb_gc_mark(lti->ids);
155
+ }
156
+
157
+
158
+ int LeafTableInfo_addEntry(LeafTableInfo *lti, VALUE key, VALUE value)
159
+ {
160
+ VALUE v_id = rb_hash_aref(lti->ids, key);
161
+ if (v_id == Qnil)
162
+ {
163
+ int id = lti->pos++;
164
+ rb_hash_aset(lti->vals, key, value);
165
+ rb_hash_aset(lti->ids, key, INT2FIX(id));
166
+ return id;
167
+ }
168
+ else
169
+ {
170
+ return FIX2INT(v_id);
171
+ }
172
+ }
173
+
174
+ VALUE LeafTableInfo_getLeavesTable(LeafTableInfo *lti)
175
+ {
176
+ VALUE key, keys = rb_funcall(lti->vals, rb_intern("keys"), 0);
177
+ unsigned int i;
178
+ VALUE val;
179
+ for (i = 0; i < lti->pos; i++)
180
+ {
181
+ key = RARRAY_PTR(keys)[i];
182
+ val = rb_hash_aref(lti->vals, key);
183
+ rb_ary_store(keys, i, val);
184
+ }
185
+ return keys;
186
+ }
187
+
188
+ int LeafTableInfo_keyToID(LeafTableInfo *lti, VALUE key)
189
+ {
190
+ VALUE id = rb_hash_aref(lti->ids, key);
191
+ return (id == Qnil) ? -1 : FIX2INT(id);
192
+ }
193
+
194
+ /* The structure keeps information about the node
195
+ that is required for its dumping to the file
196
+ (mainly hashes with relocatable identifiers) */
197
+ typedef struct {
198
+ LeafTableInfo syms; // Node symbols
199
+ LeafTableInfo lits; // Node literals
200
+ LeafTableInfo idtabs; // Table of identifiers
201
+ #ifdef USE_RB_ARGS_INFO
202
+ LeafTableInfo args; // Table of arguments
203
+ #endif
204
+ LeafTableInfo gentries; // Global variables table
205
+ LeafTableInfo nodes; // Table of nodes
206
+ } NODEInfo;
207
+
208
+ void NODEInfo_init(NODEInfo *info)
209
+ {
210
+ LeafTableInfo_init(&(info->syms));
211
+ LeafTableInfo_init(&(info->lits));
212
+ LeafTableInfo_init(&(info->idtabs));
213
+ #ifdef USE_RB_ARGS_INFO
214
+ LeafTableInfo_init(&(info->args));
215
+ #endif
216
+ LeafTableInfo_init(&(info->gentries));
217
+ LeafTableInfo_init(&(info->nodes));
218
+ }
219
+
220
+ void NODEInfo_mark(NODEInfo *info)
221
+ {
222
+ LeafTableInfo_mark(&(info->syms));
223
+ LeafTableInfo_mark(&(info->lits));
224
+ LeafTableInfo_mark(&(info->idtabs));
225
+ #ifdef USE_RB_ARGS_INFO
226
+ LeafTableInfo_mark(&(info->args));
227
+ #endif
228
+ LeafTableInfo_mark(&(info->gentries));
229
+ LeafTableInfo_mark(&(info->nodes));
230
+ }
231
+
232
+ void NODEInfo_free(NODEInfo *info)
233
+ {
234
+ xfree(info);
235
+ }
236
+
237
+ LeafTableInfo *NODEInfo_getTableByID(NODEInfo *info, int id)
238
+ {
239
+ switch (id)
240
+ {
241
+ case NT_ID:
242
+ return &info->syms;
243
+ case NT_VALUE:
244
+ return &info->lits;
245
+ case NT_IDTABLE:
246
+ return &info->idtabs;
247
+ #ifdef USE_RB_ARGS_INFO
248
+ case NT_ARGS:
249
+ return &info->args;
250
+ #endif
251
+ case NT_ENTRY:
252
+ return &info->gentries;
253
+ case NT_NODE:
254
+ return &info->nodes;
255
+ default:
256
+ return NULL;
257
+ }
258
+ }
259
+
260
+ /*
261
+ * Converts node value to the binary data
262
+ * Input parameters:
263
+ * info -- current NODEInfo structure
264
+ * node -- parent node (that contains the value)
265
+ * ptr -- pointer to the output memory buffer
266
+ * type -- type of the entry (NT_...)
267
+ * value -- node->u?.value VALUE
268
+ * child_id -- child node number (1,2,3)
269
+ * Returns:
270
+ * Byte that contains the next information
271
+ * a) upper half-byte: VL_... data type (for node loader)
272
+ * b) lower half-byte: number of bytes written to the buffer
273
+ */
274
+ #define DUMP_RAW_VALUE(vl_ans, vl) (vl_ans | (value_to_bin(vl, (unsigned char *) ptr) << 4))
275
+ static int dump_node_value(NODEInfo *info, char *ptr, NODE *node, int type, VALUE value, int child_id)
276
+ {
277
+ if (type == NT_NULL || type == NT_LONG)
278
+ {
279
+ return DUMP_RAW_VALUE(VL_RAW, value);
280
+ }
281
+ else if (type == NT_NODE)
282
+ {
283
+ if (value == 0)
284
+ { // Variant a: empty node
285
+ return DUMP_RAW_VALUE(VL_RAW, value);
286
+ }
287
+ else if (nd_type(node) == NODE_ATTRASGN && value == 1 && child_id == 1)
288
+ { // Special case: "self"
289
+ return DUMP_RAW_VALUE(VL_RAW, value);
290
+ }
291
+ else if (TYPE(value) != T_NODE)
292
+ {
293
+ rb_raise(rb_eArgError, "dump_node_value, parent node %s: child node %d (ADR 0x%s): is not a node\n"
294
+ " Type: %s (%d), Value: %s",
295
+ ruby_node_name(nd_type(node)), child_id, RSTRING_PTR(value_to_str(value)),
296
+ RSTRING_PTR(rb_funcall(rb_funcall(value, rb_intern("class"), 0), rb_intern("to_s"), 0)),
297
+ TYPE(value),
298
+ RSTRING_PTR(rb_funcall(value, rb_intern("to_s"), 0)) );
299
+ }
300
+ else
301
+ { // Variant b: not empty node
302
+ VALUE id = LeafTableInfo_keyToID(&info->nodes, value_to_str(value));
303
+ if (id == (VALUE) -1)
304
+ {
305
+ rb_raise(rb_eArgError, "dump_node_value, parent node %s: child node %d (ADR 0x%s) not found",
306
+ ruby_node_name(nd_type(node)), child_id, RSTRING_PTR(value_to_str(value)));
307
+ return VL_RAW;
308
+ }
309
+ else
310
+ {
311
+ return DUMP_RAW_VALUE(VL_NODE, id);
312
+ }
313
+ return VL_NODE;
314
+ }
315
+ }
316
+ else if (type == NT_VALUE)
317
+ {
318
+ if (!is_value_in_heap(value))
319
+ { // a) value that is inside VALUE
320
+ return DUMP_RAW_VALUE(VL_RAW, value);
321
+ }
322
+ else
323
+ { // b) value that requires reference to literals table
324
+ VALUE id = LeafTableInfo_keyToID(&info->lits, value_to_str(value));
325
+ if (id == (VALUE) -1)
326
+ rb_raise(rb_eArgError, "Cannot find literal");
327
+ else
328
+ return DUMP_RAW_VALUE(VL_LIT, id);
329
+ }
330
+ }
331
+ else if (type == NT_ID)
332
+ {
333
+ ID sym = (VALUE) value; // We are working with RAW data from RAM!
334
+ VALUE id = LeafTableInfo_keyToID(&info->syms, INT2FIX(sym));
335
+ if (id == (VALUE) -1)
336
+ {
337
+ rb_raise(rb_eArgError, "Cannot find symbol ID %d (%s) (parent node %s, line %d)",
338
+ (int) sym, RSTRING_PTR(rb_id2str(ID2SYM(sym))),
339
+ ruby_node_name(nd_type(node)), nd_line(node));
340
+ return VL_RAW;
341
+ }
342
+ else
343
+ {
344
+ return DUMP_RAW_VALUE(VL_ID, id);
345
+ }
346
+ }
347
+ else if (type == NT_ENTRY || type == NT_ARGS || type == NT_IDTABLE)
348
+ {
349
+ VALUE key = value_to_str(value);
350
+ LeafTableInfo *lti = NODEInfo_getTableByID(info, type);
351
+ VALUE id = LeafTableInfo_keyToID(lti, key);
352
+ if (id == (VALUE) -1)
353
+ {
354
+ rb_raise(rb_eArgError, "Cannot find some entry");
355
+ return VL_RAW;
356
+ }
357
+ else
358
+ {
359
+ //memcpy(ptr, &id, sizeof(VALUE));
360
+ switch(type)
361
+ {
362
+ case NT_ENTRY: return DUMP_RAW_VALUE(VL_GVAR, id);
363
+ case NT_IDTABLE: return DUMP_RAW_VALUE(VL_IDTABLE, id);
364
+ case NT_ARGS: return DUMP_RAW_VALUE(VL_ARGS, id);
365
+ default: rb_raise(rb_eArgError, "Internal error");
366
+ }
367
+ }
368
+ }
369
+ else
370
+ {
371
+ rb_raise(rb_eArgError, "Unknown child node type %d", type);
372
+ }
373
+ }
374
+
375
+ static VALUE dump_nodes(NODEInfo *info)
376
+ {
377
+ int node_size = sizeof(int) + sizeof(VALUE) * 4;
378
+ int i, nt, flags_len;
379
+ NODE *node;
380
+ char *bin, *ptr, *rtypes;
381
+ VALUE nodes_ary = rb_funcall(info->nodes.vals, rb_intern("keys"), 0);
382
+ VALUE nodes_bin = rb_str_new(NULL, RARRAY_LEN(nodes_ary) * node_size);
383
+ VALUE ut[3];
384
+ bin = RSTRING_PTR(nodes_bin);
385
+
386
+ for (i = 0, ptr = bin; i < RARRAY_LEN(nodes_ary); i++)
387
+ {
388
+ node = RNODE(str_to_value(RARRAY_PTR(nodes_ary)[i]));
389
+ nt = nd_type(node);
390
+ rtypes = (char *) ptr; ptr += sizeof(int);
391
+ flags_len = value_to_bin(node->flags >> 5, (unsigned char *) ptr); ptr += flags_len;
392
+ //memcpy(ptr, &(node->flags), sizeof(VALUE)); ptr += sizeof(VALUE);
393
+ ut[0] = nodes_ctbl[nt * 3];
394
+ ut[1] = nodes_ctbl[nt * 3 + 1];
395
+ ut[2] = nodes_ctbl[nt * 3 + 2];
396
+ if (nt == NODE_OP_ASGN2 && LeafTableInfo_keyToID(&info->syms, INT2FIX(node->u1.value)) != -1)
397
+ {
398
+ ut[0] = NT_ID; ut[1] = NT_ID; ut[2] = NT_ID;
399
+ }
400
+
401
+ if (nt == NODE_ARGS_AUX)
402
+ {
403
+ ut[0] = NT_ID; ut[1] = NT_LONG; ut[2] = NT_NODE;
404
+ if (LeafTableInfo_keyToID(&info->syms, INT2FIX(node->u2.value)) != -1)
405
+ {
406
+ ut[1] = NT_ID;
407
+ }
408
+ else
409
+ {
410
+ ut[1] = NT_LONG;
411
+ }
412
+ if (node->u1.value == 0) ut[0] = NT_NULL;
413
+ if (node->u2.value == 0) ut[1] = NT_NULL;
414
+ if (node->u3.value == 0) ut[2] = NT_NULL;
415
+ }
416
+
417
+ rtypes[0] = dump_node_value(info, ptr, node, ut[0], node->u1.value, 1);
418
+ ptr += (rtypes[0] & 0xF0) >> 4;
419
+ rtypes[1] = dump_node_value(info, ptr, node, ut[1], node->u2.value, 2);
420
+ ptr += (rtypes[1] & 0xF0) >> 4;
421
+ rtypes[2] = dump_node_value(info, ptr, node, ut[2], node->u3.value, 3);
422
+ ptr += (rtypes[2] & 0xF0) >> 4;
423
+ rtypes[3] = flags_len;
424
+ }
425
+ rb_str_resize(nodes_bin, (int) (ptr - bin) + 1);
426
+ //printf("%d", ptr - bin);
427
+ return nodes_bin;
428
+ }
429
+
430
+
431
+ /*
432
+ * Transforms preprocessed node to Ruby hash that can be used
433
+ * to load the node from disk.
434
+ *
435
+ * See m_nodedump_to_hash function for output hash format details
436
+ */
437
+ VALUE NODEInfo_toHash(NODEInfo *info)
438
+ {
439
+ VALUE ans = rb_hash_new();
440
+ VALUE idtbl, idtabs = LeafTableInfo_getLeavesTable(&info->idtabs);
441
+ VALUE syms = LeafTableInfo_getLeavesTable(&info->syms);
442
+ VALUE args;
443
+ int i, j, id;
444
+ // Add some signatures
445
+ rb_hash_aset(ans, ID2SYM(rb_intern("MAGIC")), rb_str_new2("NODEMARSHAL10"));
446
+ rb_hash_aset(ans, ID2SYM(rb_intern("RUBY_PLATFORM")),
447
+ rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM")));
448
+ rb_hash_aset(ans, ID2SYM(rb_intern("RUBY_VERSION")),
449
+ rb_const_get(rb_cObject, rb_intern("RUBY_VERSION")));
450
+ // Write literals, symbols and global_entries arrays: they don't need to be corrected
451
+ rb_hash_aset(ans, ID2SYM(rb_intern("literals")), LeafTableInfo_getLeavesTable(&info->lits));
452
+ rb_hash_aset(ans, ID2SYM(rb_intern("symbols")), syms);
453
+ rb_hash_aset(ans, ID2SYM(rb_intern("global_entries")), LeafTableInfo_getLeavesTable(&info->gentries));
454
+ // Replace RAM IDs to disk IDs in id_tables
455
+ for (i = 0; i < RARRAY_LEN(idtabs); i++)
456
+ {
457
+ idtbl = RARRAY_PTR(idtabs)[i];
458
+ for (j = 0; j < RARRAY_LEN(idtbl); j++)
459
+ {
460
+ id = LeafTableInfo_keyToID(&info->syms, RARRAY_PTR(idtbl)[j]);
461
+
462
+ if (id == -1)
463
+ {
464
+ ID sym = FIX2INT(RARRAY_PTR(idtbl)[j]);
465
+ rb_raise(rb_eArgError, "Cannot find the symbol ID %d", (int) sym);
466
+ }
467
+ else
468
+ {
469
+ rb_ary_store(idtbl, j, INT2FIX(id));
470
+ }
471
+
472
+ }
473
+ }
474
+ rb_hash_aset(ans, ID2SYM(rb_intern("id_tables")), idtabs);
475
+ // Replace RAM IDs to disk IDs in args tables
476
+ #ifdef USE_RB_ARGS_INFO
477
+ args = LeafTableInfo_getLeavesTable(&info->args);
478
+ for (i = 0; i < RARRAY_LEN(args); i++)
479
+ {
480
+ VALUE args_entry = RARRAY_PTR(args)[i];
481
+ VALUE *eptr = RARRAY_PTR(args_entry);
482
+ int args_vals[5] = {0, 1, 7, 8, 9};
483
+ int args_ids[3] = {4, 5, 6};
484
+ if (RARRAY_LEN(args_entry) != 10)
485
+ rb_raise(rb_eArgError, "Corrupted args entry");
486
+ // Pointer to nodes to be replaced:
487
+ // a) VALUES
488
+ // (0) pre_init, (1) post_init,
489
+ // (7) kw_args, (8) kw_rest_arg, (9) opt_args
490
+ for (j = 0; j < 5; j++)
491
+ {
492
+ int ind = args_vals[j];
493
+ VALUE key = eptr[ind];
494
+ if (!strcmp(RSTRING_PTR(key), "0"))
495
+ eptr[ind] = INT2FIX(-1);
496
+ else
497
+ {
498
+ eptr[ind] = INT2FIX(LeafTableInfo_keyToID(&info->nodes, key));
499
+ if (FIX2INT(eptr[ind]) == -1)
500
+ rb_raise(rb_eArgError, "Unknown NODE in args tables");
501
+ }
502
+ }
503
+ // b) IDs (symbols)
504
+ // (4) first_post_arg (5) rest_arg (6) block_arg
505
+ for (j = 0; j < 3; j++)
506
+ {
507
+ int ind = args_ids[j];
508
+ VALUE key = eptr[ind];
509
+ if (FIX2INT(key) != 0)
510
+ {
511
+ eptr[ind] = INT2FIX(LeafTableInfo_keyToID(&info->syms, key));
512
+ if (FIX2INT(eptr[ind]) == -1)
513
+ rb_raise(rb_eArgError, "Unknown symbolic ID in args tables");
514
+ }
515
+ else
516
+ eptr[ind] = INT2FIX(-1);
517
+ }
518
+ }
519
+ #else
520
+ args = rb_ary_new();
521
+ #endif
522
+
523
+ rb_hash_aset(ans, ID2SYM(rb_intern("args")), args);
524
+ // Special case: NODES. Nodes are kept as binary string
525
+ rb_hash_aset(ans, ID2SYM(rb_intern("nodes")), dump_nodes(info));
526
+ return ans;
527
+ }
528
+
529
+
530
+ static void NODEInfo_addValue(NODEInfo *info, VALUE value)
531
+ {
532
+ if (is_value_in_heap(value))
533
+ {
534
+ VALUE lkey = value_to_str(value);
535
+ LeafTableInfo_addEntry(&info->lits, lkey, value);
536
+ }
537
+ }
538
+
539
+ /*
540
+ * Function counts number of nodes and fills NODEInfo struct
541
+ * that is neccessary for the node saving to the HDD
542
+ */
543
+ int count_num_of_nodes(NODE *node, NODE *parent, NODEInfo *info)
544
+ {
545
+ int ut[3], num, offset;
546
+ if (node == 0)
547
+ {
548
+ return 0;
549
+ }
550
+ else if (TYPE((VALUE) node) != T_NODE)
551
+ {
552
+ rb_raise(rb_eArgError, "count_num_of_nodes: parent node %s: child node (ADR 0x%s) is not a node; Type: %d",
553
+ ruby_node_name(nd_type(parent)), RSTRING_PTR(value_to_str((VALUE) node)), TYPE((VALUE) node));
554
+ return 0;
555
+ }
556
+ else
557
+ {
558
+ offset = nd_type(node) * 3;
559
+ ut[0] = nodes_ctbl[offset++];
560
+ ut[1] = nodes_ctbl[offset++];
561
+ ut[2] = nodes_ctbl[offset];
562
+
563
+ if (nd_type(node) == NODE_OP_ASGN2 && nd_type(parent) == NODE_OP_ASGN2)
564
+ {
565
+ ut[0] = NT_ID;
566
+ ut[1] = NT_ID;
567
+ ut[2] = NT_ID;
568
+ }
569
+
570
+ /* Some Ruby 1.9.3 style function arguments (without rb_args_info) */
571
+ if (nd_type(node) == NODE_ARGS_AUX)
572
+ {
573
+ ut[0] = NT_ID;
574
+ ut[1] = (nd_type(parent) == NODE_ARGS_AUX) ? NT_LONG : NT_ID;
575
+ ut[2] = NT_NODE;
576
+
577
+ if (node->u1.value == 0) ut[0] = NT_NULL;
578
+ if (node->u2.value == 0) ut[1] = NT_NULL;
579
+ if (node->u3.value == 0) ut[2] = NT_NULL;
580
+ }
581
+ /* Some Ruby 1.9.3-specific code for NODE_ATTRASGN */
582
+ if (nd_type(node) == NODE_ATTRASGN)
583
+ {
584
+ if (node->u1.value == 1) ut[0] = NT_LONG;
585
+ }
586
+ /* Check if there is information about child nodes types */
587
+ if (ut[0] == NT_UNKNOWN || ut[1] == NT_UNKNOWN || ut[2] == NT_UNKNOWN)
588
+ {
589
+ rb_raise(rb_eArgError, "Cannot interpret node %d (%s)", nd_type(node), ruby_node_name(nd_type(node)));
590
+ }
591
+ /* Save the ID of the node */
592
+ num = 1;
593
+ LeafTableInfo_addEntry(&info->nodes, value_to_str((VALUE) node), value_to_str((VALUE) node));
594
+ /* Analyze node childs */
595
+ /* a) child 1 */
596
+ if (ut[0] == NT_NODE)
597
+ {
598
+ num += count_num_of_nodes(node->u1.node, node, info);
599
+ }
600
+ else if (ut[0] == NT_ID)
601
+ {
602
+ LeafTableInfo_addEntry(&info->syms, INT2FIX(node->u1.id), rb_id2str(node->u1.id));
603
+ }
604
+ else if (ut[0] == NT_VALUE)
605
+ {
606
+ if (TYPE(node->u1.value) == T_NODE)
607
+ rb_raise(rb_eArgError, "NODE instead of VALUE in child 1 of node %s", ruby_node_name(nd_type(node)));
608
+ NODEInfo_addValue(info, node->u1.value);
609
+ }
610
+ else if (ut[0] == NT_IDTABLE)
611
+ {
612
+ VALUE tkey = value_to_str(node->u1.value);
613
+ VALUE idtbl_ary = rb_ary_new();
614
+ ID *idtbl = (ID *) node->u1.value;
615
+ int i, size = (node->u1.value) ? *idtbl++ : 0;
616
+ for (i = 0; i < size; i++)
617
+ {
618
+ ID sym = *idtbl++;
619
+ rb_ary_push(idtbl_ary, INT2FIX(sym));
620
+ LeafTableInfo_addEntry(&info->syms, INT2FIX(sym), rb_id2str(sym));
621
+ }
622
+ LeafTableInfo_addEntry(&info->idtabs, tkey, idtbl_ary);
623
+ }
624
+ else if (ut[0] != NT_LONG && ut[0] != NT_NULL)
625
+ {
626
+ rb_raise(rb_eArgError, "1!");
627
+ }
628
+ /* b) child 2 */
629
+ if (ut[1] == NT_NODE)
630
+ {
631
+ num += count_num_of_nodes(node->u2.node, node, info);
632
+ }
633
+ else if (ut[1] == NT_ID)
634
+ {
635
+ LeafTableInfo_addEntry(&info->syms, INT2FIX(node->u2.id), rb_id2str(node->u2.id));
636
+ }
637
+ else if (ut[1] == NT_VALUE)
638
+ {
639
+ if (TYPE(node->u2.value) == T_NODE)
640
+ rb_raise(rb_eArgError, "NODE instead of VALUE in child 2 of node %s", ruby_node_name(nd_type(node)));
641
+ NODEInfo_addValue(info, node->u2.value);
642
+ }
643
+ else if (ut[1] != NT_LONG && ut[1] != NT_NULL)
644
+ {
645
+ rb_raise(rb_eArgError, "2!");
646
+ }
647
+
648
+ /* c) child 3 */
649
+ if (ut[2] == NT_NODE)
650
+ {
651
+ num += count_num_of_nodes(node->u3.node, node, info);
652
+ }
653
+ else if (ut[2] == NT_ID)
654
+ {
655
+ LeafTableInfo_addEntry(&info->syms, INT2FIX(node->u3.id), rb_id2str(node->u3.id));
656
+ }
657
+ else if (ut[2] == NT_ARGS)
658
+ {
659
+ #ifdef USE_RB_ARGS_INFO
660
+ VALUE varg = Qtrue;
661
+ struct rb_args_info *ainfo;
662
+ ID asym;
663
+ ainfo = node->u3.args;
664
+ // Save child nodes
665
+ num += count_num_of_nodes(ainfo->pre_init, node, info);
666
+ num += count_num_of_nodes(ainfo->post_init, node, info);
667
+ num += count_num_of_nodes(ainfo->kw_args, node, info);
668
+ num += count_num_of_nodes(ainfo->kw_rest_arg, node, info);
669
+ num += count_num_of_nodes(ainfo->opt_args, node, info);
670
+ // Save rb_args_info structure content
671
+ varg = rb_ary_new();
672
+ rb_ary_push(varg, value_to_str((VALUE) ainfo->pre_init));
673
+ rb_ary_push(varg, value_to_str((VALUE) ainfo->post_init));
674
+ rb_ary_push(varg, INT2FIX(ainfo->pre_args_num));
675
+ rb_ary_push(varg, INT2FIX(ainfo->post_args_num));
676
+
677
+ asym = ainfo->first_post_arg; rb_ary_push(varg, INT2FIX(asym)); // ID
678
+ if (asym != 0)
679
+ LeafTableInfo_addEntry(&info->syms, INT2FIX(asym), rb_id2str(asym));
680
+
681
+ asym = ainfo->rest_arg; rb_ary_push(varg, INT2FIX(asym)); // ID
682
+ if (asym != 0)
683
+ LeafTableInfo_addEntry(&info->syms, INT2FIX(asym), rb_id2str(asym));
684
+
685
+ asym = ainfo->block_arg; rb_ary_push(varg, INT2FIX(asym)); // ID
686
+ if (asym != 0)
687
+ LeafTableInfo_addEntry(&info->syms, INT2FIX(asym), rb_id2str(asym));
688
+ rb_ary_push(varg, value_to_str((VALUE) ainfo->kw_args));
689
+ rb_ary_push(varg, value_to_str((VALUE) ainfo->kw_rest_arg));
690
+ rb_ary_push(varg, value_to_str((VALUE) ainfo->opt_args));
691
+
692
+ LeafTableInfo_addEntry(&info->args, value_to_str((VALUE) ainfo), varg);
693
+ #else
694
+ rb_raise(rb_eArgError, "NT_ARGS entry withour USE_RB_ARGS_INFO");
695
+ #endif
696
+ }
697
+ else if (ut[2] == NT_ENTRY)
698
+ {
699
+ ID gsym = node->u3.entry->id;
700
+ // Save symbol to the symbol table
701
+ int newid = LeafTableInfo_addEntry(&info->syms, INT2FIX(gsym), rb_id2str(gsym));
702
+ LeafTableInfo_addEntry(&info->gentries, value_to_str(node->u3.value), INT2FIX(newid));
703
+ }
704
+ else if (ut[2] != NT_LONG && ut[2] != NT_NULL)
705
+ {
706
+ rb_raise(rb_eArgError, "Invalid child node 3: %d", ut[2]);
707
+ }
708
+
709
+ return num;
710
+ }
711
+ }
712
+
713
+
714
+
715
+ //-------------------------------------------------------------------------
716
+
717
+ /*
718
+ * Part 4. Functions for loading marshalled nodes
719
+ */
720
+ typedef struct {
721
+ ID *syms_adr; // Table of symbols
722
+ int syms_len;
723
+
724
+ VALUE *lits_adr; // Table of literals
725
+ int lits_len;
726
+
727
+ ID **idtbls_adr; // Table of symbols tables
728
+ int idtbls_len;
729
+
730
+ struct rb_global_entry **gvars_adr; // Table of global variables entries
731
+ int gvars_len;
732
+
733
+ NODE **nodes_adr; // Table of nodes
734
+ int nodes_len;
735
+ #ifdef USE_RB_ARGS_INFO
736
+ struct rb_args_info **args_adr; // Table of code blocks arguments
737
+ int args_len;
738
+ #endif
739
+ } NODEObjAddresses;
740
+
741
+
742
+ void NODEObjAddresses_free(NODEObjAddresses *obj)
743
+ {
744
+ xfree(obj->syms_adr);
745
+ xfree(obj->idtbls_adr);
746
+ xfree(obj->gvars_adr);
747
+ xfree(obj->nodes_adr);
748
+ #ifdef USE_RB_ARGS_INFO
749
+ xfree(obj->args_adr);
750
+ #endif
751
+ xfree(obj);
752
+ }
753
+
754
+
755
+
756
+ void rbstr_printf(VALUE str, const char *fmt, ...)
757
+ {
758
+ char buf[1024];
759
+ va_list ptr;
760
+
761
+ va_start(ptr, fmt);
762
+ vsprintf(buf, fmt, ptr);
763
+ rb_str_append(str, rb_str_new2(buf));
764
+ va_end(ptr);
765
+ }
766
+
767
+ #define PRINT_NODE_TAB for (j = 0; j < tab; j++) rbstr_printf(str, " ");
768
+ void print_node(VALUE str, NODE *node, int tab)
769
+ {
770
+ int i, j, type, ut[3];
771
+ int show_offsets = 0;
772
+ VALUE uref[3];
773
+
774
+ PRINT_NODE_TAB
775
+ if (node == NULL)
776
+ {
777
+ rbstr_printf(str, "(NULL)\n");
778
+ return;
779
+ }
780
+ type = nd_type(node);
781
+
782
+ if (show_offsets)
783
+ {
784
+ rbstr_printf(str, "@ %s | %16"PRIxPTR " %16"PRIxPTR " %16"PRIxPTR "\n", ruby_node_name(type),
785
+ (intptr_t) node->u1.value, (intptr_t) node->u2.value, (intptr_t) node->u3.value);
786
+ }
787
+ else
788
+ {
789
+ rbstr_printf(str, "@ %s\n", ruby_node_name(type));
790
+ }
791
+
792
+ ut[0] = nodes_ctbl[type * 3];
793
+ ut[1] = nodes_ctbl[type * 3 + 1];
794
+ ut[2] = nodes_ctbl[type * 3 + 2];
795
+
796
+ uref[0] = node->u1.value;
797
+ uref[1] = node->u2.value;
798
+ uref[2] = node->u3.value;
799
+
800
+ for (i = 0; i < 3; i++)
801
+ {
802
+
803
+ if (ut[i] == NT_NODE)
804
+ {
805
+ if (nd_type(node) != NODE_OP_ASGN2 || i != 2)
806
+ print_node(str, RNODE(uref[i]), tab + 1);
807
+ else
808
+ {
809
+ if (ut[i] != 0 && TYPE(ut[i]) != T_NODE)
810
+ rb_raise(rb_eArgError, "print_node: broken node 0x%s", RSTRING_PTR(value_to_str(ut[i])));
811
+ PRINT_NODE_TAB; rbstr_printf(str, " ");
812
+ rbstr_printf(str, "%"PRIxPTR " %"PRIxPTR " %"PRIxPTR"\n",
813
+ (intptr_t) RNODE(uref[i])->u1.value,
814
+ (intptr_t) RNODE(uref[i])->u2.value,
815
+ (intptr_t) RNODE(uref[i])->u3.value);
816
+ }
817
+ }
818
+ else if (ut[i] == NT_VALUE)
819
+ {
820
+ char *class_name = RSTRING_PTR(rb_funcall(rb_funcall(uref[i], rb_intern("class"), 0), rb_intern("to_s"), 0));
821
+ PRINT_NODE_TAB; rbstr_printf(str, " ");
822
+ if (show_offsets)
823
+ {
824
+ rbstr_printf(str, ">| ADR: %"PRIxPTR" TYPE: %d TEXT: %s\n",
825
+ (intptr_t) uref[i],
826
+ class_name, TYPE(uref[i]),
827
+ RSTRING_PTR(rb_funcall(uref[i], rb_intern("to_s"), 0)));
828
+ }
829
+ else
830
+ {
831
+ rbstr_printf(str, ">| CLASS: %s (TYPE %d); VALUE: %s\n",
832
+ class_name, TYPE(uref[i]),
833
+ RSTRING_PTR(rb_funcall(uref[i], rb_intern("to_s"), 0)));
834
+ }
835
+ }
836
+ else if (ut[i] == NT_ID)
837
+ {
838
+ PRINT_NODE_TAB; rbstr_printf(str, " ");
839
+ if (show_offsets)
840
+ rbstr_printf(str, ">| ID: %d; SYMBOL: %s\n", (ID) uref[i], RSTRING_PTR(rb_id2str(uref[i])));
841
+ else
842
+ rbstr_printf(str, ">| SYMBOL: :%s\n", RSTRING_PTR(rb_id2str(uref[i])));
843
+ }
844
+ else if (ut[i] == NT_LONG)
845
+ {
846
+ PRINT_NODE_TAB; rbstr_printf(str, " ");
847
+ rbstr_printf(str, ">| %"PRIxPTR "\n", (intptr_t) uref[i]);
848
+ }
849
+ else if (ut[i] == NT_NULL)
850
+ {
851
+ PRINT_NODE_TAB; rbstr_printf(str, " ");
852
+ rbstr_printf(str, ">| (NULL)\n");
853
+ }
854
+ else if (ut[i] == NT_ARGS)
855
+ {
856
+ PRINT_NODE_TAB; rbstr_printf(str, " ");
857
+ rbstr_printf(str, ">| ARGS\n");
858
+ }
859
+ else if (ut[i] == NT_IDTABLE)
860
+ {
861
+ PRINT_NODE_TAB; rbstr_printf(str, " ");
862
+ rbstr_printf(str, ">| IDTABLE\n");
863
+ }
864
+ else
865
+ {
866
+ PRINT_NODE_TAB; rbstr_printf(str, " ");
867
+ rbstr_printf(str, ">| [UNKNOWN]\n");
868
+ }
869
+ }
870
+ }
871
+
872
+
873
+
874
+ void resolve_syms_ords(VALUE data, NODEObjAddresses *relocs)
875
+ {
876
+ VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("symbols")));
877
+ int i;
878
+ if (tbl_val == Qnil)
879
+ {
880
+ rb_raise(rb_eArgError, "Cannot find symbols table");
881
+ }
882
+ if (TYPE(tbl_val) != T_ARRAY)
883
+ {
884
+ rb_raise(rb_eArgError, "Symbols table is not an array");
885
+ }
886
+ relocs->syms_len = RARRAY_LEN(tbl_val);
887
+ relocs->syms_adr = ALLOC_N(ID, relocs->syms_len);
888
+ for (i = 0; i < relocs->syms_len; i++)
889
+ relocs->syms_adr[i] = rb_intern(RSTRING_PTR(RARRAY_PTR(tbl_val)[i]));
890
+ }
891
+
892
+ void resolve_lits_ords(VALUE data, NODEObjAddresses *relocs)
893
+ {
894
+ VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("literals")));
895
+ if (tbl_val == Qnil)
896
+ {
897
+ rb_raise(rb_eArgError, "Cannot find literals table");
898
+ }
899
+ if (TYPE(tbl_val) != T_ARRAY)
900
+ {
901
+ rb_raise(rb_eArgError, "Literals table is not an array");
902
+ }
903
+ relocs->lits_adr = RARRAY_PTR(tbl_val);
904
+ relocs->lits_len = RARRAY_LEN(tbl_val);
905
+ }
906
+
907
+ void resolve_gvars_ords(VALUE data, NODEObjAddresses *relocs)
908
+ {
909
+ VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("global_entries")));
910
+ int i;
911
+
912
+ if (tbl_val == Qnil)
913
+ {
914
+ rb_raise(rb_eArgError, "Cannot find global entries table");
915
+ }
916
+ if (TYPE(tbl_val) != T_ARRAY)
917
+ {
918
+ rb_raise(rb_eArgError, "Global entries table should be an array");
919
+ }
920
+ relocs->gvars_len = RARRAY_LEN(tbl_val);
921
+ relocs->gvars_adr = ALLOC_N(struct rb_global_entry *, relocs->gvars_len);
922
+ for (i = 0; i < relocs->gvars_len; i++)
923
+ {
924
+ int ind = FIX2INT(RARRAY_PTR(tbl_val)[i]);
925
+ ID sym = relocs->syms_adr[ind];
926
+ relocs->gvars_adr[i] = rb_global_entry(sym);
927
+ }
928
+ }
929
+
930
+
931
+ void resolve_idtbls_ords(VALUE data, NODEObjAddresses *relocs)
932
+ {
933
+ VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("id_tables")));
934
+ int i, j, idnum;
935
+
936
+ if (tbl_val == Qnil)
937
+ {
938
+ rb_raise(rb_eArgError, "Cannot find id_tables entries");
939
+ }
940
+ relocs->idtbls_len = RARRAY_LEN(tbl_val);
941
+ relocs->idtbls_adr = ALLOC_N(ID *, relocs->idtbls_len);
942
+ for (i = 0; i < relocs->idtbls_len; i++)
943
+ {
944
+ VALUE idtbl = RARRAY_PTR(tbl_val)[i];
945
+ idnum = RARRAY_LEN(idtbl);
946
+ if (idnum == 0)
947
+ { // Empty table: NULL pointer in the address table
948
+ relocs->idtbls_adr[i] = NULL;
949
+ }
950
+ else
951
+ { // Filled table: pointer to dynamic memory
952
+ relocs->idtbls_adr[i] = ALLOC_N(ID, idnum + 1);
953
+ relocs->idtbls_adr[i][0] = idnum;
954
+ for (j = 0; j < idnum; j++)
955
+ {
956
+ int ind = FIX2INT(RARRAY_PTR(idtbl)[j]);
957
+ relocs->idtbls_adr[i][j+1] = relocs->syms_adr[ind];
958
+ }
959
+ }
960
+ }
961
+ }
962
+
963
+ void resolve_nodes_ords(VALUE data, int num_of_nodes, NODEObjAddresses *relocs)
964
+ {
965
+ int i;
966
+ VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("nodes")));
967
+ if (tbl_val == Qnil)
968
+ {
969
+ rb_raise(rb_eArgError, "Cannot find nodes entries");
970
+ }
971
+ if (TYPE(tbl_val) != T_STRING)
972
+ {
973
+ rb_raise(rb_eArgError, "Nodes description must be a string");
974
+ }
975
+ relocs->nodes_adr = ALLOC_N(NODE *, num_of_nodes);
976
+ relocs->nodes_len = num_of_nodes;
977
+ for (i = 0; i < num_of_nodes; i++)
978
+ {
979
+ relocs->nodes_adr[i] = (NODE *) NEW_NODE((enum node_type) 0, 0, 0, 0);
980
+ }
981
+ }
982
+
983
+ #ifdef USE_RB_ARGS_INFO
984
+ void resolve_args_ords(VALUE data, NODEObjAddresses *relocs)
985
+ {
986
+ int i;
987
+ VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("args")));
988
+
989
+ if (tbl_val == Qnil)
990
+ {
991
+ rb_raise(rb_eArgError, "Cannot find args entries table");
992
+ }
993
+ if (TYPE(tbl_val) != T_ARRAY)
994
+ {
995
+ rb_raise(rb_eArgError, "args description must be an array");
996
+ }
997
+ relocs->args_len = RARRAY_LEN(tbl_val);
998
+ relocs->args_adr = ALLOC_N(struct rb_args_info *, relocs->args_len);
999
+ for (i = 0; i < relocs->args_len; i++)
1000
+ {
1001
+ int ord;
1002
+ VALUE ainfo_val, *aiptr;
1003
+ struct rb_args_info *ainfo;
1004
+
1005
+ relocs->args_adr[i] = ALLOC(struct rb_args_info);
1006
+ ainfo_val = RARRAY_PTR(tbl_val)[i];
1007
+ aiptr = RARRAY_PTR(ainfo_val);
1008
+ ainfo = relocs->args_adr[i];
1009
+
1010
+ if (TYPE(ainfo_val) != T_ARRAY || RARRAY_LEN(ainfo_val) != 10)
1011
+ {
1012
+ rb_raise(rb_eArgError, "args entry %d is corrupted", i);
1013
+ }
1014
+ // Load unresolved values
1015
+ ainfo->pre_init = (NODE *) FIX2LONG(aiptr[0]); // Node ordinal
1016
+ ainfo->post_init = (NODE *) FIX2LONG(aiptr[1]); // Node ordinal
1017
+ ainfo->pre_args_num = FIX2INT(aiptr[2]); // No ordinal resolving
1018
+ ainfo->post_args_num = FIX2INT(aiptr[3]); // No ordinal resolving
1019
+ ainfo->first_post_arg = FIX2INT(aiptr[4]); // Symbolic ordinal
1020
+ ainfo->rest_arg = FIX2INT(aiptr[5]); // Symbolic ordinal
1021
+ ainfo->block_arg = FIX2INT(aiptr[6]); // Symbolic ordinal
1022
+ ainfo->kw_args = (NODE *) FIX2LONG(aiptr[7]); // Node ordinal
1023
+ ainfo->kw_rest_arg = (NODE *) FIX2LONG(aiptr[8]); // Node ordinal
1024
+ ainfo->opt_args = (NODE *) FIX2LONG(aiptr[9]); // Node ordinal
1025
+ // Resolve nodes
1026
+ ord = (int) (((VALUE) ainfo->pre_init) & 0xFFFFFFFF);
1027
+ if (ord < -1 || ord >= relocs->nodes_len)
1028
+ rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
1029
+ ainfo->pre_init = (ord == -1) ? NULL : relocs->nodes_adr[ord];
1030
+
1031
+ ord = (int) (((VALUE) ainfo->post_init) & 0xFFFFFFFF);
1032
+ if (ord < -1 || ord >= relocs->nodes_len)
1033
+ rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
1034
+ ainfo->post_init = (ord == -1) ? NULL : relocs->nodes_adr[ord];
1035
+
1036
+ ord = (int) (((VALUE) ainfo->kw_args) & 0xFFFFFFFF);
1037
+ if (ord < -1 || ord >= relocs->nodes_len)
1038
+ rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
1039
+ ainfo->kw_args = (ord == -1) ? NULL : relocs->nodes_adr[ord];
1040
+
1041
+ ord = (int) (((VALUE) ainfo->kw_rest_arg) & 0xFFFFFFFF);
1042
+ if (ord < -1 || ord >= relocs->nodes_len)
1043
+ rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
1044
+ ainfo->kw_rest_arg = (ord == -1) ? NULL : relocs->nodes_adr[ord];
1045
+
1046
+ ord = (int) (((VALUE) ainfo->opt_args) & 0xFFFFFFFF);
1047
+ if (ord < -1 || ord >= relocs->nodes_len)
1048
+ rb_raise(rb_eArgError, "Invalid node ordinal %d", ord);
1049
+ ainfo->opt_args = (ord == -1) ? NULL : relocs->nodes_adr[ord];
1050
+ // Resolve symbolic ordinals
1051
+ ord = ainfo->first_post_arg;
1052
+ if (ord < -1 || ord >= relocs->syms_len)
1053
+ rb_raise(rb_eArgError, "1- Invalid symbol ID ordinal %d", ord);
1054
+ ainfo->first_post_arg = (ord == -1) ? 0 : relocs->syms_adr[ord];
1055
+
1056
+ ord = ainfo->rest_arg;
1057
+ if (ord < -1 || ord >= relocs->syms_len)
1058
+ rb_raise(rb_eArgError, "2- Invalid symbol ID ordinal %d", ord);
1059
+ ainfo->rest_arg = (ord == -1) ? 0 : relocs->syms_adr[ord];
1060
+
1061
+ ord = ainfo->block_arg;
1062
+ if (ord < -1 || ord >= relocs->syms_len)
1063
+ rb_raise(rb_eArgError, "3- Invalid symbol ID ordinal %d", ord);
1064
+ ainfo->block_arg = (ord == -1) ? 0 : relocs->syms_adr[ord];
1065
+ }
1066
+ }
1067
+ #endif
1068
+
1069
+ void load_nodes_from_str(VALUE data, NODEObjAddresses *relocs)
1070
+ {
1071
+ int i, j;
1072
+ VALUE tbl_val = rb_hash_aref(data, ID2SYM(rb_intern("nodes")));
1073
+ unsigned char *bin = (unsigned char *) RSTRING_PTR(tbl_val);
1074
+ NODE *node = NULL;
1075
+ for (i = 0; i < relocs->nodes_len; i++)
1076
+ {
1077
+ int rtypes[4];
1078
+ VALUE u[3], flags;
1079
+ // Read data structure info
1080
+ for (j = 0; j < 4; j++)
1081
+ rtypes[j] = *bin++;
1082
+ flags = bin_to_value(bin, rtypes[3]); bin += rtypes[3];
1083
+ for (j = 0; j < 3; j++)
1084
+ {
1085
+ int val_len = (rtypes[j] & 0xF0) >> 4;
1086
+ u[j] = bin_to_value(bin, val_len);
1087
+ bin += val_len;
1088
+ rtypes[j] &= 0x0F;
1089
+
1090
+ }
1091
+ if ((char *)bin - RSTRING_PTR(tbl_val) > RSTRING_LEN(tbl_val))
1092
+ rb_raise(rb_eArgError, "Nodes binary dump is too short");
1093
+ // Resolving all addresses
1094
+ for (j = 0; j < 3; j++)
1095
+ {
1096
+ switch(rtypes[j])
1097
+ {
1098
+ case VL_RAW: // Do nothing: it is raw data
1099
+ break;
1100
+ case VL_NODE:
1101
+ if (u[j] >= (unsigned int) relocs->nodes_len)
1102
+ rb_raise(rb_eArgError, "Cannot resolve VL_NODE entry %d", (int) u[j]);
1103
+ u[j] = (VALUE) relocs->nodes_adr[u[j]];
1104
+ if (TYPE(u[j]) != T_NODE)
1105
+ rb_raise(rb_eArgError, "load_nodes_from_str: nodes memory corrupted");
1106
+ break;
1107
+ case VL_ID:
1108
+ if (u[j] >= (unsigned int) relocs->syms_len)
1109
+ rb_raise(rb_eArgError, "Cannot resolve VL_ID entry %d", (int) u[j]);
1110
+ u[j] = relocs->syms_adr[u[j]];
1111
+ break;
1112
+ case VL_GVAR:
1113
+ if (u[j] >= (unsigned int) relocs->gvars_len)
1114
+ rb_raise(rb_eArgError, "Cannot resolve VL_GVAR entry %d", (int) u[j]);
1115
+ u[j] = (VALUE) relocs->gvars_adr[u[j]];
1116
+ break;
1117
+ case VL_IDTABLE:
1118
+ if (u[j] >= (unsigned int) relocs->idtbls_len)
1119
+ rb_raise(rb_eArgError, "Cannot resolve VL_IDTABLE entry %d", (int) u[j]);
1120
+ u[j] = (VALUE) relocs->idtbls_adr[u[j]];
1121
+ break;
1122
+ #ifdef USE_RB_ARGS_INFO
1123
+ case VL_ARGS:
1124
+ if (u[j] >= (unsigned int) relocs->args_len)
1125
+ rb_raise(rb_eArgError, "Cannot resolve VL_ARGS entry %d", (int) u[j]);
1126
+ u[j] = (VALUE) relocs->args_adr[u[j]];
1127
+ break;
1128
+ #endif
1129
+ case VL_LIT:
1130
+ if (u[j] >= (unsigned int) relocs->lits_len)
1131
+ rb_raise(rb_eArgError, "Cannot resolve VL_LIT entry %d", (int) u[j]);
1132
+ u[j] = (VALUE) relocs->lits_adr[u[j]];
1133
+ break;
1134
+ default:
1135
+ rb_raise(rb_eArgError, "Unknown RTYPE %d", rtypes[j]);
1136
+ }
1137
+ }
1138
+
1139
+ // Fill classic node structure
1140
+ node = relocs->nodes_adr[i];
1141
+ #ifdef RESET_GC_FLAGS
1142
+ flags = flags & (~0x3); // Ruby 1.9.x -- specific thing
1143
+ #endif
1144
+ node->flags = (flags << 5) | T_NODE;
1145
+ node->nd_reserved = 0;
1146
+ node->u1.value = u[0];
1147
+ node->u2.value = u[1];
1148
+ node->u3.value = u[2];
1149
+ }
1150
+ }
1151
+
1152
+ /*
1153
+ * Returns the value of string hash field using symbolic key
1154
+ */
1155
+ static VALUE get_hash_strfield(VALUE hash, const char *idtxt)
1156
+ {
1157
+ VALUE str = rb_hash_aref(hash, ID2SYM(rb_intern(idtxt)));
1158
+ if (TYPE(str) != T_STRING)
1159
+ {
1160
+ rb_raise(rb_eArgError, "Hash field %s is not a string", idtxt);
1161
+ return Qnil;
1162
+ }
1163
+ else
1164
+ {
1165
+ return str;
1166
+ }
1167
+ }
1168
+
1169
+ /*
1170
+ * Check validity of node hash representation signatures ("magic" values)
1171
+ */
1172
+ static VALUE check_hash_magic(VALUE data)
1173
+ {
1174
+ VALUE val, refval;
1175
+ // MAGIC signature must be valid
1176
+ val = get_hash_strfield(data, "MAGIC");
1177
+ if (strcmp("NODEMARSHAL10", RSTRING_PTR(val)))
1178
+ rb_raise(rb_eArgError, "Bad value of MAGIC signature");
1179
+ // RUBY_PLATFORM signature must match the current platform
1180
+ val = get_hash_strfield(data, "RUBY_PLATFORM");
1181
+ refval = rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM"));
1182
+ if (strcmp(RSTRING_PTR(refval), RSTRING_PTR(val)))
1183
+ rb_raise(rb_eArgError, "Incompatible RUBY_PLATFORM value %s", RSTRING_PTR(val));
1184
+ // RUBY_VERSION signature must match the used Ruby interpreter
1185
+ val = get_hash_strfield(data, "RUBY_VERSION");
1186
+ refval = rb_const_get(rb_cObject, rb_intern("RUBY_VERSION"));
1187
+ if (strcmp(RSTRING_PTR(refval), RSTRING_PTR(val)))
1188
+ rb_raise(rb_eArgError, "Incompatible RUBY_VERSION value %s", RSTRING_PTR(val));
1189
+ return Qtrue;
1190
+ }
1191
+
1192
+ /*
1193
+ * Part 5. C-to-Ruby interface
1194
+ *
1195
+ */
1196
+
1197
+ /*
1198
+ * Restore Ruby node from the binary blob (dump)
1199
+ */
1200
+ static VALUE m_nodedump_from_memory(VALUE self, VALUE dump)
1201
+ {
1202
+ VALUE cMarshal, data, val, val_relocs;
1203
+ int num_of_nodes;
1204
+ NODEObjAddresses *relocs;
1205
+ /* DISABLE GARBAGE COLLECTOR (required for stable loading
1206
+ of large node trees */
1207
+ rb_gc_disable();
1208
+ /* Wrap struct for relocations */
1209
+ val_relocs = Data_Make_Struct(cNodeObjAddresses, NODEObjAddresses,
1210
+ NULL, NODEObjAddresses_free, relocs); // This data envelope cannot exist without NODE
1211
+ /* Load and unpack our dump */
1212
+ cMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
1213
+ data = rb_funcall(cMarshal, rb_intern("load"), 1, dump);
1214
+ if (TYPE(data) != T_HASH)
1215
+ {
1216
+ rb_raise(rb_eArgError, "Input dump is corrupted");
1217
+ }
1218
+ val = rb_hash_aref(data, ID2SYM(rb_intern("num_of_nodes")));
1219
+ if (val == Qnil)
1220
+ {
1221
+ rb_raise(rb_eArgError, "num_of_nodes not found");
1222
+ }
1223
+ else
1224
+ {
1225
+ num_of_nodes = FIX2INT(val);
1226
+ }
1227
+ /* Check "magic" signature and platform identifiers */
1228
+ check_hash_magic(data);
1229
+ /* Get the information about the source file that was compiled to the node */
1230
+ // a) node name
1231
+ val = rb_hash_aref(data, ID2SYM(rb_intern("nodename")));
1232
+ if (val == Qnil || TYPE(val) == T_STRING)
1233
+ rb_iv_set(self, "@nodename", val);
1234
+ else
1235
+ rb_raise(rb_eArgError, "nodename value is corrupted");
1236
+ // b) file name
1237
+ val = rb_hash_aref(data, ID2SYM(rb_intern("filename")));
1238
+ if (val == Qnil || TYPE(val) == T_STRING)
1239
+ rb_iv_set(self, "@filename", val);
1240
+ else
1241
+ rb_raise(rb_eArgError, "filename value is corrupted");
1242
+ // c) file path
1243
+ val = rb_hash_aref(data, ID2SYM(rb_intern("filepath")));
1244
+ if (val == Qnil || TYPE(val) == T_STRING)
1245
+ rb_iv_set(self, "@filepath", val);
1246
+ else
1247
+ rb_raise(rb_eArgError, "filepath value is corrupted");
1248
+ /* Load all required data */
1249
+ resolve_syms_ords(data, relocs); // Symbols
1250
+ resolve_lits_ords(data, relocs); // Literals
1251
+ resolve_gvars_ords(data, relocs); // Global entries (with symbol ID resolving)
1252
+ resolve_idtbls_ords(data, relocs); // Identifiers tables (with symbol ID resolving)
1253
+ resolve_nodes_ords(data, num_of_nodes, relocs); // Allocate memory for all nodes
1254
+ #ifdef USE_RB_ARGS_INFO
1255
+ resolve_args_ords(data, relocs); // Load args entries with symbols ID and nodes resolving
1256
+ #endif
1257
+ load_nodes_from_str(data, relocs);
1258
+ /* Save the loaded node tree and collect garbage */
1259
+ rb_iv_set(self, "@node", (VALUE) relocs->nodes_adr[0]);
1260
+ rb_iv_set(self, "@num_of_nodes", INT2FIX(num_of_nodes));
1261
+ rb_iv_set(self, "@obj_addresses", val_relocs);
1262
+ rb_gc_enable();
1263
+ rb_gc_start();
1264
+ return self;
1265
+ }
1266
+
1267
+
1268
+ /*
1269
+ * Return array with the list of symbols
1270
+ */
1271
+ static VALUE m_nodedump_symbols(VALUE self)
1272
+ {
1273
+ int i;
1274
+ VALUE val_relocs, val_nodeinfo, syms;
1275
+ // Variant 1: node loaded from file
1276
+ val_relocs = rb_iv_get(self, "@obj_addresses");
1277
+ if (val_relocs != Qnil)
1278
+ {
1279
+ NODEObjAddresses *relocs;
1280
+ Data_Get_Struct(val_relocs, NODEObjAddresses, relocs);
1281
+ syms = rb_ary_new();
1282
+ for (i = 0; i < relocs->syms_len; i++)
1283
+ rb_ary_push(syms, ID2SYM(relocs->syms_adr[i]));
1284
+ return syms;
1285
+ }
1286
+ // Variant 2: node saved to file (parsed from memory)
1287
+ val_nodeinfo = rb_iv_get(self, "@nodeinfo");
1288
+ if (val_nodeinfo != Qnil)
1289
+ {
1290
+ NODEInfo *ninfo;
1291
+ VALUE *ary;
1292
+ Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
1293
+ syms = rb_funcall(ninfo->syms.vals, rb_intern("values"), 0);
1294
+ ary = RARRAY_PTR(syms);
1295
+ for (i = 0; i < RARRAY_LEN(syms); i++)
1296
+ {
1297
+ ary[i] = rb_funcall(ary[i], rb_intern("to_sym"), 0);
1298
+ }
1299
+ return syms;
1300
+ }
1301
+ rb_raise(rb_eArgError, "Symbol information not initialized. Run to_hash before reading.");
1302
+ }
1303
+
1304
+ /*
1305
+ * Return array with the list of literals
1306
+ */
1307
+ static VALUE m_nodedump_literals(VALUE self)
1308
+ {
1309
+ int i;
1310
+ VALUE val_relocs, val_nodeinfo, lits;
1311
+ // Variant 1: node loaded from file
1312
+ val_relocs = rb_iv_get(self, "@obj_addresses");
1313
+ if (val_relocs != Qnil)
1314
+ {
1315
+ NODEObjAddresses *relocs;
1316
+
1317
+ Data_Get_Struct(val_relocs, NODEObjAddresses, relocs);
1318
+ lits = rb_ary_new();
1319
+ for (i = 0; i < relocs->lits_len; i++)
1320
+ rb_ary_push(lits, rb_funcall(relocs->lits_adr[i], rb_intern("dup"), 0));
1321
+ return lits;
1322
+ }
1323
+ // Variant 2: node saved to file (parsed from memory)
1324
+ val_nodeinfo = rb_iv_get(self, "@nodeinfo");
1325
+ if (val_nodeinfo != Qnil)
1326
+ {
1327
+ NODEInfo *ninfo;
1328
+ VALUE *ary;
1329
+ Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
1330
+ lits = rb_funcall(ninfo->lits.vals, rb_intern("values"), 0);
1331
+ ary = RARRAY_PTR(lits);
1332
+ for (i = 0; i < RARRAY_LEN(lits); i++)
1333
+ {
1334
+ int t = TYPE(ary[i]);
1335
+ if (t != T_SYMBOL && t != T_FLOAT && t != T_FIXNUM)
1336
+ ary[i] = rb_funcall(ary[i], rb_intern("dup"), 0);
1337
+ }
1338
+ return lits;
1339
+ }
1340
+ rb_raise(rb_eArgError, "Literals information not initialized. Run to_hash before reading.");
1341
+ }
1342
+
1343
+
1344
+
1345
+ /*
1346
+ * call-seq:
1347
+ * obj.compile
1348
+ *
1349
+ * Creates the RubyVM::InstructionSequence object from the node
1350
+ */
1351
+ static VALUE m_nodedump_compile(VALUE self)
1352
+ {
1353
+ NODE *node = RNODE(rb_iv_get(self, "@node"));
1354
+ VALUE nodename = rb_iv_get(self, "@nodename");
1355
+ VALUE filename = rb_iv_get(self, "@filename");
1356
+ VALUE filepath = rb_iv_get(self, "@filepath");
1357
+ return rb_iseq_new_top(node, nodename, filename, filepath, Qfalse);
1358
+ }
1359
+
1360
+ /*
1361
+ * Parses Ruby file with the source code and saves the node
1362
+ */
1363
+ static VALUE m_nodedump_from_source(VALUE self, VALUE file)
1364
+ {
1365
+ VALUE line = INT2FIX(1), f, node, filepath;
1366
+ const char *fname;
1367
+
1368
+ rb_secure(1);
1369
+ FilePathValue(file);
1370
+ fname = StringValueCStr(file);
1371
+ /* Remember information about the file */
1372
+ rb_iv_set(self, "@nodename", rb_str_new2("<main>"));
1373
+ rb_iv_set(self, "@filename", file);
1374
+ filepath = rb_funcall(rb_cFile, rb_intern("realpath"), 1, file); // Envelope for rb_realpath_internal
1375
+ rb_iv_set(self, "@filepath", filepath);
1376
+ /* Create node from the source */
1377
+ f = rb_file_open_str(file, "r");
1378
+ node = (VALUE) rb_compile_file(fname, f, NUM2INT(line));
1379
+ rb_iv_set(self, "@node", node);
1380
+ return self;
1381
+ }
1382
+
1383
+ /*
1384
+ * Parses Ruby string with the source code and saves the node
1385
+ */
1386
+ static VALUE m_nodedump_from_string(VALUE self, VALUE str)
1387
+ {
1388
+ VALUE line = INT2FIX(1), node;
1389
+ const char *fname = "STRING";
1390
+ Check_Type(str, T_STRING);
1391
+ rb_secure(1);
1392
+ /* Create empty information about the file */
1393
+ rb_iv_set(self, "@nodename", rb_str_new2("<main>"));
1394
+ if (RUBY_API_VERSION_MAJOR == 1)
1395
+ { /* For Ruby 1.9.x */
1396
+ rb_iv_set(self, "@filename", Qnil);
1397
+ rb_iv_set(self, "@filepath", Qnil);
1398
+ }
1399
+ else
1400
+ { /* For Ruby 2.x */
1401
+ rb_iv_set(self, "@filename", rb_str_new2("<compiled>"));
1402
+ rb_iv_set(self, "@filepath", rb_str_new2("<compiled>"));
1403
+ }
1404
+ /* Create node from the string */
1405
+ rb_gc_disable();
1406
+ node = (VALUE) rb_compile_string(fname, str, NUM2INT(line));
1407
+ rb_iv_set(self, "@node", node);
1408
+ rb_gc_enable();
1409
+ rb_gc_start();
1410
+ return self;
1411
+ }
1412
+
1413
+ /*
1414
+ * call-seq:
1415
+ * obj.new(:srcfile, filename) # Will load source file from the disk
1416
+ * obj.new(:binfile, filename) # Will load file with node binary dump from the disk
1417
+ * obj.new(:srcmemory, srcstr) # Will load source code from the string
1418
+ * obj.new(:binmemory, binstr) # Will load node binary dump from the string
1419
+ *
1420
+ * Creates NodeMarshal class example from the source code or dumped
1421
+ * syntax tree (NODEs), i.e. preparsed and packed source code. Created
1422
+ * object can be used either for code execution or for saving it
1423
+ * in the preparsed form (useful for code obfuscation/protection)
1424
+ */
1425
+ static VALUE m_nodedump_init(VALUE self, VALUE source, VALUE info)
1426
+ {
1427
+ ID id_usr;
1428
+ Check_Type(source, T_SYMBOL);
1429
+ id_usr = SYM2ID(source);
1430
+ if (id_usr == rb_intern("srcfile"))
1431
+ {
1432
+ return m_nodedump_from_source(self, info);
1433
+ }
1434
+ else if (id_usr == rb_intern("srcmemory"))
1435
+ {
1436
+ return m_nodedump_from_string(self, info);
1437
+ }
1438
+ else if (id_usr == rb_intern("binmemory"))
1439
+ {
1440
+ return m_nodedump_from_memory(self, info);
1441
+ }
1442
+ else if (id_usr == rb_intern("binfile"))
1443
+ {
1444
+ VALUE cFile = rb_const_get(rb_cObject, rb_intern("File"));
1445
+ VALUE bin = rb_funcall(cFile, rb_intern("binread"), 1, info);
1446
+ return m_nodedump_from_memory(self, bin);
1447
+ }
1448
+ else
1449
+ {
1450
+ rb_raise(rb_eArgError, "Invalid source type (it must be :srcfile, :srcmemory, :binmemory of :binfile)");
1451
+ }
1452
+ return Qnil;
1453
+ }
1454
+
1455
+ /*
1456
+ * call-seq:
1457
+ * obj.dump_tree
1458
+ *
1459
+ * Transforms Ruby syntax tree (NODE) to the text string using
1460
+ * rb_parser_dump_tree function from node.c (see Ruby source code).
1461
+ */
1462
+ static VALUE m_nodedump_parser_dump_tree(VALUE self)
1463
+ {
1464
+ NODE *node = RNODE(rb_iv_get(self, "@node"));
1465
+ return rb_parser_dump_tree(node, 0);
1466
+ }
1467
+
1468
+ /*
1469
+ * Prints the node tree in the short variant
1470
+ */
1471
+ static VALUE m_nodedump_dump_tree_short(VALUE self)
1472
+ {
1473
+ VALUE str = rb_str_new2(""); // Output string
1474
+ NODE *node = RNODE(rb_iv_get(self, "@node"));
1475
+ print_node(str, node, 0);
1476
+ return str;
1477
+ }
1478
+
1479
+ /*
1480
+ * call-seq:
1481
+ * obj.to_hash
1482
+ *
1483
+ * Converts NodeMarshal class example to the hash that contains full
1484
+ * and independent from data structures memory addresses information.
1485
+ * Format of the obtained hash depends on used platform (especially
1486
+ * size of the pointer) and Ruby version.
1487
+ *
1488
+ * <b>Format of the hash</b>
1489
+ *
1490
+ * <i>Part 1: Signatures</i>
1491
+ *
1492
+ * - <tt>MAGIC</tt> -- NODEMARSHAL10
1493
+ * - <tt>RUBY_PLATFORM</tt> -- saved <tt>RUBY_PLATFORM</tt> constant value
1494
+ * - <tt>RUBY_VERSION</tt> -- saved <tt>RUBY_VERSION</tt> constant value
1495
+ *
1496
+ * <i>Part 2: Program loadable elements.</i>
1497
+ *
1498
+ * All loadable elements are arrays. Index of the array element means
1499
+ * its identifier that is used in the node tree.
1500
+ *
1501
+ * - <tt>literals</tt> -- program literals (strings, ranges etc.)
1502
+ * - <tt>symbols</tt> -- program symbols
1503
+ * - <tt>global_entries</tt> -- global variables information
1504
+ * - <tt>id_tables</tt> -- array of arrays. Each array contains symbols IDs
1505
+ * - <tt>args</tt> -- information about code block argument(s)
1506
+ *
1507
+ * <i>Part 3: Nodes information</i>
1508
+ * - <tt>nodes</tt> -- string that contains binary encoded information
1509
+ * about the nodes
1510
+ */
1511
+ static VALUE m_nodedump_to_hash(VALUE self)
1512
+ {
1513
+ NODE *node = RNODE(rb_iv_get(self, "@node"));
1514
+ NODEInfo *info;
1515
+ VALUE ans, num, val_info;
1516
+ // DISABLE GARBAGE COLLECTOR (important for dumping)
1517
+ rb_gc_disable();
1518
+ // Allocate memory for the information about node
1519
+ val_info = Data_Make_Struct(cNodeInfo, NODEInfo,
1520
+ NODEInfo_mark, NODEInfo_free, info); // This data envelope cannot exist without NODE
1521
+ NODEInfo_init(info);
1522
+ rb_iv_set(self, "@nodeinfo", val_info);
1523
+ // Convert node to NODEInfo structure
1524
+ num = INT2FIX(count_num_of_nodes(node, node, info));
1525
+ ans = NODEInfo_toHash(info);
1526
+ rb_hash_aset(ans, ID2SYM(rb_intern("num_of_nodes")), num);
1527
+ rb_hash_aset(ans, ID2SYM(rb_intern("nodename")), rb_iv_get(self, "@nodename"));
1528
+ rb_hash_aset(ans, ID2SYM(rb_intern("filename")), rb_iv_get(self, "@filename"));
1529
+ rb_hash_aset(ans, ID2SYM(rb_intern("filepath")), rb_iv_get(self, "@filepath"));
1530
+ // ENABLE GARBAGE COLLECTOR (important for dumping)
1531
+ rb_gc_enable();
1532
+ return ans;
1533
+ }
1534
+
1535
+ /*
1536
+ * call-seq:
1537
+ * obj.to_bin
1538
+ *
1539
+ * Converts NodeMarshal class example to the binary string that
1540
+ * can be saved to the file and used for loading the node from the file.
1541
+ * Format of the obtained binary dump depends on used platform (especially
1542
+ * size of the pointer) and Ruby version.
1543
+ */
1544
+ static VALUE m_nodedump_to_bin(VALUE self)
1545
+ {
1546
+ VALUE hash = m_nodedump_to_hash(self);
1547
+ VALUE cMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
1548
+ return rb_funcall(cMarshal, rb_intern("dump"), 1, hash);
1549
+ }
1550
+
1551
+ /*
1552
+ * Gives the information about the node
1553
+ */
1554
+ static VALUE m_nodedump_inspect(VALUE self)
1555
+ {
1556
+ static char str[1024], buf[512];
1557
+ VALUE num_of_nodes, nodename, filepath, filename;
1558
+ VALUE val_obj_addresses, val_nodeinfo;
1559
+ // Get generic information about node
1560
+ num_of_nodes = rb_iv_get(self, "@num_of_nodes");
1561
+ nodename = rb_iv_get(self, "@nodename");
1562
+ filepath = rb_iv_get(self, "@filepath");
1563
+ filename = rb_iv_get(self, "@filename");
1564
+ // Generate string with generic information about node
1565
+ sprintf(str,
1566
+ "----- NodeMarshal:0x%"PRIxPTR"\n"
1567
+ " num_of_nodes: %d\n nodename: %s\n filepath: %s\n filename: %s\n",
1568
+ self,
1569
+ (num_of_nodes == Qnil) ? -1 : FIX2INT(num_of_nodes),
1570
+ (nodename == Qnil) ? "nil" : RSTRING_PTR(nodename),
1571
+ (filepath == Qnil) ? "nil" : RSTRING_PTR(filepath),
1572
+ (filename == Qnil) ? "nil" : RSTRING_PTR(filename)
1573
+ );
1574
+ // Check if the information about node struct is available
1575
+ val_nodeinfo = rb_iv_get(self, "@nodeinfo");
1576
+ val_obj_addresses = rb_iv_get(self, "@obj_addresses");
1577
+ if (val_nodeinfo == Qnil && val_obj_addresses == Qnil)
1578
+ {
1579
+ m_nodedump_to_hash(self);
1580
+ val_nodeinfo = rb_iv_get(self, "@nodeinfo");
1581
+ }
1582
+ // Information about preparsed node
1583
+ // a) NODEInfo struct
1584
+ if (val_nodeinfo == Qnil)
1585
+ {
1586
+ sprintf(buf, " NODEInfo struct is empty\n");
1587
+ }
1588
+ else
1589
+ {
1590
+ NODEInfo *ninfo;
1591
+ Data_Get_Struct(val_nodeinfo, NODEInfo, ninfo);
1592
+ sprintf(buf,
1593
+ " NODEInfo struct:\n"
1594
+ " syms hash len (Symbols): %d\n"
1595
+ " lits hash len (Literals): %d\n"
1596
+ " idtabs hash len (ID tables): %d\n"
1597
+ " gentries hash len (Global vars): %d\n"
1598
+ " nodes hash len (Nodes): %d\n"
1599
+ #ifdef USE_RB_ARGS_INFO
1600
+ " args hash len (args info): %d\n"
1601
+ #endif
1602
+ ,
1603
+ FIX2INT(rb_funcall(ninfo->syms.vals, rb_intern("length"), 0)),
1604
+ FIX2INT(rb_funcall(ninfo->lits.vals, rb_intern("length"), 0)),
1605
+ FIX2INT(rb_funcall(ninfo->idtabs.vals, rb_intern("length"), 0)),
1606
+ FIX2INT(rb_funcall(ninfo->gentries.vals, rb_intern("length"), 0)),
1607
+ FIX2INT(rb_funcall(ninfo->nodes.vals, rb_intern("length"), 0))
1608
+ #ifdef USE_RB_ARGS_INFO
1609
+ ,
1610
+ FIX2INT(rb_funcall(ninfo->args.vals, rb_intern("length"), 0))
1611
+ #endif
1612
+ );
1613
+ }
1614
+ strcat(str, buf);
1615
+ // b) NODEObjAddresses struct
1616
+ if (val_obj_addresses == Qnil)
1617
+ {
1618
+ sprintf(buf, " NODEObjAddresses struct is empty\n");
1619
+ }
1620
+ else
1621
+ {
1622
+ NODEObjAddresses *objadr;
1623
+ Data_Get_Struct(val_obj_addresses, NODEObjAddresses, objadr);
1624
+ sprintf(buf,
1625
+ " NODEObjAddresses struct:\n"
1626
+ " syms_len (Num of symbols): %d\n"
1627
+ " lits_len (Num of literals): %d\n"
1628
+ " idtbls_len (Num of ID tables): %d\n"
1629
+ " gvars_len (Num of global vars): %d\n"
1630
+ " nodes_len (Num of nodes): %d\n"
1631
+ #ifdef USE_RB_ARGS_INFO
1632
+ " args_len: (Num of args info): %d\n"
1633
+ #endif
1634
+ , objadr->syms_len, objadr->lits_len,
1635
+ objadr->idtbls_len, objadr->gvars_len,
1636
+ objadr->nodes_len
1637
+ #ifdef USE_RB_ARGS_INFO
1638
+ , objadr->args_len
1639
+ #endif
1640
+ );
1641
+ }
1642
+ strcat(str, buf);
1643
+ strcat(str, "------------------\n");
1644
+ // Generate output string
1645
+ return rb_str_new2(str);
1646
+ }
1647
+
1648
+ /*
1649
+ * Returns node name (usually <main>)
1650
+ */
1651
+ static VALUE m_nodedump_nodename(VALUE self)
1652
+ {
1653
+ return rb_funcall(rb_iv_get(self, "@nodename"), rb_intern("dup"), 0);
1654
+ }
1655
+
1656
+ /*
1657
+ * Returns name of file that was used for node generation and will be used
1658
+ * by YARV (or nil/<compiled> if a string of code was used)
1659
+ */
1660
+ static VALUE m_nodedump_filename(VALUE self)
1661
+ {
1662
+ return rb_funcall(rb_iv_get(self, "@filename"), rb_intern("dup"), 0);
1663
+ }
1664
+
1665
+ /*
1666
+ * Sets name of file that was used for node generation and will be used
1667
+ * by YARV (or nil/<compiled> if a string of code was used)
1668
+ */
1669
+ static VALUE m_nodedump_set_filename(VALUE self, VALUE val)
1670
+ {
1671
+ if (val != Qnil)
1672
+ {
1673
+ Check_Type(val, T_STRING);
1674
+ rb_iv_set(self, "@filename", rb_funcall(val, rb_intern("dup"), 0));
1675
+ }
1676
+ else
1677
+ {
1678
+ rb_iv_set(self, "@filename", Qnil);
1679
+ }
1680
+ return self;
1681
+ }
1682
+
1683
+ /*
1684
+ * Returns path of file that was used for node generation and will be used
1685
+ * by YARV (or nil/<compiled> if a string of code was used)
1686
+ */
1687
+ static VALUE m_nodedump_filepath(VALUE self)
1688
+ {
1689
+ return rb_funcall(rb_iv_get(self, "@filepath"), rb_intern("dup"), 0);
1690
+ }
1691
+
1692
+ /*
1693
+ * call-seq:
1694
+ * obj.filepath=value
1695
+ *
1696
+ * Sets the path of file that was used for node generation and will
1697
+ * be used by YARV (or nil/<compiled> if a string of code was used)
1698
+ */
1699
+ static VALUE m_nodedump_set_filepath(VALUE self, VALUE val)
1700
+ {
1701
+ if (val != Qnil)
1702
+ {
1703
+ Check_Type(val, T_STRING);
1704
+ rb_iv_set(self, "@filepath", rb_funcall(val, rb_intern("dup"), 0));
1705
+ }
1706
+ else
1707
+ {
1708
+ rb_iv_set(self, "@filepath", Qnil);
1709
+ }
1710
+ return self;
1711
+ }
1712
+
1713
+ /*
1714
+ * call-seq:
1715
+ * NodeMarshal.base85r_encode(input) -> output
1716
+ *
1717
+ * Encode arbitrary binary string to the ASCII string
1718
+ * using modified version of BASE85 (useful for obfuscation
1719
+ * of .rb source files)
1720
+ */
1721
+ static VALUE m_base85r_encode(VALUE obj, VALUE input)
1722
+ {
1723
+ return base85r_encode(input);
1724
+ }
1725
+
1726
+ /*
1727
+ * call-seq:
1728
+ * NodeMarshal.base85r_decode(input) -> output
1729
+ *
1730
+ * Decode ASCII string in the modified BASE85 format
1731
+ * to the binary string (useful for obfuscation of .rb
1732
+ * source files)
1733
+ */
1734
+ static VALUE m_base85r_decode(VALUE obj, VALUE input)
1735
+ {
1736
+ return base85r_decode(input);
1737
+ }
1738
+
1739
+ /* call-seq:
1740
+ * obj.to_bin
1741
+ *
1742
+ * Converts NodeMarshal class example to the text string (modified Base85 encoding) that
1743
+ * can be saved to the file and used for loading the node from the file.
1744
+ * Format of the obtained binary dump depends on used platform (especially
1745
+ * size of the pointer) and Ruby version.
1746
+ */
1747
+ static VALUE m_nodedump_to_text(VALUE self)
1748
+ {
1749
+ VALUE bin = m_nodedump_to_bin(self);
1750
+ return base85r_encode(bin);
1751
+ }
1752
+
1753
+ /*
1754
+ * Returns node object
1755
+ */
1756
+ static VALUE m_nodedump_node(VALUE self)
1757
+ {
1758
+ return rb_iv_get(self, "@node");
1759
+ }
1760
+
1761
+
1762
+ /*
1763
+ * This class can load and save Ruby code in the form of the
1764
+ * platform-dependent syntax tree (made of NODEs). Such function
1765
+ * allows to hide the source code from users. Main features:
1766
+ *
1767
+ * - Irreversible transformation of Ruby source code to the syntax tree
1768
+ * - Representation of syntax tree in binary form dependent from the platform and Ruby version
1769
+ * - Simple options for node inspection
1770
+ * - Ruby 1.9.3 and 2.2.1 support
1771
+ */
1772
+ void Init_nodemarshal()
1773
+ {
1774
+ static VALUE cNodeMarshal;
1775
+ init_nodes_table(nodes_ctbl, NODES_CTBL_SIZE);
1776
+ base85r_init_tables();
1777
+
1778
+ cNodeMarshal = rb_define_class("NodeMarshal", rb_cObject);
1779
+ rb_define_singleton_method(cNodeMarshal, "base85r_encode", RUBY_METHOD_FUNC(m_base85r_encode), 1);
1780
+ rb_define_singleton_method(cNodeMarshal, "base85r_decode", RUBY_METHOD_FUNC(m_base85r_decode), 1);
1781
+
1782
+ rb_define_method(cNodeMarshal, "initialize", RUBY_METHOD_FUNC(m_nodedump_init), 2);
1783
+ rb_define_method(cNodeMarshal, "to_hash", RUBY_METHOD_FUNC(m_nodedump_to_hash), 0);
1784
+ rb_define_method(cNodeMarshal, "to_bin", RUBY_METHOD_FUNC(m_nodedump_to_bin), 0);
1785
+ rb_define_method(cNodeMarshal, "to_text", RUBY_METHOD_FUNC(m_nodedump_to_text), 0);
1786
+ rb_define_method(cNodeMarshal, "dump_tree", RUBY_METHOD_FUNC(m_nodedump_parser_dump_tree), 0);
1787
+ rb_define_method(cNodeMarshal, "dump_tree_short", RUBY_METHOD_FUNC(m_nodedump_dump_tree_short), 0);
1788
+ rb_define_method(cNodeMarshal, "compile", RUBY_METHOD_FUNC(m_nodedump_compile), 0);
1789
+ // Methods for working with the information about the node
1790
+ // a) literals, symbols, generic information
1791
+ rb_define_method(cNodeMarshal, "symbols", RUBY_METHOD_FUNC(m_nodedump_symbols), 0);
1792
+ rb_define_method(cNodeMarshal, "literals", RUBY_METHOD_FUNC(m_nodedump_literals), 0);
1793
+ rb_define_method(cNodeMarshal, "inspect", RUBY_METHOD_FUNC(m_nodedump_inspect), 0);
1794
+ rb_define_method(cNodeMarshal, "node", RUBY_METHOD_FUNC(m_nodedump_node), 0);
1795
+ // b) node and file names
1796
+ rb_define_method(cNodeMarshal, "nodename", RUBY_METHOD_FUNC(m_nodedump_nodename), 0);
1797
+ rb_define_method(cNodeMarshal, "filename", RUBY_METHOD_FUNC(m_nodedump_filename), 0);
1798
+ rb_define_method(cNodeMarshal, "filename=", RUBY_METHOD_FUNC(m_nodedump_set_filename), 1);
1799
+ rb_define_method(cNodeMarshal, "filepath", RUBY_METHOD_FUNC(m_nodedump_filepath), 0);
1800
+ rb_define_method(cNodeMarshal, "filepath=", RUBY_METHOD_FUNC(m_nodedump_set_filepath), 1);
1801
+ // C structure wrappers
1802
+ cNodeObjAddresses = rb_define_class("NodeObjAddresses", rb_cObject);
1803
+ cNodeInfo = rb_define_class("NodeInfo", rb_cObject);
1804
+ }